Skip to content

Commit

Permalink
refactor(bin/oli): use clap_derive to reduce boilerplate code (#5233)
Browse files Browse the repository at this point in the history
* refactor(bin/oli): use `clap_derive` to reduce boilerplate code

* support proxy manner

* fix proxy manner

* add ConfigParams to reduce boilerplate
  • Loading branch information
koushiro authored Oct 23, 2024
1 parent 6f8fff0 commit c230c8a
Show file tree
Hide file tree
Showing 14 changed files with 285 additions and 313 deletions.
15 changes: 14 additions & 1 deletion bin/oli/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion bin/oli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ services-sled = ["opendal/services-sled"]

[dependencies]
anyhow = "1"
clap = { version = "4", features = ["cargo", "string"] }
clap = { version = "4", features = ["cargo", "string", "derive", "deprecated"] }
dirs = "5.0.1"
futures = "0.3"
opendal = { version = "0.50.0", path = "../../core", features = [
Expand Down
51 changes: 18 additions & 33 deletions bin/oli/src/bin/oli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,28 +28,13 @@ use std::path::PathBuf;

use anyhow::anyhow;
use anyhow::Result;
use clap::value_parser;
use clap::Arg;
use clap::Command;
use dirs::config_dir;
use oli::commands::OliSubcommand;

fn new_cmd(name: &'static str) -> Result<Command> {
let d = config_dir().ok_or_else(|| anyhow!("unknown config dir"))?;
let default_config_path = d.join("oli/config.toml").as_os_str().to_owned();

Ok(Command::new(name)
.version(env!("CARGO_PKG_VERSION"))
.arg(
Arg::new("config")
.long("config")
.help("Path to the config file")
.global(true)
.default_value(default_config_path)
.value_parser(value_parser!(PathBuf))
.required(false),
)
.subcommand_required(true)
.arg_required_else_help(true))
#[derive(Debug, clap::Parser)]
#[command(about, version)]
pub struct Oli {
#[command(subcommand)]
subcommand: OliSubcommand,
}

#[tokio::main]
Expand All @@ -66,28 +51,28 @@ async fn main() -> Result<()> {
.and_then(OsStr::to_str)
{
Some("oli") => {
let cmd = oli::commands::cli::cli(new_cmd("oli")?);
oli::commands::cli::main(&cmd.get_matches()).await?;
let cmd: Oli = clap::Parser::parse();
cmd.subcommand.run().await?;
}
Some("ocat") => {
let cmd = oli::commands::cat::cli(new_cmd("ocat")?);
oli::commands::cat::main(&cmd.get_matches()).await?;
let cmd: oli::commands::cat::CatCmd = clap::Parser::parse();
cmd.run().await?;
}
Some("ocp") => {
let cmd = oli::commands::cp::cli(new_cmd("ocp")?);
oli::commands::cp::main(&cmd.get_matches()).await?;
let cmd: oli::commands::cp::CopyCmd = clap::Parser::parse();
cmd.run().await?;
}
Some("ols") => {
let cmd = oli::commands::ls::cli(new_cmd("ols")?);
oli::commands::ls::main(&cmd.get_matches()).await?;
let cmd: oli::commands::ls::LsCmd = clap::Parser::parse();
cmd.run().await?;
}
Some("orm") => {
let cmd = oli::commands::rm::cli(new_cmd("orm")?);
oli::commands::rm::main(&cmd.get_matches()).await?;
let cmd: oli::commands::rm::RmCmd = clap::Parser::parse();
cmd.run().await?;
}
Some("ostat") => {
let cmd = oli::commands::stat::cli(new_cmd("ostat")?);
oli::commands::stat::main(&cmd.get_matches()).await?;
let cmd: oli::commands::stat::StatCmd = clap::Parser::parse();
cmd.run().await?;
}
Some(v) => {
println!("{v} is not supported")
Expand Down
53 changes: 26 additions & 27 deletions bin/oli/src/commands/cat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,39 +15,38 @@
// specific language governing permissions and limitations
// under the License.

use std::path::PathBuf;

use anyhow::anyhow;
use anyhow::Result;
use clap::Arg;
use clap::ArgMatches;
use clap::Command;
use futures::io;

use crate::config::Config;
use crate::params::config::ConfigParams;

pub async fn main(args: &ArgMatches) -> Result<()> {
let config_path = args
.get_one::<PathBuf>("config")
.ok_or_else(|| anyhow!("missing config path"))?;
let cfg = Config::load(config_path)?;
#[derive(Debug, clap::Parser)]
#[command(
name = "cat",
about = "Display object content",
disable_version_flag = true
)]
pub struct CatCmd {
#[command(flatten)]
pub config_params: ConfigParams,
#[arg()]
pub target: String,
}

let target = args
.get_one::<String>("target")
.ok_or_else(|| anyhow!("missing target"))?;
let (op, path) = cfg.parse_location(target)?;
impl CatCmd {
pub async fn run(&self) -> Result<()> {
let cfg = Config::load(&self.config_params.config)?;

let reader = op.reader(&path).await?;
let meta = op.stat(&path).await?;
let mut buf_reader = reader
.into_futures_async_read(0..meta.content_length())
.await?;
let mut stdout = io::AllowStdIo::new(std::io::stdout());
io::copy_buf(&mut buf_reader, &mut stdout).await?;
Ok(())
}
let (op, path) = cfg.parse_location(&self.target)?;

pub fn cli(cmd: Command) -> Command {
cmd.about("display object content")
.arg(Arg::new("target").required(true))
let reader = op.reader(&path).await?;
let meta = op.stat(&path).await?;
let mut buf_reader = reader
.into_futures_async_read(0..meta.content_length())
.await?;
let mut stdout = io::AllowStdIo::new(std::io::stdout());
io::copy_buf(&mut buf_reader, &mut stdout).await?;
Ok(())
}
}
47 changes: 0 additions & 47 deletions bin/oli/src/commands/cli.rs

This file was deleted.

129 changes: 58 additions & 71 deletions bin/oli/src/commands/cp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,90 +16,77 @@
// under the License.

use std::path::Path;
use std::path::PathBuf;

use anyhow::anyhow;
use anyhow::Result;
use clap::Arg;
use clap::ArgAction;
use clap::ArgMatches;
use clap::Command;
use futures::AsyncWriteExt;
use futures::TryStreamExt;

use crate::config::Config;
use crate::params::config::ConfigParams;

pub async fn main(args: &ArgMatches) -> Result<()> {
let config_path = args
.get_one::<PathBuf>("config")
.ok_or_else(|| anyhow!("missing config path"))?;
let cfg = Config::load(config_path)?;
let recursive = args.get_flag("recursive");
#[derive(Debug, clap::Parser)]
#[command(name = "cp", about = "Copy object", disable_version_flag = true)]
pub struct CopyCmd {
#[command(flatten)]
pub config_params: ConfigParams,
#[arg()]
pub source: String,
#[arg()]
pub destination: String,
/// Copy objects recursively.
#[arg(short = 'r', long)]
pub recursive: bool,
}

let src = args
.get_one::<String>("source")
.ok_or_else(|| anyhow!("missing source"))?;
let (src_op, src_path) = cfg.parse_location(src)?;
impl CopyCmd {
pub async fn run(&self) -> Result<()> {
let cfg = Config::load(&self.config_params.config)?;

let dst = args
.get_one::<String>("destination")
.ok_or_else(|| anyhow!("missing target"))?;
let (dst_op, dst_path) = cfg.parse_location(dst)?;
let (src_op, src_path) = cfg.parse_location(&self.source)?;

if !recursive {
let mut dst_w = dst_op.writer(&dst_path).await?.into_futures_async_write();
let src_meta = src_op.stat(&src_path).await?;
let reader = src_op.reader_with(&src_path).chunk(8 * 1024 * 1024).await?;
let buf_reader = reader
.into_futures_async_read(0..src_meta.content_length())
.await?;
futures::io::copy_buf(buf_reader, &mut dst_w).await?;
// flush data
dst_w.close().await?;
return Ok(());
}
let (dst_op, dst_path) = cfg.parse_location(&self.destination)?;

let dst_root = Path::new(&dst_path);
let mut ds = src_op.lister_with(&src_path).recursive(true).await?;
let prefix = src_path.strip_prefix('/').unwrap_or(src_path.as_str());
while let Some(de) = ds.try_next().await? {
let meta = de.metadata();
if meta.mode().is_dir() {
continue;
if !self.recursive {
let mut dst_w = dst_op.writer(&dst_path).await?.into_futures_async_write();
let src_meta = src_op.stat(&src_path).await?;
let reader = src_op.reader_with(&src_path).chunk(8 * 1024 * 1024).await?;
let buf_reader = reader
.into_futures_async_read(0..src_meta.content_length())
.await?;
futures::io::copy_buf(buf_reader, &mut dst_w).await?;
// flush data
dst_w.close().await?;
return Ok(());
}
let depath = de.path();
let fp = depath
.strip_prefix('/')
.unwrap_or(depath)
.strip_prefix(prefix)
.expect("invalid path");
let reader = src_op.reader_with(de.path()).chunk(8 * 1024 * 1024).await?;
let buf_reader = reader
.into_futures_async_read(0..meta.content_length())
.await?;

let mut writer = dst_op
.writer(&dst_root.join(fp).to_string_lossy())
.await?
.into_futures_async_write();
let dst_root = Path::new(&dst_path);
let mut ds = src_op.lister_with(&src_path).recursive(true).await?;
let prefix = src_path.strip_prefix('/').unwrap_or(src_path.as_str());
while let Some(de) = ds.try_next().await? {
let meta = de.metadata();
if meta.mode().is_dir() {
continue;
}
let depath = de.path();
let fp = depath
.strip_prefix('/')
.unwrap_or(depath)
.strip_prefix(prefix)
.expect("invalid path");
let reader = src_op.reader_with(de.path()).chunk(8 * 1024 * 1024).await?;
let buf_reader = reader
.into_futures_async_read(0..meta.content_length())
.await?;

println!("Copying {}", de.path());
futures::io::copy_buf(buf_reader, &mut writer).await?;
writer.close().await?;
}
Ok(())
}
let mut writer = dst_op
.writer(&dst_root.join(fp).to_string_lossy())
.await?
.into_futures_async_write();

pub fn cli(cmd: Command) -> Command {
cmd.about("copy")
.arg(Arg::new("source").required(true))
.arg(Arg::new("destination").required(true))
.arg(
Arg::new("recursive")
.required(false)
.long("recursive")
.short('r')
.help("Copy files under source recursively to destination")
.action(ArgAction::SetTrue),
)
println!("Copying {}", de.path());
futures::io::copy_buf(buf_reader, &mut writer).await?;
writer.close().await?;
}
Ok(())
}
}
Loading

0 comments on commit c230c8a

Please sign in to comment.