use backend;
use backend::*;
use {Error, Result};
use fs_representation::{RepoPath, in_repo_root};
use rand;
use std;
use std::collections::BTreeMap;
use std::iter::Iterator;
use std::path::{Path, PathBuf};
impl<'env, R: rand::Rng> MutTxn<'env, R> {
pub 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> {
let parent_id = OwnedFileId {
parent_inode: parent_inode.clone(),
basename: SmallString::from_str(filename),
};
if filename == ".pijul" {
return Err(Error::CannotAddDotPijul);
}
if let Some(inode) = self.get_tree(&parent_id.as_file_id()) {
let mut header = if let Some(header) = self.get_inodes(inode) {
header.to_owned()
} else {
return Err(Error::AlreadyAdded);
};
if let FileStatus::Ok = header.status {
} else {
header.status = FileStatus::Ok;
self.replace_inodes(inode, header)?;
}
Ok(inode)
} else {
let child_inode = match child_inode {
None => self.create_new_inode(),
Some(i) => i.clone(),
};
self.put_tree(&parent_id.as_file_id(), child_inode)?;
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(""),
};
self.put_tree(&dir_id.as_file_id(), child_inode)?;
};
Ok(child_inode)
}
}
pub fn add_inode<P: AsRef<Path>>(
&mut self,
inode: Option<Inode>,
path: &RepoPath<P>,
is_dir: bool,
) -> Result<()> {
if let Some(parent) = path.parent() {
let (mut current_inode, unrecorded_path) =
self.closest_in_repo_ancestor(&parent).unwrap();
for c in unrecorded_path {
current_inode =
self.make_new_child(current_inode, c.as_os_str().to_str().unwrap(), true, None)?
}
self.make_new_child(
current_inode,
path.file_name().unwrap().to_str().unwrap(),
is_dir,
inode,
)?;
}
Ok(())
}
pub fn inode_is_ancestor_of(&self, a: Inode, mut b: Inode) -> bool {
loop {
if a == b {
return true;
}
if let Some(b_parent) = self.get_revtree(b) {
b = b_parent.parent_inode
} else {
return false;
}
}
}
pub fn move_file(
&mut self,
origin: &RepoPath<impl AsRef<Path>>,
destination: &RepoPath<impl AsRef<Path>>,
is_dir: bool,
) -> Result<()> {
debug!("move_file: {},{}", origin.display(), destination.display());
if let Some(parent) = origin.parent() {
let fileref = OwnedFileId {
parent_inode: self.find_inode(&parent)?,
basename: SmallString::from_str(origin.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={} destination={}", inode.to_hex(), destination.display());
self.add_inode(Some(inode), destination, is_dir)?;
self.mark_inode_moved(inode);
return Ok(());
}
}
Err(Error::FileNotInRepo(origin.to_path_buf()))
}
pub fn rec_delete(&mut self, key: Inode) -> Result<bool> {
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)
.filter(|&(ref k, _)| !k.basename.is_empty())
.map(|(k, v)| (k.to_owned(), v.to_owned()))
.collect();
let mut has_recorded_descendants = false;
for (_, b) in children {
debug!("deleting from tree {:?}", b);
has_recorded_descendants |= self.rec_delete(b)?;
}
if let Some(mut header) = self.get_inodes(key).map(|h| h.clone()) {
debug!("key {:?}, header = {:?}", key, header);
header.status = FileStatus::Deleted;
self.replace_inodes(key, header)?;
debug!("after = {:?}", self.get_inodes(key).map(|h| h.clone()));
} else if !has_recorded_descendants {
let parent = self.get_revtree(key).unwrap().to_owned();
debug!("key = {:?}, parent = {:?}", key, parent);
self.del_tree(&parent.as_file_id(), None)?;
self.del_revtree(key, None)?;
}
Ok(has_recorded_descendants)
}
pub fn remove_file(&mut self, path: &RepoPath<impl AsRef<Path>>)
-> Result<()> {
debug!("remove_file");
let inode = self.find_inode(path)?;
debug!("rec_delete");
self.rec_delete(inode)?;
debug!("/rec_delete");
Ok(())
}
}
impl<A: Transaction, R> backend::GenericTxn<A, R> {
fn collect(
&self,
key: Inode,
current_path: &RepoPath<impl AsRef<Path>>,
basename: &str,
files: &mut Vec<RepoPath<PathBuf>>,
) -> Result<()> {
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 = current_path.join(Path::new(basename));
if basename.len() > 0 {
files.push(next_pb.clone())
}
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 {
self.collect(v.to_owned(), &next_pb, 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, inode: Inode) -> Result<Vec<RepoPath<PathBuf>>> {
debug!("list_files {:?}", inode);
let mut files = Vec::new();
self.collect(inode, &in_repo_root(), "", &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,
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>,
) -> BTreeMap<Key<PatchId>, Vec<(FileMetadata, &str)>> {
let mut result = BTreeMap::new();
let e = Edge::zero(EdgeFlags::FOLDER_EDGE);
for (_, child) in self
.iter_nodes(branch, Some((key, Some(e))))
.take_while(|&(k, ref v)| {
k == key && v.flag <= EdgeFlags::FOLDER_EDGE | EdgeFlags::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 <= EdgeFlags::FOLDER_EDGE | EdgeFlags::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 RepoPath<impl AsRef<Path>>
) -> Result<(Inode, std::iter::Peekable<std::path::Components<'a>>)> {
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: &RepoPath<impl AsRef<Path>>)
-> Result<Inode> {
let (inode, mut remaining_path_components) = 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(EdgeFlags::FOLDER_EDGE | EdgeFlags::PARENT_EDGE);
debug!("file_names, key {:?}", key);
for (_, parent) in self
.iter_nodes(branch, Some((key, Some(e))))
.take_while(|&(k, _)| k == key)
.filter(|&(_, ref v)| {
v.flag
.contains(EdgeFlags::FOLDER_EDGE | EdgeFlags::PARENT_EDGE)
})
{
debug!("file_names, parent {:?}", parent);
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, _)| k == parent.dest)
.filter(|&(_, ref v)| {
v.flag
.contains(EdgeFlags::FOLDER_EDGE | EdgeFlags::PARENT_EDGE)
})
{
result.push((grandparent.dest.to_owned(), perms, basename));
break;
}
}
_ => error!("Key: {:?}, file {}, line {}", key, file!(), line!()),
}
}
result
}
pub fn prefix_keys(&self, branch: &Branch, path: &RepoPath<impl AsRef<Path>>)
-> Result<Vec<Key<PatchId>>> {
let mut result = Vec::new();
let e = Edge::zero(EdgeFlags::FOLDER_EDGE);
let mut current_key = ROOT_KEY;
for comp in path.components() {
let mut is_first = true;
let cur = current_key;
for (_, child) in self
.iter_nodes(branch, Some((current_key, Some(e))))
.take_while(|&(k, _)| k == cur)
.filter(|&(_, ref v)| v.flag.contains(EdgeFlags::FOLDER_EDGE))
{
let contents = self.get_contents(child.dest).unwrap();
if contents.into_cow().split_at(2).1
== comp.as_os_str().to_str().expect("file encoding problem").as_bytes() {
if !is_first {
return Err(Error::FileNameCount(current_key));
}
for (_, grandchild) in self
.iter_nodes(branch, Some((child.dest, Some(e))))
.take_while(|&(k, _)| k == child.dest)
.filter(|&(_, ref v)| v.flag.contains(EdgeFlags::FOLDER_EDGE))
{
result.push(grandchild.dest);
current_key = grandchild.dest;
}
}
}
}
Ok(result)
}
}