I am writing an application that stores various data in a content addressable store. I have several implementations (several storage formats), including one that uses plain files, one that uses an SQLite database, and one that just uses an in-memory hash table.
Currently, I represent these with a trait:
pub trait Source {
fn find(&self, key: &Oid) -> Result<Chunk>;
fn get_writer<'a>(&'a self) -> Result<Box<Sink + 'a>>;
fn add(&self, chunk: &Chunk, writer: &Sink) -> Result<()>;
...
}
pub trait Sink {
fn flush(self: Box<Self>) -> Result<()>;
fn inner(&self) -> &Source;
}
and this is working. However, there are numerous things about it that are awkward:
- The 'Sink' is clumsy, but I need something to hold the
Transaction
from rusqlite. - Nothing takes self as
&mut
. This means every implementation needs inner mutability to actually do anything. - The writer must be boxed.
- I'm trying to keep
Source
object safe, since I'd like to have a function that can look at a disk directory and return the appropriate implementation. - Clients of the
Sink
need to do weird things like:sink.inner().add(&chunk, sink.inner)
to call the add method.
Trying to be object safe restricts me from doing useful things, such as using an associated type within Source to hold the Sink.
Again, I have this working, but I was wondering if anyone had any ideas/suggestions to make this more usable.
I can push the current code up to github if that would be helpful.