#[cfg(test)]
mod tests;
use log::debug;
use nuts_backend::Backend;
use std::cmp;
use crate::entry::mode::Mode;
use crate::entry::{populate_mode_api, populate_tstamp_api, Inner};
use crate::error::ArchiveResult;
use crate::flush_header;
use crate::header::Header;
use crate::id::Id;
use crate::pager::Pager;
use crate::tree::Tree;
macro_rules! impl_new {
($type:ident, $mode:ident) => {
pub(crate) fn new(
pager: &'a mut Pager<B>,
header_id: &'a Id<B>,
header: &'a mut Header,
tree: &'a mut Tree<B>,
name: String,
) -> $type<'a, B> {
$type(InnerBuilder::new(
pager,
header_id,
header,
tree,
name,
Mode::$mode(),
))
}
};
}
pub struct FileBuilder<'a, B: Backend>(InnerBuilder<'a, B>);
impl<'a, B: Backend> FileBuilder<'a, B> {
impl_new!(FileBuilder, file);
populate_mode_api!(mut);
populate_tstamp_api!(mut);
pub fn build(self) -> ArchiveResult<EntryMut<'a, B>, B> {
self.0.build()
}
fn inner(&self) -> &Inner {
&self.0.entry
}
fn inner_mut(&mut self) -> &mut Inner {
&mut self.0.entry
}
}
pub struct DirectoryBuilder<'a, B: Backend>(InnerBuilder<'a, B>);
impl<'a, B: Backend> DirectoryBuilder<'a, B> {
impl_new!(DirectoryBuilder, directory);
populate_mode_api!(mut);
populate_tstamp_api!(mut);
pub fn build(self) -> ArchiveResult<(), B> {
self.0.build().map(|_| ())
}
fn inner(&self) -> &Inner {
&self.0.entry
}
fn inner_mut(&mut self) -> &mut Inner {
&mut self.0.entry
}
}
pub struct SymlinkBuilder<'a, B: Backend> {
builder: InnerBuilder<'a, B>,
target: String,
}
impl<'a, B: Backend> SymlinkBuilder<'a, B> {
pub(crate) fn new(
pager: &'a mut Pager<B>,
header_id: &'a Id<B>,
header: &'a mut Header,
tree: &'a mut Tree<B>,
name: String,
target: String,
) -> SymlinkBuilder<'a, B> {
let builder = InnerBuilder::new(pager, header_id, header, tree, name, Mode::symlink());
SymlinkBuilder { builder, target }
}
populate_mode_api!(mut);
populate_tstamp_api!(mut);
pub fn build(self) -> ArchiveResult<(), B> {
let mut entry = self.builder.build()?;
entry.write_all(self.target.as_bytes())?;
Ok(())
}
fn inner(&self) -> &Inner {
&self.builder.entry
}
fn inner_mut(&mut self) -> &mut Inner {
&mut self.builder.entry
}
}
struct InnerBuilder<'a, B: Backend> {
pager: &'a mut Pager<B>,
header_id: &'a Id<B>,
header: &'a mut Header,
tree: &'a mut Tree<B>,
entry: Inner,
}
impl<'a, B: Backend> InnerBuilder<'a, B> {
fn new(
pager: &'a mut Pager<B>,
header_id: &'a Id<B>,
header: &'a mut Header,
tree: &'a mut Tree<B>,
name: String,
mode: Mode,
) -> InnerBuilder<'a, B> {
InnerBuilder {
pager,
header_id,
header,
tree,
entry: Inner::new(name, mode),
}
}
fn build(self) -> ArchiveResult<EntryMut<'a, B>, B> {
let id = self.tree.aquire(self.pager)?.clone();
self.entry.flush(self.pager, &id)?;
self.header.inc_files();
flush_header(self.pager, self.header_id, self.header, self.tree)?;
Ok(EntryMut::new(
self.pager,
self.header_id,
self.header,
self.tree,
self.entry,
id,
))
}
}
pub struct EntryMut<'a, B: Backend> {
pager: &'a mut Pager<B>,
header_id: &'a Id<B>,
header: &'a mut Header,
tree: &'a mut Tree<B>,
entry: Inner,
first: Id<B>,
last: Id<B>,
cache: Vec<u8>,
}
impl<'a, B: Backend> EntryMut<'a, B> {
fn new(
pager: &'a mut Pager<B>,
header_id: &'a Id<B>,
header: &'a mut Header,
tree: &'a mut Tree<B>,
entry: Inner,
id: Id<B>,
) -> EntryMut<'a, B> {
EntryMut {
pager,
header_id,
header,
tree,
entry,
first: id.clone(),
last: id,
cache: vec![],
}
}
pub fn write(&mut self, buf: &[u8]) -> ArchiveResult<usize, B> {
let block_size = self.pager.block_size() as u64;
let pos = (self.entry.size % block_size) as usize;
let available = if pos == 0 {
self.last = self.tree.aquire(self.pager)?.clone();
debug!("block aquired: {}", self.last);
self.cache.clear();
self.cache.resize(block_size as usize, 0);
block_size as usize
} else {
assert_eq!(self.cache.len(), block_size as usize);
block_size as usize - pos
};
let nbytes = cmp::min(buf.len(), available as usize);
debug!(
"bsize={}, pos={}, available={}, nbytes={}",
block_size, pos, available, nbytes
);
self.cache[pos..pos + nbytes].copy_from_slice(&buf[..nbytes]);
self.pager.write(&self.last, &self.cache)?;
self.entry.size += nbytes as u64;
self.entry.flush(self.pager, &self.first)?;
flush_header(self.pager, self.header_id, self.header, self.tree)?;
Ok(nbytes)
}
pub fn write_all(&mut self, mut buf: &[u8]) -> ArchiveResult<(), B> {
while !buf.is_empty() {
let n = self.write(buf)?;
buf = &buf[n..]
}
Ok(())
}
}