use crate::changestore::ChangeStore;
use crate::path;
use crate::pristine::*;
use std::collections::HashMap;
mod output;
pub use output::*;
mod archive;
pub use archive::*;
#[derive(Debug, Error)]
pub enum OutputError<
ChangestoreError: std::error::Error + 'static,
Txn: std::error::Error + 'static,
W: std::error::Error + 'static,
> {
#[error("Working copy error: {0}")]
WorkingCopy(W),
#[error(transparent)]
Pristine(#[from] PristineOutputError<ChangestoreError, Txn>),
}
#[derive(Debug, Error)]
pub enum PristineOutputError<ChangestoreError: std::error::Error, Txn: std::error::Error + 'static>
{
#[error(transparent)]
Txn(Txn),
#[error("Changestore error: {0}")]
Changestore(ChangestoreError),
#[error(transparent)]
Io(#[from] std::io::Error),
#[error(transparent)]
Fs(#[from] crate::fs::FsError<Txn>),
}
use crate::working_copy::WriteError;
impl<C: std::error::Error, T: std::error::Error + 'static, W: std::error::Error>
OutputError<C, T, W>
{
fn from(e: WriteError<Self>) -> Self {
match e {
WriteError::Io(e) => OutputError::Pristine(PristineOutputError::Io(e)),
WriteError::E(e) => e,
}
}
}
impl<C: std::error::Error, T: std::error::Error + 'static> From<TxnErr<T>>
for PristineOutputError<C, T>
{
fn from(e: TxnErr<T>) -> Self {
PristineOutputError::Txn(e.0)
}
}
impl<C: std::error::Error, T: std::error::Error + 'static, W: std::error::Error> From<TxnErr<T>>
for OutputError<C, T, W>
{
fn from(e: TxnErr<T>) -> Self {
OutputError::Pristine(e.into())
}
}
#[derive(Debug, Error)]
pub enum FileError<ChangestoreError: std::error::Error + 'static, T: std::error::Error + 'static> {
#[error(transparent)]
Changestore(ChangestoreError),
#[error(transparent)]
Txn(T),
#[error(transparent)]
Io(#[from] std::io::Error),
}
impl<C: std::error::Error, T: std::error::Error + 'static> From<FileError<C, T>>
for PristineOutputError<C, T>
{
fn from(e: FileError<C, T>) -> Self {
match e {
FileError::Changestore(e) => PristineOutputError::Changestore(e),
FileError::Io(e) => PristineOutputError::Io(e),
FileError::Txn(t) => PristineOutputError::Txn(t),
}
}
}
impl<C: std::error::Error, T: std::error::Error + 'static> From<TxnErr<T>> for FileError<C, T> {
fn from(e: TxnErr<T>) -> Self {
FileError::Txn(e.0)
}
}
#[derive(Debug)]
struct OutputItem {
parent: Inode,
path: String,
meta: InodeMetadata,
pos: Position<ChangeId>,
is_zombie: bool,
}
fn collect_children<T: GraphTxnT, P: ChangeStore>(
txn: &T,
changes: &P,
channel: &T::Graph,
inode_pos: Position<ChangeId>,
inode: Inode,
path: &str,
prefix_basename: Option<&str>,
files: &mut HashMap<String, Vec<(Vertex<ChangeId>, OutputItem)>>,
) -> Result<(), PristineOutputError<P::Error, T::GraphError>> {
debug!("path = {:?}", path);
for e in iter_adjacent(
txn,
channel,
inode_pos.inode_vertex(),
EdgeFlags::FOLDER,
EdgeFlags::FOLDER | EdgeFlags::PSEUDO | EdgeFlags::BLOCK,
)? {
let e = e?;
let name_vertex = txn.find_block(channel, e.dest).unwrap();
let mut name_buf = Vec::new();
changes
.get_contents(|h| txn.get_external(h).unwrap(), name_vertex, &mut name_buf)
.map_err(PristineOutputError::Changestore)?;
let (perms, basename) = name_buf.as_slice().split_at(2);
let (perms, basename) = (
InodeMetadata::from_basename(perms),
std::str::from_utf8(basename).unwrap(),
);
debug!("filename: {:?} {:?}", perms, basename);
let mut name = path.to_string();
if let Some(next) = prefix_basename {
if next != basename {
continue;
}
}
path::push(&mut name, basename);
debug!("name_vertex: {:?} {:?}", e, name_vertex);
let child = if let Some(child) = iter_adjacent(
txn,
channel,
name_vertex,
EdgeFlags::FOLDER,
EdgeFlags::FOLDER | EdgeFlags::BLOCK | EdgeFlags::PSEUDO,
)?
.next()
{
child?
} else {
let mut edge = None;
for e in iter_adjacent(
txn,
channel,
name_vertex,
EdgeFlags::FOLDER,
EdgeFlags::all(),
)? {
let e = e?;
if !e.flag.contains(EdgeFlags::PARENT) {
edge = Some(e);
break;
}
}
let e = edge.unwrap();
let mut f = std::fs::File::create("debug_output").unwrap();
debug_root(txn, channel, e.dest.inode_vertex(), &mut f, false).unwrap();
panic!("no child");
};
debug!("child: {:?}", child);
let v = files.entry(name).or_insert_with(Vec::new);
v.push((
name_vertex,
OutputItem {
parent: inode,
path: path.to_string(),
meta: perms,
pos: child.dest,
is_zombie: is_zombie(txn, channel, child.dest)?,
},
));
}
Ok(())
}
fn is_zombie<T: GraphTxnT>(
txn: &T,
channel: &T::Graph,
pos: Position<ChangeId>,
) -> Result<bool, TxnErr<T::GraphError>> {
let f = EdgeFlags::FOLDER | EdgeFlags::PARENT | EdgeFlags::DELETED;
if let Some(n) =
iter_adjacent(txn, channel, pos.inode_vertex(), f, f | EdgeFlags::BLOCK)?.next()
{
n?;
Ok(true)
} else {
Ok(false)
}
}