sapling/eden/mononoke/cmds/admin/content_fetch.rs
Thomas Orozco c7478113a3 mononoke/mercurial_types: get rid of HgManifest & HgEntry
Summary:
This trait is no longer used all that much outsides of a handful of tests, the
walker, and an admin subcommand, as it has been replaced by the `Manifest`
trait, which works over all kinds of Manifests, and has stronger typing (its
sub-entries always have a path, and they are wrapped in an enum that knows if
they're leaves or trees).

This left a bunch of old legacy code here or there, which is worth removing
to make sure we don't introduce any new callsites to this. Another motivation
is that this legacy code is often not very compatible with new code, and has
historically made it a bit tricky (everything owns a blobstore in this code,
which is pretty awkward and not at all how we do things nowadays).

There is, I think, a bit more potential here since we could also perhaps try to
remove the `HgBlobEntry` struct, but that has a callsites still, so I'm not
doing this here.

Reviewed By: StanislavGlebik

Differential Revision: D24306946

fbshipit-source-id: 8a73dbbf40a904ce19ac65d791b732091c206263
2020-10-15 04:56:13 -07:00

128 lines
4.1 KiB
Rust

/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This software may be used and distributed according to the terms of the
* GNU General Public License version 2.
*/
use anyhow::{format_err, Error};
use blobrepo::BlobRepo;
use blobrepo_hg::BlobRepoHg;
use blobstore::Loadable;
use clap::{App, ArgMatches, SubCommand};
use cmdlib::{args, helpers};
use context::CoreContext;
use fbinit::FacebookInit;
use futures::compat::Future01CompatExt;
use manifest::{Entry, Manifest, ManifestOps};
use mercurial_types::{HgFileNodeId, HgManifestId, MPath};
use mononoke_types::FileType;
use slog::Logger;
use crate::error::SubcommandError;
pub const CONTENT_FETCH: &str = "content-fetch";
pub fn build_subcommand<'a, 'b>() -> App<'a, 'b> {
SubCommand::with_name(CONTENT_FETCH)
.about("fetches content of the file or manifest from blobrepo")
.args_from_usage(
"<CHANGESET_ID> 'hg/bonsai id or bookmark to fetch file from'
<PATH> 'path to fetch'",
)
}
pub async fn subcommand_content_fetch<'a>(
fb: FacebookInit,
logger: Logger,
matches: &'a ArgMatches<'_>,
sub_m: &'a ArgMatches<'_>,
) -> Result<(), SubcommandError> {
let rev = sub_m.value_of("CHANGESET_ID").unwrap().to_string();
let path = sub_m.value_of("PATH").unwrap().to_string();
async {
args::init_cachelib(fb, &matches, None);
let ctx = CoreContext::new_with_logger(fb, logger.clone());
let repo = args::open_repo(fb, &logger, &matches).compat().await?;
let entry = fetch_entry(&ctx, &repo, &rev, &path).await?;
match entry {
Entry::Leaf((FileType::Executable, _)) => {
println!("Binary file");
}
Entry::Leaf((FileType::Symlink, id)) | Entry::Leaf((FileType::Regular, id)) => {
let envelope = id.load(ctx.clone(), repo.blobstore()).await?;
let bytes = filestore::fetch_concat(
&repo.get_blobstore(),
ctx.clone(),
envelope.content_id(),
)
.compat()
.await?;
let content = String::from_utf8(bytes.to_vec()).expect("non-utf8 file content");
println!("{}", content);
}
Entry::Tree(id) => {
let manifest = id.load(ctx.clone(), repo.blobstore()).await?;
let entries: Vec<_> = manifest.list().collect();
let mut longest_len = 0;
for (name, _) in entries.iter() {
let basename_len = name.len();
if basename_len > longest_len {
longest_len = basename_len;
}
}
for (name, entry) in entries {
let mut name = String::from_utf8_lossy(name.as_ref()).to_string();
for _ in name.len()..longest_len {
name.push(' ');
}
let (t, h) = match entry {
Entry::Leaf((t, id)) => (t.to_string(), id.to_string()),
Entry::Tree(id) => ("tree".to_string(), id.to_string()),
};
println!("{} {} {}", name, h, t);
}
}
};
Result::<_, Error>::Ok(())
}
.await
.map_err(|e| e.into())
}
async fn fetch_entry(
ctx: &CoreContext,
repo: &BlobRepo,
rev: &str,
path: &str,
) -> Result<Entry<HgManifestId, (FileType, HgFileNodeId)>, Error> {
let mpath = MPath::new(path)?;
let bcs_id = helpers::csid_resolve(ctx.clone(), repo.clone(), rev.to_string())
.compat()
.await?;
let hg_cs_id = repo
.get_hg_from_bonsai_changeset(ctx.clone(), bcs_id)
.compat()
.await?;
let hg_cs = hg_cs_id.load(ctx.clone(), repo.blobstore()).await?;
let ret = hg_cs
.manifestid()
.find_entry(ctx.clone(), repo.get_blobstore(), Some(mpath))
.compat()
.await?
.ok_or_else(|| format_err!("Path does not exist: {}", path))?;
Ok(ret)
}