sapling/cmds/lfs_import.rs
Mark Thomas bd94758eb1 cmdlib: make MononokeApp interface more fluent
Summary:
The `MononokeApp` interface is almost like a builder.  Make it a proper builder
with a fluent API.  This will allow us to add new options in the future without
changing all the places it is called.

As an example of why this is useful, we also clean up the options for selecting
a repo.  The default behaviour is to add `--repo-id` and `--repo-name` options
that are optional.  This is not usually what programs want, so they can now
specify `.with_repo_required()` to make one of these options mandatory, or
`.with_all_repos()` to remove the options entirely for commands that operate
on all repos.

The LFS server is updates to specify `.with_all_repos()`.  We will update the
remaining existing commands separately as needed.

Reviewed By: krallin

Differential Revision: D18009138

fbshipit-source-id: 5ce46ee2418c92c3697138cccb2b3e2807e46faa
2019-10-28 08:00:06 -07:00

90 lines
2.7 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 found in the LICENSE file in the root
* directory of this source tree.
*/
use bytes::Bytes;
use clap::Arg;
use cmdlib::args;
use context::CoreContext;
use failure_ext::{Error, Result};
use fbinit::FacebookInit;
use futures::{stream, Future, IntoFuture, Stream};
use lfs_import_lib::lfs_upload;
use mercurial_types::blobs::File;
const NAME: &str = "lfs_import";
const ARG_LFS_HELPER: &str = "lfs-helper";
const ARG_CONCURRENCY: &str = "concurrency";
const ARG_POINTERS: &str = "pointers";
const DEFAULT_CONCURRENCY: usize = 16;
#[fbinit::main]
fn main(fb: FacebookInit) -> Result<()> {
let app = args::MononokeApp::new(NAME)
.with_advanced_args_hidden()
.build()
.version("0.0.0")
.about("Import LFS blobs")
.arg(
Arg::with_name(ARG_CONCURRENCY)
.long("concurrency")
.takes_value(true)
.help("The number of OIDs to process in parallel"),
)
.arg(
Arg::with_name(ARG_LFS_HELPER)
.required(true)
.takes_value(true)
.help("LFS Helper"),
)
.arg(
Arg::with_name(ARG_POINTERS)
.takes_value(true)
.required(true)
.min_values(1)
.help("Raw LFS pointers to be imported"),
);
let matches = app.get_matches();
args::init_cachelib(fb, &matches);
let logger = args::init_logging(fb, &matches);
let ctx = CoreContext::new_with_logger(fb, logger.clone());
let blobrepo = args::open_repo(fb, &logger, &matches);
let lfs_helper = matches.value_of(ARG_LFS_HELPER).unwrap().to_string();
let concurrency: usize = matches
.value_of(ARG_CONCURRENCY)
.map_or(Ok(DEFAULT_CONCURRENCY), |j| j.parse())
.map_err(Error::from)?;
let entries: Result<Vec<_>> = matches
.values_of(ARG_POINTERS)
.unwrap()
.into_iter()
.map(|e| File::new(Bytes::from(e), None, None).get_lfs_content())
.collect();
let import = (blobrepo, entries)
.into_future()
.and_then(move |(blobrepo, entries)| {
stream::iter_ok(entries)
.map({
move |lfs| lfs_upload(ctx.clone(), blobrepo.clone(), lfs_helper.clone(), lfs)
})
.buffered(concurrency)
.for_each(|_| Ok(()))
});
let mut runtime = tokio::runtime::Runtime::new()?;
let result = runtime.block_on(import);
runtime.shutdown_on_idle();
result
}