BUKUHTU2FHX7K5KJC434LFCR7CCP7DIVNDCWWCXNG5Q2Q2SPS23AC // Copyright © 2023 Kim Altintop <kim@eagain.io>// SPDX-License-Identifier: GPL-2.0-onlyuse automerge::{Automerge,Change,ChangeHash,};pub trait WritableStorage {type Error;fn store(&mut self,doc: &str,changes: impl IntoIterator<Item = Change>,) -> Result<(), Self::Error>;}pub trait ReadableStorage {type Error;type Doc;fn load(&self, doc: &str) -> Result<Option<Automerge>, Self::Error>;fn exists(&self, doc: &str) -> Result<bool, Self::Error>;fn doc(&self, doc: &str) -> Result<Option<Self::Doc>, Self::Error>;}pub trait ChangeStore {type Error;fn write_change(&self, change: Change) -> Result<(), Self::Error>;fn get_change(&self, hash: &ChangeHash) -> Result<Option<Change>, Self::Error>;fn has_change(&self, hash: &ChangeHash) -> Result<bool, Self::Error>;}
#[derive(Debug, Error)]pub enum Error {#[error("missing change: {0}")]MissingChange(ChangeHash),#[error(transparent)]Automerge(#[from] automerge::AutomergeError),#[error(transparent)]Changes(#[from] changes::fs::Error),#[error(transparent)]Index(#[from] index::Error),}pub struct Storage {changes: FileStore,index: index::Sanakirja,}impl Storage {pub fn new(root: impl AsRef<Path>) -> Result<Self, Error> {let changes = FileStore::new(root.as_ref().join("changes"));let index = index::Sanakirja::new(Some(&root.as_ref().join("index")))?;Ok(Self { changes, index })}pub fn readable(&self) -> Result<Readable, Error> {Ok(Readable {changes: self.changes.clone(),tx: self.index.txn_begin()?,})}pub fn writable(&self) -> Result<Writable<()>, Error> {Ok(Writable {changes: self.changes.clone(),tx: self.index.mut_txn_begin()?,})}}pub type Readable = Tx<index::Txn>;pub type Writable<T> = Tx<index::MutTxn<T>>;
mod fs;pub use fs::FileStore;
pub struct Tx<T> {changes: FileStore,tx: T,}impl<T: sanakirja::Commit> Tx<T> {pub fn commit(self) -> Result<(), Error> {Ok(sanakirja::Commit::commit(self.tx).map_err(index::Error::from)?)}}impl<T> ReadableStorage for Tx<index::GenericTxn<T>>whereT: sanakirja::LoadPage<Error = sanakirja::Error>,{type Error = Error;type Doc = index::Doc;fn load(&self, doc: &str) -> Result<Option<Automerge>, Error> {let stored = self.tx.get_doc(doc)?;if let Some(doc) = stored {let mut am = Automerge::new();for change in doc.changes(&self.tx) {let (_, hash) = change?;let change = self.changes.get_change(&hash)?.ok_or_else(|| Error::MissingChange(hash))?;am.apply_changes(Some(change))?;}Ok(Some(am))} else {Ok(None)}}fn exists(&self, doc: &str) -> Result<bool, Self::Error> {Ok(self.tx.has_doc(doc)?)}fn doc(&self, doc: &str) -> Result<Option<Self::Doc>, Self::Error> {Ok(self.tx.get_doc(doc)?)}}impl<T> WritableStorage for Writable<T> {type Error = Error;
pub trait ChangeStore {type Error;
fn store(&mut self,doc: &str,changes: impl IntoIterator<Item = Change>,) -> Result<(), Self::Error> {let mut stored = self.tx.create_doc(doc)?;for change in changes {let hash = change.hash();self.changes.write_change(change)?;stored.put_change(&mut self.tx, hash)?;}
// Copyright © 2023 Kim Altintop <kim@eagain.io>// SPDX-License-Identifier: GPL-2.0-onlypub mod fs;pub use fs::FileStore;