use backend::*;
use backend;
use error::*;
use rand;
use std;
use std::path::{Path, PathBuf};
use rustc_serialize::hex::ToHex;
use std::iter::Iterator;
use std::collections::HashMap;
impl<'env, R: rand::Rng> MutTxn<'env, R> {
fn mark_inode_moved(&mut self, inode: &Inode) {
let mut header = None;
if let Some(h) = self.get_inodes(inode) {
header = Some(h.clone())
}
if let Some(mut h) = header {
h.status = FileStatus::Moved;
self.replace_inodes(inode, &h).unwrap();
}
}
pub fn create_new_inode(&self) -> Inode {
let mut already_taken = true;
let mut inode: Inode = ROOT_INODE.clone();
while already_taken {
for i in inode.iter_mut() {
*i = rand::random()
}
already_taken = self.get_revtree(&inode).is_some();
}
inode
}
fn make_new_child(&mut self,
parent_inode: &Inode,
filename: &str,
is_dir: bool,
child_inode: Option<&Inode>)
-> Result<Inode, Error> {
let parent_id = OwnedFileId {
parent_inode: parent_inode.clone(),
basename: SmallString::from_str(filename),
};
let child_inode = match child_inode {
None => self.create_new_inode(),
Some(i) => i.clone(),
};
if self.get_tree(&parent_id.as_file_id()).is_none() {
try!(self.put_tree(&parent_id.as_file_id(), &child_inode));
try!(self.put_revtree(&child_inode, &parent_id.as_file_id()));
if is_dir {
let dir_id = OwnedFileId {
parent_inode: child_inode.clone(),
basename: SmallString::from_str(""),
};
try!(self.put_tree(&dir_id.as_file_id(), &child_inode));
};
Ok(child_inode)
} else {
Err(Error::AlreadyAdded)
}
}
pub fn add_inode(&mut self,
inode: Option<&Inode>,
path: &std::path::Path,
is_dir: bool)
-> Result<(), Error> {
let parent = path.parent().unwrap();
let (mut current_inode, unrecorded_path) = self.closest_in_repo_ancestor(&parent).unwrap();
for c in unrecorded_path {
current_inode = try!(self.make_new_child(¤t_inode,
c.as_os_str().to_str().unwrap(),
true,
None))
}
try!(self.make_new_child(¤t_inode,
path.file_name().unwrap().to_str().unwrap(),
is_dir,
inode));
Ok(())
}
pub fn move_file(&mut self,
path: &std::path::Path,
path_: &std::path::Path,
is_dir: bool)
-> Result<(), Error> {
debug!("move_file: {:?},{:?}", path, path_);
let fileref = OwnedFileId {
parent_inode: try!(self.find_inode(path.parent().unwrap())),
basename: SmallString::from_str(path.file_name().unwrap().to_str().unwrap()),
};
if let Some(inode) = self.get_tree(&fileref.as_file_id()).map(|i| i.clone()) {
debug!("txn.del fileref={:?}", fileref);
self.del_tree(&fileref.as_file_id(), None)?;
self.del_revtree(&inode, None)?;
debug!("inode={} path_={:?}", inode.to_hex(), path_);
try!(self.add_inode(Some(&inode), path_, is_dir));
self.mark_inode_moved(&inode);
Ok(())
} else {
Err(Error::FileNotInRepo(path.to_path_buf()))
}
}
fn rec_delete(&mut self, key: &Inode) -> Result<(), Error> {
debug!("rec_delete, key={:?}", key.to_hex());
let file_id = OwnedFileId {
parent_inode: key.clone(),
basename: SmallString::from_str(""),
};
let children: Vec<(_, Inode)> = self.iter_tree(Some((&file_id.as_file_id(), None)))
.take_while(|&(ref k, _)| key == k.parent_inode)
.map(|(k, v)| (k.to_owned(), v.to_owned()))
.collect();
{
for (a, b) in children {
try!(self.rec_delete(&b));
println!("deleting {:?} {:?}", a, b);
debug!("deleting from tree");
try!(self.del_tree(&a.as_file_id(), Some(&b)));
try!(self.del_revtree(&b, Some(&a.as_file_id())));
debug!("done deleting from tree");
}
}
debug!("b");
if let Some(mut header) = self.get_inodes(key).map(|h| h.clone()) {
header.status = FileStatus::Deleted;
try!(self.replace_inodes(key, &header));
} else {
let parent = self.get_revtree(key).unwrap().to_owned();
try!(self.del_tree(&parent.as_file_id(), None));
try!(self.del_revtree(key, None));
}
Ok(())
}
pub fn remove_file(&mut self, path: &std::path::Path) -> Result<(), Error> {
debug!("remove_file");
let inode = try!(self.find_inode(path));
debug!("rec_delete");
try!(self.rec_delete(&inode));
debug!("/rec_delete");
Ok(())
}
}
impl<A: Transaction, R> backend::T<A, R> {
fn collect(&self,
key: &Inode,
pb: &Path,
basename: &str,
files: &mut Vec<PathBuf>)
-> Result<(), Error> {
debug!("collecting {:?},{:?}", key, basename);
let add = match self.get_inodes(key) {
Some(inode) => {
debug!("node = {:?}", inode);
inode.status != FileStatus::Deleted
}
None => true,
};
if add {
debug!("basename = {:?}", basename);
let next_pb = pb.join(basename);
let next_pb_ = next_pb.clone();
if basename.len() > 0 {
files.push(next_pb)
}
debug!("starting iterator, key={:?}", key);
let fileid = OwnedFileId {
parent_inode: key.clone(),
basename: SmallString::from_str(""),
};
for (k, v) in self.iter_tree(Some((&fileid.as_file_id(), None)))
.take_while(|&(ref k, _)| k.parent_inode == key) {
debug!("iter: {:?} {:?}", k, v);
if k.basename.len() > 0 {
try!(self.collect(v, next_pb_.as_path(), k.basename.as_str(), files));
}
}
debug!("ending iterator {:?}", { let v: Vec<_> = self.iter_tree(Some((&fileid.as_file_id(), None))).collect(); v });
}
Ok(())
}
pub fn list_files(&self) -> Result<Vec<PathBuf>, Error> {
let mut files = Vec::new();
let mut pathbuf = PathBuf::new();
try!(self.collect(&ROOT_INODE, &mut pathbuf, "", &mut files));
Ok(files)
}
pub fn list_files_under_inode(&self,
inode: &Inode)
-> Vec<(SmallString, Option<Key<PatchId>>, Inode)> {
let mut result = Vec::new();
let file_id = OwnedFileId {
parent_inode: inode.clone(),
basename: SmallString::from_str(""),
};
for (k, v) in self.iter_tree(Some((&file_id.as_file_id(), None)))
.take_while(|&(ref k, _)| k.parent_inode == inode) {
let header = self.get_inodes(k.parent_inode).map(|x| x.clone());
println!("============= {:?} {:?}", k, v);
let add = match header {
Some(ref h) => h.status == FileStatus::Ok,
None => true,
};
if add && k.basename.len() > 0 {
result.push((k.basename.to_owned(), header.map(|h| h.key.clone()), v.clone()))
}
}
result
}
pub fn list_files_under_node(&self,
branch: &Branch,
key: &Key<PatchId>)
-> HashMap<Key<PatchId>, Vec<(FileMetadata, &str)>> {
let mut result = HashMap::new();
let e = Edge::zero(FOLDER_EDGE);
for (_, child) in self.iter_nodes(branch, Some((key, Some(&e))))
.take_while(|&(ref k, ref v)| *k == key && v.flag <= FOLDER_EDGE|PSEUDO_EDGE) {
let name = self.get_contents(&child.dest).unwrap();
let (perms, basename) = name.as_slice().split_at(2);
let perms = FileMetadata::from_contents(perms);
let basename = std::str::from_utf8(basename).unwrap();
for (_, grandchild) in self.iter_nodes(branch, Some((&child.dest, Some(&e))))
.take_while(|&(k, ref v)| *k == child.dest && v.flag <= FOLDER_EDGE|PSEUDO_EDGE) {
let names = result.entry(grandchild.dest.to_owned()).or_insert(vec![]);
names.push((perms, basename))
}
}
result
}
pub fn is_directory(&self, inode: &Inode) -> bool {
let file_id = OwnedFileId {
parent_inode: inode.clone(),
basename: SmallString::from_str(""),
};
inode == &ROOT_INODE || self.get_tree(&file_id.as_file_id()).is_some()
}
fn closest_in_repo_ancestor<'a>
(&self,
path: &'a std::path::Path)
-> Result<(Inode, std::iter::Peekable<std::path::Components<'a>>), Error> {
let mut components = path.components().peekable();
let mut fileid = OwnedFileId {
parent_inode: ROOT_INODE,
basename: SmallString::from_str(""),
};
loop {
if let Some(c) = components.peek() {
fileid.basename = SmallString::from_str(c.as_os_str().to_str().unwrap());
if let Some(v) = self.get_tree(&fileid.as_file_id()) {
fileid.parent_inode = v.clone()
} else {
break;
}
} else {
break;
}
components.next();
}
Ok((fileid.parent_inode.clone(), components))
}
pub fn find_inode(&self, path: &std::path::Path) -> Result<Inode, Error> {
let (inode, mut remaining_path_components) = try!(self.closest_in_repo_ancestor(path));
if remaining_path_components.next().is_none() {
Ok(inode)
} else {
Err(Error::FileNotInRepo(path.to_path_buf()))
}
}
pub fn file_names(&self, branch: &Branch, key: &Key<PatchId>) -> Vec<(Key<PatchId>, FileMetadata, &str)> {
let mut result = Vec::new();
let e = Edge::zero(FOLDER_EDGE | PARENT_EDGE);
for (_, parent) in self.iter_nodes(branch, Some((key, Some(&e))))
.take_while(|&(ref k, ref v)| *k == key && v.flag <= FOLDER_EDGE | PARENT_EDGE | PSEUDO_EDGE) {
match self.get_contents(&parent.dest) {
Some(ref name) if name.len() >= 2 => {
let (perms, basename) = name.as_slice().split_at(2);
let perms = FileMetadata::from_contents(perms);
let basename = std::str::from_utf8(basename).unwrap();
for (_, grandparent) in self.iter_nodes(branch, Some((&parent.dest, Some(&e))))
.take_while(|&(k, ref v)| *k == parent.dest && v.flag <= FOLDER_EDGE | PARENT_EDGE | PSEUDO_EDGE) {
result.push((grandparent.dest.to_owned(), perms, basename));
break
}
},
_ => error!("Key: {:?}, file {}, line {}",
key,
file!(), line!())
}
}
result
}
}