162 lines
4.5 KiB
Rust
162 lines
4.5 KiB
Rust
use cap_std::fs::Dir;
|
|
use std::io::{Error as IoError, ErrorKind as IoEk, Read, Result as IoResult, Write};
|
|
|
|
mod changelist;
|
|
pub use changelist::ChangeList;
|
|
|
|
mod lock;
|
|
use lock::RepoLock;
|
|
|
|
mod patience;
|
|
pub use patience::{patience_diff, DiffBlock};
|
|
|
|
mod thin_snapshot;
|
|
pub use thin_snapshot::ThinSnapshot;
|
|
|
|
mod traits;
|
|
pub use traits::{Event, FlowData};
|
|
|
|
pub struct Repository(pub Dir);
|
|
|
|
impl Repository {
|
|
fn subdir(&self, sd: &str) -> IoResult<Dir> {
|
|
let _ = self.0.create_dir(sd);
|
|
self.0.open_dir(sd)
|
|
}
|
|
pub fn changes(&self) -> IoResult<Changes> {
|
|
self.subdir("changes").map(Changes)
|
|
}
|
|
pub fn snapshots(&self) -> IoResult<Snapshots> {
|
|
self.subdir("snapshots").map(Snapshots)
|
|
}
|
|
}
|
|
|
|
pub type ChangeHash = [u8; 32];
|
|
pub struct Changes(Dir);
|
|
|
|
fn yzb64_file_name(h: &ChangeHash) -> [u8; 43] {
|
|
let mut buf = [0u8; 43];
|
|
assert_eq!(
|
|
yzb64::base64::encode_engine_slice(h, &mut buf, &*yzb64::B64_ENGINE),
|
|
43
|
|
);
|
|
buf
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
pub struct MassChangesGet {
|
|
cd: Dir,
|
|
l: RepoLock,
|
|
}
|
|
|
|
impl Changes {
|
|
pub fn insert(&self, h: &ChangeHash, contents: &[u8]) -> IoResult<bool> {
|
|
let buf = yzb64_file_name(h);
|
|
let buf = std::str::from_utf8(&buf).unwrap();
|
|
let _l = RepoLock::lock_write(&self.0, ".lock");
|
|
if self.0.exists(buf) {
|
|
return Ok(false);
|
|
}
|
|
let mut ecd = zstd::stream::write::Encoder::new(self.0.create(buf)?, 5)?;
|
|
ecd.write_all(contents)?;
|
|
ecd.finish()?;
|
|
Ok(true)
|
|
}
|
|
|
|
pub fn remove(&self, h: &ChangeHash) -> IoResult<()> {
|
|
let buf = yzb64_file_name(h);
|
|
let buf = std::str::from_utf8(&buf).unwrap();
|
|
let _l = RepoLock::lock_write(&self.0, ".lock");
|
|
self.0.remove_file(buf)
|
|
}
|
|
|
|
pub fn get(&self, h: &ChangeHash) -> IoResult<Vec<u8>> {
|
|
let buf = yzb64_file_name(h);
|
|
let buf = std::str::from_utf8(&buf).unwrap();
|
|
let _l = RepoLock::lock_read(&self.0, ".lock");
|
|
let mut dcd = zstd::stream::read::Decoder::new(self.0.open(buf)?)?;
|
|
let mut buf2 = Vec::new();
|
|
dcd.read_to_end(&mut buf2)?;
|
|
Ok(buf2)
|
|
}
|
|
|
|
pub fn massget(&self) -> IoResult<MassChangesGet> {
|
|
let l = RepoLock::lock_read(&self.0, ".lock")?;
|
|
Ok(MassChangesGet {
|
|
cd: self.0.try_clone()?,
|
|
l,
|
|
})
|
|
}
|
|
}
|
|
|
|
impl MassChangesGet {
|
|
pub fn get(&self, h: &ChangeHash) -> IoResult<Vec<u8>> {
|
|
let buf = yzb64_file_name(h);
|
|
let buf = std::str::from_utf8(&buf).unwrap();
|
|
let mut dcd = zstd::stream::read::Decoder::new(self.cd.open(buf)?)?;
|
|
let mut buf2 = Vec::new();
|
|
dcd.read_to_end(&mut buf2)?;
|
|
Ok(buf2)
|
|
}
|
|
pub fn try_clone(&self) -> IoResult<Self> {
|
|
let l = RepoLock::lock_read(&self.cd, ".lock")?;
|
|
Ok(MassChangesGet {
|
|
cd: self.cd.try_clone()?,
|
|
l,
|
|
})
|
|
}
|
|
}
|
|
|
|
pub struct Snapshots(pub Dir);
|
|
|
|
impl Snapshots {
|
|
pub fn insert_thin(&self, name: &str, contents: &ThinSnapshot) -> IoResult<bool> {
|
|
let _l = RepoLock::lock_write(&self.0, ".lock");
|
|
if self.0.exists(name) {
|
|
return Ok(false);
|
|
}
|
|
let f = self.0.create(name)?;
|
|
contents.write_to_stream(f)?;
|
|
Ok(true)
|
|
}
|
|
|
|
pub fn update_thin<R>(
|
|
&self,
|
|
name: &str,
|
|
mut tf: impl FnMut(&mut ThinSnapshot) -> IoResult<R>,
|
|
) -> IoResult<R> {
|
|
let _l = RepoLock::lock_write(&self.0, ".lock");
|
|
let mut snap = ThinSnapshot::read_from_stream(self.0.open(name)?)?;
|
|
let res = tf(&mut snap)?;
|
|
let mut tmpf = cap_tempfile::TempFile::new(&self.0)?;
|
|
snap.write_to_stream(&mut tmpf)?;
|
|
tmpf.replace(name)?;
|
|
Ok(res)
|
|
}
|
|
|
|
pub fn remove(&self, name: &str) -> IoResult<()> {
|
|
let _l = RepoLock::lock_write(&self.0, ".lock");
|
|
self.0.remove_file(name)
|
|
}
|
|
|
|
pub fn get_thin(&self, name: &str) -> IoResult<ThinSnapshot> {
|
|
let _l = RepoLock::lock_read(&self.0, ".lock");
|
|
let f = self.0.open(name)?;
|
|
ThinSnapshot::read_from_stream(f)
|
|
}
|
|
|
|
pub fn fork(&self, old: &str, new: &str) -> IoResult<()> {
|
|
let _l = RepoLock::lock_write(&self.0, ".lock");
|
|
if self.0.exists(new) {
|
|
return Err(IoError::new(
|
|
IoEk::AlreadyExists,
|
|
"fork target already exists",
|
|
));
|
|
}
|
|
let mut src = self.0.open(old)?.into_std();
|
|
let mut trg = self.0.create(new)?.into_std();
|
|
std::io::copy(&mut src, &mut trg)?;
|
|
Ok(())
|
|
}
|
|
}
|