mononoke: remove get_mpath and get_path and use get_name

Summary:
As we discussed before, let's add get_name() method that returns MPathElement,
and remove get_path() and get_mpath().

Except for renaming, diff also make repoconfig work with tree manifest, and
fixes linknodes creation in blobimport - previously basename was used instead
of the whole path.

Reviewed By: jsgf

Differential Revision: D6857097

fbshipit-source-id: c09f3ff40d38643bd44aee8b4488277d658cf4f6
This commit is contained in:
Stanislau Hlebik 2018-02-07 07:31:25 -08:00 committed by Facebook Github Bot
parent e2d6103e27
commit 1d7668ffba
19 changed files with 250 additions and 140 deletions

View File

@ -11,7 +11,7 @@ use futures::future::Future;
use futures_ext::{BoxFuture, FutureExt};
use mercurial::file;
use mercurial_types::{Blob, MPath, ManifestId, NodeHash, Parents, RepoPath};
use mercurial_types::{Blob, MPath, MPathElement, ManifestId, NodeHash, Parents};
use mercurial_types::manifest::{Content, Entry, Manifest, Type};
use mercurial_types::nodehash::EntryId;
@ -25,7 +25,7 @@ use utils::{get_node, RawNodeBlob};
pub struct BlobEntry {
blobstore: Arc<Blobstore>,
path: RepoPath,
name: Option<MPathElement>,
id: EntryId,
ty: Type,
}
@ -53,14 +53,15 @@ pub fn fetch_file_content_from_blobstore(
}
impl BlobEntry {
pub fn new(blobstore: Arc<Blobstore>, path: MPath, nodeid: NodeHash, ty: Type) -> Result<Self> {
let path = match ty {
Type::Tree => RepoPath::dir(path)?,
_ => RepoPath::file(path)?,
};
pub fn new(
blobstore: Arc<Blobstore>,
name: Option<MPathElement>,
nodeid: NodeHash,
ty: Type,
) -> Result<Self> {
Ok(Self {
blobstore,
path,
name,
id: EntryId::new(nodeid),
ty,
})
@ -69,7 +70,7 @@ impl BlobEntry {
pub fn new_root(blobstore: Arc<Blobstore>, manifestid: ManifestId) -> Self {
Self {
blobstore,
path: RepoPath::RootPath,
name: None,
id: EntryId::new(manifestid.into_nodehash()),
ty: Type::Tree,
}
@ -158,7 +159,7 @@ impl Entry for BlobEntry {
&self.id
}
fn get_path(&self) -> &RepoPath {
&self.path
fn get_name(&self) -> &Option<MPathElement> {
&self.name
}
}

View File

@ -62,11 +62,14 @@ impl BlobManifest {
impl Manifest for BlobManifest {
fn lookup(&self, path: &MPath) -> BoxFuture<Option<Box<Entry + Sync>>, Error> {
// Path is a single MPathElement. In t25575327 we'll change the type.
let name = path.clone().into_iter().next_back();
let res = self.files.get(path).map({
move |d| {
BlobEntry::new(
self.blobstore.clone(),
path.clone(),
name,
d.entryid().into_nodehash(),
d.flag(),
)
@ -86,9 +89,10 @@ impl Manifest for BlobManifest {
.map({
let blobstore = self.blobstore.clone();
move |(path, d)| {
let name = path.clone().into_iter().next_back();
BlobEntry::new(
blobstore.clone(),
path,
name,
d.entryid().into_nodehash(),
d.flag(),
)

View File

@ -19,7 +19,7 @@ use heads::Heads;
use linknodes::Linknodes;
use mercurial::{self, RevlogManifest, RevlogRepo};
use mercurial::revlog::RevIdx;
use mercurial_types::{Changeset, Manifest, NodeHash, RepoPath};
use mercurial_types::{Changeset, MPath, Manifest, NodeHash, RepoPath};
use mercurial_types::nodehash::{ChangesetId, EntryId};
use stats::Timeseries;
@ -209,14 +209,15 @@ where
entry,
revlog_repo.clone(),
linkrev.clone(),
MPath::empty(),
)
}
})
.flatten()
.for_each(move |entry| {
.for_each(move |(entry, repopath)| {
// All entries share the same linknode to the changelog.
let linknode_future = linknodes_store.add(
entry.get_path().clone(),
repopath,
&entry.get_hash().into_nodehash(),
&linknode,
);

View File

@ -15,7 +15,7 @@ use blobrepo::RawNodeBlob;
use futures_ext::StreamExt;
use mercurial::RevlogRepo;
use mercurial::revlog::RevIdx;
use mercurial_types::{self, Blob, BlobHash, Entry, NodeHash, Parents, Type};
use mercurial_types::{self, Blob, BlobHash, Entry, MPath, NodeHash, Parents, RepoPath, Type};
use BlobstoreEntry;
@ -73,8 +73,15 @@ pub(crate) fn get_entry_stream(
entry: Box<Entry>,
revlog_repo: RevlogRepo,
cs_rev: RevIdx,
) -> Box<Stream<Item = Box<Entry>, Error = Error> + Send> {
let revlog = revlog_repo.get_path_revlog(entry.get_path());
basepath: MPath,
) -> Box<Stream<Item = (Box<Entry>, RepoPath), Error = Error> + Send> {
let path = basepath.join_element(&entry.get_name());
let repopath = if entry.get_type() == Type::Tree {
RepoPath::DirectoryPath(path.clone())
} else {
RepoPath::FilePath(path.clone())
};
let revlog = revlog_repo.get_path_revlog(&repopath);
let linkrev = revlog
.and_then(|file_revlog| file_revlog.get_entry_by_id(&entry.get_hash()))
@ -96,7 +103,9 @@ pub(crate) fn get_entry_stream(
}
match entry.get_type() {
Type::File | Type::Executable | Type::Symlink => futures::stream::once(Ok(entry)).boxify(),
Type::File | Type::Executable | Type::Symlink => {
futures::stream::once(Ok((entry, repopath))).boxify()
}
Type::Tree => entry
.get_content()
.and_then(|content| match content {
@ -105,11 +114,11 @@ pub(crate) fn get_entry_stream(
})
.flatten_stream()
.map(move |entry| {
get_entry_stream(entry, revlog_repo.clone(), cs_rev.clone())
get_entry_stream(entry, revlog_repo.clone(), cs_rev.clone(), path.clone())
})
.map_err(Error::from)
.flatten()
.chain(futures::stream::once(Ok(entry)))
.chain(futures::stream::once(Ok((entry, repopath))))
.boxify(),
}
}

View File

@ -65,7 +65,7 @@ use futures_ext::{BoxFuture, FutureExt};
use futures_stats::{Stats, Timed};
use hyper::StatusCode;
use hyper::server::{Http, Request, Response, Service};
use mercurial_types::NodeHash;
use mercurial_types::{MPathElement, NodeHash};
use native_tls::TlsAcceptor;
use native_tls::backend::openssl::TlsAcceptorBuilderExt;
use openssl::ssl::{SSL_VERIFY_FAIL_IF_NO_PEER_CERT, SSL_VERIFY_PEER};
@ -166,15 +166,21 @@ lazy_static! {
struct TreeMetadata {
hash: NodeHash,
path: PathBuf,
#[serde(rename = "type")] ty: mercurial_types::Type,
#[serde(rename = "type")]
ty: mercurial_types::Type,
size: Option<usize>,
}
impl TreeMetadata {
fn new(size: Option<usize>, entry: Box<mercurial_types::Entry>) -> TreeMetadata {
let name = entry
.get_name()
.clone()
.unwrap_or(MPathElement::new(vec![]));
TreeMetadata {
hash: entry.get_hash().into_nodehash().clone(),
path: PathBuf::from(OsString::from_vec(entry.get_mpath().to_vec())),
path: PathBuf::from(OsString::from_vec(Vec::from(name.as_bytes()))),
ty: entry.get_type(),
size,
}

View File

@ -110,7 +110,7 @@ where
.map(move |parents| (entry, parents, content, linknode, basepath))
})
.and_then(|(entry, parents, content, linknode, basepath)| {
let path = basepath.clone().join(&entry.get_mpath());
let path = basepath.join_element(entry.get_name());
let path = if path.is_empty() {
Ok(RepoPath::RootPath)
} else {

View File

@ -10,7 +10,7 @@ use failure::Error;
use futures::{stream, IntoFuture};
use futures_ext::{BoxFuture, BoxStream, FutureExt, StreamExt};
use mercurial_types::{Blob, Entry, MPath, Manifest, RepoPath, Type};
use mercurial_types::{Blob, Entry, MPath, MPathElement, Manifest, RepoPath, Type};
use mercurial_types::blobnode::Parents;
use mercurial_types::manifest::Content;
use mercurial_types::nodehash::EntryId;
@ -22,6 +22,7 @@ pub fn make_file<C: AsRef<str>>(content: C) -> ContentFactory {
Arc::new(move || Content::File(Blob::Dirty(content.clone())))
}
#[derive(Clone)]
pub struct MockManifest {
entries: Vec<MockEntry>,
}
@ -47,10 +48,14 @@ impl MockManifest {
MockManifest { entries }
}
pub fn with_content(content: Vec<(&'static str, ContentFactory)>) -> Self {
pub fn with_content(content: Vec<(&'static str, ContentFactory, Type)>) -> Self {
let entries = content
.into_iter()
.map(|(p, c)| MockEntry::new(Self::p(p), c))
.map(|(p, c, ty)| {
let mut mock_entry = MockEntry::new(Self::p(p), c);
mock_entry.set_type(ty);
mock_entry
})
.collect();
MockManifest { entries }
}
@ -67,6 +72,7 @@ impl Manifest for MockManifest {
pub struct MockEntry {
path: RepoPath,
name: Option<MPathElement>,
content_factory: ContentFactory,
ty: Option<Type>,
hash: Option<EntryId>,
@ -76,6 +82,7 @@ impl Clone for MockEntry {
fn clone(&self) -> Self {
MockEntry {
path: self.path.clone(),
name: self.name.clone(),
content_factory: self.content_factory.clone(),
ty: self.ty.clone(),
hash: self.hash.clone(),
@ -85,8 +92,15 @@ impl Clone for MockEntry {
impl MockEntry {
pub fn new(path: RepoPath, content_factory: ContentFactory) -> Self {
let name = match path.clone() {
RepoPath::RootPath => None,
RepoPath::FilePath(path) | RepoPath::DirectoryPath(path) => {
path.clone().into_iter().next_back()
}
};
MockEntry {
path,
name,
content_factory,
ty: None,
hash: None,
@ -124,7 +138,7 @@ impl Entry for MockEntry {
None => panic!("hash is not set!"),
}
}
fn get_path(&self) -> &RepoPath {
&self.path
fn get_name(&self) -> &Option<MPathElement> {
&self.name
}
}

View File

@ -14,7 +14,7 @@ use blob::Blob;
use blobnode::Parents;
use futures_ext::{BoxFuture, BoxStream, FutureExt, StreamExt};
use nodehash::EntryId;
use path::{MPath, RepoPath};
use path::{MPath, MPathElement};
/// Interface for a manifest
///
@ -155,15 +155,8 @@ pub trait Entry: Send + 'static {
/// Get the identity of the object this entry refers to.
fn get_hash(&self) -> &EntryId;
/// Get the full path of this entry (meaningless - see T25575327)
fn get_path(&self) -> &RepoPath;
fn get_mpath(&self) -> MPath {
match self.get_path() {
&RepoPath::RootPath => MPath::empty(),
&RepoPath::DirectoryPath(ref path) | &RepoPath::FilePath(ref path) => path.clone(),
}
}
/// Get the name of the entry. None means that this is a root entry
fn get_name(&self) -> &Option<MPathElement>;
/// Return an Entry as a type-erased trait object.
/// (Do we still need this as a trait method? T25577105)
@ -222,12 +215,8 @@ where
self.entry.get_hash()
}
fn get_path(&self) -> &RepoPath {
self.entry.get_path()
}
fn get_mpath(&self) -> MPath {
self.entry.get_mpath()
fn get_name(&self) -> &Option<MPathElement> {
self.entry.get_name()
}
}
@ -256,11 +245,7 @@ impl Entry for Box<Entry + Sync> {
(**self).get_hash()
}
fn get_path(&self) -> &RepoPath {
(**self).get_path()
}
fn get_mpath(&self) -> MPath {
(**self).get_mpath()
fn get_name(&self) -> &Option<MPathElement> {
(**self).get_name()
}
}

View File

@ -9,7 +9,7 @@ use futures::stream::{empty, iter_ok, once, Stream};
use futures_ext::{BoxStream, StreamExt};
use std::collections::VecDeque;
use super::{Entry, MPath, Manifest};
use super::{Entry, MPath, MPathElement, Manifest};
use super::manifest::{Content, Type};
use errors::*;
@ -84,15 +84,18 @@ fn recursive_changed_entry_stream(changed_entry: ChangedEntry) -> BoxStream<Chan
let substream = if left.get_type() == Type::Tree {
let contents = left.get_content().join(right.get_content());
let path = changed_entry.path.clone();
let entry_path = left.get_mpath().clone();
let entry_path = left.get_name().clone();
let substream = contents
.map(move |(left_content, right_content)| {
let left_manifest = get_tree_content(left_content);
let right_manifest = get_tree_content(right_content);
diff_manifests(path.join(&entry_path), left_manifest, right_manifest)
.map(recursive_changed_entry_stream)
diff_manifests(
path.join_element(&entry_path),
left_manifest,
right_manifest,
).map(recursive_changed_entry_stream)
})
.flatten_stream()
.flatten();
@ -115,15 +118,15 @@ fn recursive_changed_entry_stream(changed_entry: ChangedEntry) -> BoxStream<Chan
/// Given an entry and path from the root of the repo to this entry, returns all subentries with
/// their path from the root of the repo.
/// For a non-tree entry returns a stream with a single (entry, path) pair.
fn recursive_entry_stream(
pub fn recursive_entry_stream(
rootpath: MPath,
entry: Box<Entry + Sync>,
) -> BoxStream<(MPath, Box<Entry + Sync>), Error> {
let subentries = match entry.get_type() {
Type::File | Type::Symlink | Type::Executable => empty().boxify(),
Type::Tree => {
let entry_basename = entry.get_mpath().clone();
let path = rootpath.join(&entry_basename);
let entry_basename = entry.get_name();
let path = rootpath.join(entry_basename);
entry
.get_content()
@ -175,8 +178,14 @@ pub fn diff_sorted_vecs(
loop {
match (left.pop_front(), right.pop_front()) {
(Some(left_entry), Some(right_entry)) => {
let left_path = left_entry.get_mpath().to_vec();
let right_path = right_entry.get_mpath().to_vec();
let left_path = left_entry
.get_name()
.clone()
.unwrap_or(MPathElement::new(vec![]));
let right_path = right_entry
.get_name()
.clone()
.unwrap_or(MPathElement::new(vec![]));
if left_path < right_path {
res.push(ChangedEntry::new_added(path.clone(), left_entry));

View File

@ -4,7 +4,6 @@
// This software may be used and distributed according to the terms of the
// GNU General Public License version 2 or any later version.
use std::cmp;
use std::convert::{From, TryFrom, TryInto};
use std::ffi::OsStr;
@ -204,6 +203,13 @@ impl MPath {
}
}
pub fn join_element(&self, element: &Option<MPathElement>) -> MPath {
match element {
&Some(ref element) => self.join(element),
&None => self.clone(),
}
}
pub fn generate<W: Write>(&self, out: &mut W) -> io::Result<()> {
out.write_all(&self.to_vec())
}

View File

@ -197,18 +197,14 @@ fn check_changed_paths(
for changed_entry in actual {
match changed_entry.status {
EntryStatus::Added(entry) => {
paths_added.push(changed_entry.path.join(&entry.get_mpath()));
paths_added.push(changed_entry.path.join_element(&entry.get_name()));
}
EntryStatus::Deleted(entry) => {
paths_deleted.push(changed_entry.path.join(&entry.get_mpath()));
paths_deleted.push(changed_entry.path.join_element(&entry.get_name()));
}
EntryStatus::Modified(left_entry, right_entry) => {
assert_eq!(left_entry.get_type(), right_entry.get_type());
assert_eq!(
left_entry.get_mpath().to_vec(),
right_entry.get_mpath().to_vec()
);
paths_modified.push(changed_entry.path.join(&left_entry.get_mpath()));
paths_modified.push(changed_entry.path.join_element(&left_entry.get_name()));
}
}
}

View File

@ -17,7 +17,7 @@ use futures_ext::{BoxFuture, BoxStream, FutureExt, StreamExt};
use errors::*;
use failure;
use file;
use mercurial_types::{Blob, BlobNode, MPath, NodeHash, Parents, RepoPath};
use mercurial_types::{Blob, BlobNode, MPath, MPathElement, NodeHash, Parents, RepoPath};
use mercurial_types::manifest::{Content, Entry, Manifest, Type};
use mercurial_types::nodehash::EntryId;
@ -200,6 +200,7 @@ where
pub struct RevlogEntry {
repo: RevlogRepo,
path: RepoPath,
name: Option<MPathElement>,
details: Details,
}
@ -247,18 +248,27 @@ impl Manifest for RevlogManifest {
impl RevlogEntry {
fn new(repo: RevlogRepo, path: MPath, details: Details) -> Result<Self> {
let name = (&path).into_iter().next_back().map(|path| path.clone());
let path = match details.flag() {
Type::Tree => RepoPath::dir(path)
.with_context(|_| ErrorKind::Path("error while creating RepoPath".into()))?,
_ => RepoPath::file(path)
.with_context(|_| ErrorKind::Path("error while creating RepoPath".into()))?,
};
// For revlog we still need to store full path, because full path is used to find revlog
// file
Ok(RevlogEntry {
repo,
path,
name,
details,
})
}
fn get_path(&self) -> &RepoPath {
&self.path
}
}
impl Entry for RevlogEntry {
@ -351,8 +361,8 @@ impl Entry for RevlogEntry {
&self.details.entryid
}
fn get_path(&self) -> &RepoPath {
&self.path
fn get_name(&self) -> &Option<MPathElement> {
&self.name
}
}
@ -424,8 +434,9 @@ mod test {
(
MPath::new(b"hello123").unwrap(),
Details {
entryid : EntryId::new(
"da39a3ee5e6b4b0d3255bfef95601890afd80709".parse().unwrap()),
entryid: EntryId::new(
"da39a3ee5e6b4b0d3255bfef95601890afd80709".parse().unwrap(),
),
flag: Type::Symlink,
},
),

View File

@ -225,6 +225,7 @@ mod test {
use std::sync::Arc;
use mercurial_types::Type;
use mercurial_types_mocks::manifest::{make_file, MockManifest};
#[test]
@ -239,10 +240,26 @@ mod test {
repotype="revlog"
"#;
let my_path_manifest = MockManifest::with_content(vec![
("my_files", Arc::new(|| unimplemented!()), Type::File),
]);
let repos_manifest = MockManifest::with_content(vec![
("fbsource", make_file(fbsource_content), Type::File),
("www", make_file(www_content), Type::File),
]);
let repoconfig = RepoConfigs::read_manifest(&MockManifest::with_content(vec![
("my_path/my_files", Arc::new(|| unimplemented!())),
("repos/fbsource", make_file(fbsource_content)),
("repos/www", make_file(www_content)),
(
"my_path",
Arc::new(move || Content::Tree(Box::new(my_path_manifest.clone()))),
Type::File,
),
(
"repos",
Arc::new(move || Content::Tree(Box::new(repos_manifest.clone()))),
Type::Tree,
),
])).wait()
.expect("failed to read config from manifest");

View File

@ -26,7 +26,7 @@ use mercurial;
use mercurial_bundles::{parts, Bundle2EncodeBuilder};
use mercurial_bundles::bundle2::{self, Bundle2Stream, StreamEvent};
use mercurial_types::{percent_encode, BlobNode, Changeset, Entry, MPath, ManifestId, NodeHash,
Parents, Type, NULL_HASH};
Parents, RepoPath, Type, NULL_HASH};
use mercurial_types::manifest_utils::{changed_entry_stream, EntryStatus};
use metaconfig::repoconfig::RepoType;
@ -510,14 +510,14 @@ impl HgCommands for RepoClient {
.filter_map(move |entry_status| match entry_status.status {
EntryStatus::Added(entry) => {
if entry.get_type() == Type::Tree {
Some(entry)
Some((entry, entry_status.path))
} else {
None
}
}
EntryStatus::Modified(entry, _) => {
if entry.get_type() == Type::Tree {
Some(entry)
Some((entry, entry_status.path))
} else {
None
}
@ -526,9 +526,9 @@ impl HgCommands for RepoClient {
})
.and_then({
let hgrepo = self.repo.hgrepo.clone();
move |val| fetch_linknode(hgrepo.clone(), val)
move |(entry, path)| fetch_linknode(hgrepo.clone(), entry, path)
})
.map(|(entry, linknode)| (entry, linknode, MPath::empty()));
.map(|(entry, linknode, basepath)| (entry, linknode, basepath));
// Append root manifest
let root_entry_stream = Ok(self.repo
@ -537,9 +537,9 @@ impl HgCommands for RepoClient {
.into_future()
.and_then({
let hgrepo = self.repo.hgrepo.clone();
move |val| fetch_linknode(hgrepo.clone(), val)
move |entry| fetch_linknode(hgrepo.clone(), entry, MPath::empty())
})
.map(|(entry, linknode)| stream::once(Ok((entry, linknode, MPath::empty()))))
.map(|(entry, linknode, basepath)| stream::once(Ok((entry, linknode, basepath))))
.flatten_stream();
parts::treepack_part(changed_entries.chain(root_entry_stream))
@ -557,8 +557,22 @@ impl HgCommands for RepoClient {
fn fetch_linknode(
repo: Arc<BlobRepo>,
entry: Box<Entry + Sync>,
) -> BoxFuture<(Box<Entry + Sync>, NodeHash), Error> {
let linknode_fut =
repo.get_linknode(entry.get_path().clone(), &entry.get_hash().into_nodehash());
linknode_fut.map(|linknode| (entry, linknode)).boxify()
basepath: MPath,
) -> BoxFuture<(Box<Entry + Sync>, NodeHash, MPath), Error> {
let path = match entry.get_name() {
&Some(ref name) => {
let path = basepath.clone().join(name.clone().into_iter());
if entry.get_type() == Type::Tree {
RepoPath::DirectoryPath(path)
} else {
RepoPath::FilePath(path)
}
}
&None => RepoPath::RootPath,
};
let linknode_fut = repo.get_linknode(path, &entry.get_hash().into_nodehash());
linknode_fut
.map(|linknode| (entry, linknode, basepath))
.boxify()
}

View File

@ -16,6 +16,31 @@ function wait_for_mononoke {
done
}
function setup_config_repo {
hg init mononoke-config
cd mononoke-config || exit
cat >> .hg/hgrc <<EOF
[extensions]
treemanifest=
remotefilelog=
[treemanifest]
server=True
[remotefilelog]
server=True
shallowtrees=True
EOF
mkdir repos
cat > repos/repo <<CONFIG
path="$TESTTMP/repo"
repotype="blob:files"
CONFIG
hg add -q repos
hg ci -ma
hg bookmark test-config
hg backfilltree
cd ..
}
function blobimport {
$MONONOKE_BLOBIMPORT "$@"
}

View File

@ -1,27 +1,7 @@
$ . $TESTDIR/library.sh
setup configuration
$ hg init mononoke-config
$ cd mononoke-config
$ mkdir repos
$ cat > repos/repo <<CONFIG
> path="$TESTTMP/repo"
> repotype="blob:files"
> CONFIG
$ hg add repos
adding repos/repo
$ hg ci -ma
$ hg bookmark test-config
$ hg log
changeset: 0:* (glob)
bookmark: test-config
tag: tip
user: test
date: Thu Jan 01 00:00:00 1970 +0000
summary: a
$ setup_config_repo
$ cd $TESTTMP
setup common configuration

View File

@ -2,25 +2,7 @@
setup configuration
$ hg init mononoke-config
$ cd mononoke-config
$ mkdir repos
$ cat > repos/repo <<CONFIG
> path="$TESTTMP/repo"
> repotype="blob:files"
> CONFIG
$ hg add repos
adding repos/repo
$ hg ci -ma
$ hg bookmark test-config
$ hg log
changeset: 0:* (glob)
bookmark: test-config
tag: tip
user: test
date: Thu Jan 01 00:00:00 1970 +0000
summary: a
$ setup_config_repo
$ cd $TESTTMP

View File

@ -13,6 +13,7 @@
#[macro_use]
extern crate failure_ext as failure;
extern crate futures;
extern crate futures_ext;
extern crate itertools;
extern crate mercurial_types;

View File

@ -9,9 +9,10 @@ use std::sync::Arc;
use futures::{Future, Stream};
use mercurial_types::{Entry, Manifest};
use mercurial_types::{Entry, Manifest, Type};
use mercurial_types::manifest::Content;
use mercurial_types::path::{MPathElement, DOT, DOTDOT};
use mercurial_types::manifest_utils::recursive_entry_stream;
use mercurial_types::path::{MPath, MPathElement, DOT, DOTDOT};
use node::{VfsDir, VfsFile, VfsNode};
use tree::{TNodeId, Tree, TreeValue, ROOT_ID};
@ -33,18 +34,23 @@ where
manifest
.list()
.map_err(|err| {
err.context("failed while listing the manifest").into()
let error: Error = err.context("failed while listing the manifest").into();
error
})
.map(|entry| recursive_entry_stream(MPath::empty(), entry))
.flatten()
.filter(|pathentry| pathentry.1.get_type() != Type::Tree)
.collect()
.and_then(|entries| {
.and_then(|pathentries| {
let mut path_tree = Tree::new();
for (entry_idx, entry) in entries.iter().enumerate() {
let mut path = entry.get_mpath().clone().into_iter();
let leaf_key = path.next_back().ok_or_else(|| {
ErrorKind::ManifestInvalidPath("the path shouldn't be empty".into())
let mut entries = vec![];
for (entry_idx, (path, entry)) in pathentries.into_iter().enumerate() {
let name = entry.get_name().clone();
let name = name.ok_or_else(|| {
ErrorKind::ManifestInvalidPath("name shouldn't be empty".into())
})?;
path_tree.insert(path, leaf_key, TEntryId(entry_idx))?;
path_tree.insert(path, name, TEntryId(entry_idx))?;
entries.push(entry);
}
Ok(ManifestVfsDir {
root: Arc::new(ManifestVfsRoot { entries, path_tree }),
@ -180,6 +186,7 @@ mod test {
use test::*;
use mercurial_types::MPath;
use mercurial_types_mocks::manifest::MockManifest;
use node::VfsWalker;
fn unwrap_dir<TDir: VfsDir>(node: VfsNode<TDir, TDir::TFile>) -> TDir {
@ -208,7 +215,49 @@ mod test {
}
fn example_vfs() -> ManifestVfsDir {
get_vfs(vec!["a/b", "a/ab", "c/d/e", "c/d/da", "c/ca/afsd", "f"])
let a_manifest = MockManifest::with_content(vec![
("b", Arc::new(|| unimplemented!()), Type::File),
("ab", Arc::new(|| unimplemented!()), Type::File),
]);
let d_manifest = MockManifest::with_content(vec![
("e", Arc::new(|| unimplemented!()), Type::File),
("da", Arc::new(|| unimplemented!()), Type::File),
]);
let ca_manifest =
MockManifest::with_content(vec![("afsd", Arc::new(|| unimplemented!()), Type::File)]);
let c_manifest = MockManifest::with_content(vec![
(
"d",
Arc::new(move || Content::Tree(Box::new(d_manifest.clone()))),
Type::Tree,
),
(
"ca",
Arc::new(move || Content::Tree(Box::new(ca_manifest.clone()))),
Type::Tree,
),
]);
let root_manifest = MockManifest::with_content(vec![
(
"a",
Arc::new(move || Content::Tree(Box::new(a_manifest.clone()))),
Type::Tree,
),
(
"c",
Arc::new(move || Content::Tree(Box::new(c_manifest.clone()))),
Type::Tree,
),
("f", Arc::new(|| unimplemented!()), Type::File),
]);
vfs_from_manifest(&root_manifest)
.wait()
.expect("failed to get vfs")
}
#[test]