From d9f21f3cab5bc284f4337042ec64300826cf2c03 Mon Sep 17 00:00:00 2001 From: Jun Wu Date: Wed, 13 Oct 2021 15:20:02 -0700 Subject: [PATCH] edenapi: remove cli Summary: The cli is not used. It is mainly to format requests from JSON and send them. But JSON has its own issues - no binary data support and `hg dbsh`, `hg debugapi` provides debugging too. So let's remove the unused CLI tool. Reviewed By: yancouto Differential Revision: D31465822 fbshipit-source-id: 71574b93a8503643cc503323d6a01f2a87bc41f3 --- eden/scm/lib/edenapi/Cargo.toml | 11 +- eden/scm/lib/edenapi/src/bin/cli.rs | 291 ---------------------------- eden/scm/lib/edenapi/src/builder.rs | 3 +- 3 files changed, 2 insertions(+), 303 deletions(-) delete mode 100644 eden/scm/lib/edenapi/src/bin/cli.rs diff --git a/eden/scm/lib/edenapi/Cargo.toml b/eden/scm/lib/edenapi/Cargo.toml index 40d238cbcf..d9b7c12f09 100644 --- a/eden/scm/lib/edenapi/Cargo.toml +++ b/eden/scm/lib/edenapi/Cargo.toml @@ -1,32 +1,23 @@ -# @generated by autocargo from //eden/scm/lib/edenapi:[edenapi,edenapi_cli] +# @generated by autocargo from //eden/scm/lib/edenapi:edenapi [package] name = "edenapi" version = "0.1.0" edition = "2018" -[[bin]] -name = "edenapi_cli" -path = "src/bin/cli.rs" - [dependencies] anyhow = "1.0" async-runtime = { path = "../async-runtime" } async-trait = "0.1.51" -atty = "0.2" auth = { path = "../auth" } bytes = { version = "1.0", features = ["serde"] } chrono = { version = "0.4", features = ["clock", "serde", "std"], default-features = false } configmodel = { path = "../configmodel" } -configparser = { path = "../configparser" } -dirs = "2.0" edenapi_trait = { path = "trait" } edenapi_types = { path = "types" } -env_logger = "0.7" futures = { version = "0.3.13", features = ["async-await", "compat"] } hg-http = { path = "../hg-http" } http-client = { path = "../http-client" } itertools = "0.10.1" -log = { version = "0.4.8", features = ["kv_unstable"] } metrics = { path = "../metrics" } minibytes = { path = "../minibytes" } once_cell = "1.4" diff --git a/eden/scm/lib/edenapi/src/bin/cli.rs b/eden/scm/lib/edenapi/src/bin/cli.rs deleted file mode 100644 index e7d241854f..0000000000 --- a/eden/scm/lib/edenapi/src/bin/cli.rs +++ /dev/null @@ -1,291 +0,0 @@ -/* - * 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 std::fmt::Debug; -use std::io::stdin; -use std::path::PathBuf; -use std::sync::Arc; - -use anyhow::{Context, Result}; -use env_logger::Env; -use futures::prelude::*; -use serde::Serialize; -use serde_json::Deserializer; -use structopt::StructOpt; -use tokio::io; -use tokio::io::AsyncWriteExt; - -use configparser::config::{ConfigSet, Options}; -use edenapi::{Builder, EdenApi, Entries, Response}; -use edenapi_types::{ - json::FromJson, wire::ToWire, BookmarkRequest, CommitRevlogDataRequest, FileRequest, - HistoryRequest, TreeRequest, -}; - -const DEFAULT_CONFIG_FILE: &str = ".hgrc.edenapi"; - -#[derive(Debug, StructOpt)] -#[structopt(name = "edenapi_cli", about = "Query the EdenAPI server")] -enum Command { - #[structopt(about = "Check whether server is reachable")] - Health(NoRepoArgs), - #[structopt(about = "Request files")] - Files(Args), - #[structopt(about = "Request file history")] - History(Args), - #[structopt(about = "Request individual tree nodes")] - Trees(Args), - #[structopt(about = "Request commit revlog data")] - CommitRevlogData(Args), - #[structopt(about = "Request Bookmarks")] - Bookmarks(Args), -} - -#[derive(Debug, StructOpt)] -struct NoRepoArgs { - #[structopt(long, short, help = "hgrc file to use (default: ~/.hgrc.edenapi)")] - config: Option, -} - -#[derive(Debug, StructOpt)] -struct Args { - repo: String, - #[structopt(long, short, help = "hgrc file to use (default: ~/.hgrc.edenapi)")] - config: Option, -} - -struct Setup { - repo: String, - client: Arc, - requests: Vec, -} - -impl Setup { - /// Common set up for all subcommands. - fn from_args(args: Args) -> Result { - Ok(Self { - repo: args.repo, - client: init_client(args.config)?, - requests: read_requests()?, - }) - } -} - -#[tokio::main] -async fn main() -> Result<()> { - env_logger::from_env(Env::default().default_filter_or("info")).init(); - match Command::from_args() { - Command::Health(args) => cmd_health(args).await, - Command::Files(args) => cmd_files(args).await, - Command::History(args) => cmd_history(args).await, - Command::Trees(args) => cmd_trees(args).await, - Command::CommitRevlogData(args) => cmd_commit_revlog_data(args).await, - Command::Bookmarks(args) => cmd_bookmarks(args).await, - } -} - -async fn cmd_health(args: NoRepoArgs) -> Result<()> { - let client = init_client(args.config)?; - let meta = client.health().await?; - log::info!("Received response from EdenAPI server:"); - println!("{:?}", &meta); - Ok(()) -} - -async fn cmd_files(args: Args) -> Result<()> { - let Setup { - repo, - client, - requests, - } = >::from_args(args)?; - - for req in requests { - log::info!("Requesting content for {} files", req.keys.len(),); - - let response = client.files(repo.clone(), req.keys).await?; - handle_response(response).await?; - } - - Ok(()) -} - -async fn cmd_bookmarks(args: Args) -> Result<()> { - let Setup { - repo, - client, - requests, - } = >::from_args(args)?; - for req in requests { - log::info!("Requesting values for {} bookmarks", req.bookmarks.len(),); - - let response = client.bookmarks(repo.clone(), req.bookmarks).await?; - handle_vec(response).await?; - } - - Ok(()) -} - -async fn cmd_history(args: Args) -> Result<()> { - let Setup { - repo, - client, - requests, - } = >::from_args(args)?; - - for req in requests { - log::info!("Requesting history for {} files", req.keys.len(),); - - let res = client.history(repo.clone(), req.keys, req.length).await?; - handle_response_raw(res).await?; - } - - Ok(()) -} - -async fn cmd_trees(args: Args) -> Result<()> { - let Setup { - repo, - client, - requests, - } = >::from_args(args)?; - - for req in requests { - log::info!("Requesting {} tree nodes", req.keys.len()); - log::trace!("{:?}", &req); - - let res = client.trees(repo.clone(), req.keys, None).await?; - handle_response(res).await?; - } - - Ok(()) -} - -async fn cmd_commit_revlog_data(args: Args) -> Result<()> { - let Setup { - repo, - client, - requests, - } = >::from_args(args)?; - - for req in requests { - log::info!("Requesting revlog data for {} commits", req.hgids.len()); - - let res = client.commit_revlog_data(repo.clone(), req.hgids).await?; - handle_response_raw(res).await?; - } - - Ok(()) -} - -/// Handle the incoming deserialized response by reserializing it -/// and dumping it to stdout (only if stdout isn't a TTY, to avoid -/// messing up the user's terminal). -async fn handle_response(res: Response) -> Result<()> { - let buf = serialize_and_concat(res.entries).await?; - let stats = res.stats.await?; - log::info!("{}", &stats); - - if atty::is(atty::Stream::Stdout) { - log::warn!("Not writing output because stdout is a TTY"); - } else { - log::info!("Writing output to stdout"); - io::stdout().write_all(&buf).await?; - } - - Ok(()) -} - -async fn handle_vec(res: Vec) -> Result<()> { - let buf = serialize_and_concat_vec(res).await?; - - if atty::is(atty::Stream::Stdout) { - log::warn!("Not writing output because stdout is a TTY"); - } else { - log::info!("Writing output to stdout"); - io::stdout().write_all(&buf).await?; - } - - Ok(()) -} - -// TODO(meyer): Remove when all types have wire type -async fn handle_response_raw(res: Response) -> Result<()> { - let buf = serialize_and_concat_raw(res.entries).await?; - let stats = res.stats.await?; - log::info!("{}", &stats); - - if atty::is(atty::Stream::Stdout) { - log::warn!("Not writing output because stdout is a TTY"); - } else { - log::info!("Writing output to stdout"); - io::stdout().write_all(&buf).await?; - } - - Ok(()) -} - -/// CBOR serialize and concatenate all items in the incoming stream. -/// -/// Normally, this wouldn't be a good idea since the EdenAPI client just -/// deserialized the entries, so immediately re-serializing them is wasteful. -/// However, in this case we're explicitly trying to exercise the public API -/// of the client, including deserialization. In practice, most users will -/// never want the raw (CBOR-encoded) entries. -async fn serialize_and_concat(entries: Entries) -> Result> { - entries - .err_into() - .and_then(|entry| async move { Ok(serde_cbor::to_vec(&entry.to_wire())?) }) - .try_concat() - .await -} - -async fn serialize_and_concat_vec(entries: Vec) -> Result> { - let serialized = entries - .into_iter() - .map(|entry| serde_cbor::to_vec(&entry.to_wire())) - .collect::, _>>()?; - Ok(serialized.concat()) -} - -// TODO: Remove when all types have wire type -async fn serialize_and_concat_raw(entries: Entries) -> Result> { - entries - .err_into() - .and_then(|entry| async move { Ok(serde_cbor::to_vec(&entry)?) }) - .try_concat() - .await -} - -fn init_client(config_path: Option) -> Result> { - let config = load_config(config_path)?; - Ok(Builder::from_config(&config)?.build()?) -} - -fn load_config(path: Option) -> Result { - let path = path - .or_else(|| Some(dirs::home_dir()?.join(DEFAULT_CONFIG_FILE))) - .context("Failed to get config file path")?; - - log::debug!("Loading config from: {:?}", &path); - let mut config = ConfigSet::new(); - let mut errors = config.load_path(path, &Options::new()); - - if errors.is_empty() { - Ok(config) - } else { - // Just return the last error for simplicity. - Err(errors.pop().unwrap().into()) - } -} - -fn read_requests() -> Result> { - log::info!("Reading requests as JSON from stdin..."); - Deserializer::from_reader(stdin()) - .into_iter() - .map(|json| Ok(R::from_json(&json?)?)) - .collect() -} diff --git a/eden/scm/lib/edenapi/src/builder.rs b/eden/scm/lib/edenapi/src/builder.rs index f8bc0b448e..d0ac7e30b3 100644 --- a/eden/scm/lib/edenapi/src/builder.rs +++ b/eden/scm/lib/edenapi/src/builder.rs @@ -368,8 +368,7 @@ impl HttpClientBuilder { /// If specified, the client will write a JSON version of every request /// it sends to the specified directory. This is primarily useful for - /// debugging. The JSON requests can be sent with the `edenapi_cli`, or - /// converted to CBOR with the `make_req` tool and sent with `curl`. + /// debugging. pub fn log_dir(mut self, dir: impl AsRef) -> Self { self.log_dir = Some(dir.as_ref().into()); self