diff --git a/Cargo.toml b/Cargo.toml index 2349d6f..16f7f10 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,5 +9,5 @@ repository = "https://github.com/blocklessnetwork/car-utils" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -clap = "4.1.8" +clap = { version = "4.3.19", features = ["derive"] } blockless-car = "0.1.5" diff --git a/README.md b/README.md index ee18d10..1cc4982 100644 --- a/README.md +++ b/README.md @@ -7,86 +7,108 @@ if you wanna lean about WASM runtime, please vist "https://github.com/blocklessn ## How to intsall. use cargo install to install the command + ``` cargo install car-utils ``` + car-utils install in the cargo bin directory. ## How to use. -execute the command `car-utils -help` to show the command help. + +execute the command `car-utils --help` to show the command help. + ``` -Usage: car-utils [COMMAND] +Usage: car-utils Commands: - ar archive local file system to a car file. - ls list the car files - ex extract the car files + ar Archive local file system to a car file + cat View cid content from a car file + ls List the car files + cid List the car cid + ex Extract the car files help Print this message or the help of the given subcommand(s) Options: - -h, --help Print help + -h, --help Print help + -V, --version Print version ``` ### ar subcommand + archive the local directory to the car file. + ``` -archive local file system to a car file. +Archive local file system to a car file -Usage: car-utils ar -c -s +Usage: car-utils ar -c -s Options: - -c the car file for archive - -s the source directory to archived + -c the car file for archive. + -s the source directory to be archived. -h, --help Print help ``` ### ls subcommand + list file structures in the car file. + ``` -list the car files +List the car files -Usage: car-utils ls +Usage: car-utils ls Arguments: - the car file for list. + the car file for list. Options: -h, --help Print help ``` #### cid subcommand + list file cids in the car file. + ``` -list the car cid +List the car cid -Usage: car-utils cid +Usage: car-utils cid Arguments: - the car file for list. + the car file for list. Options: -h, --help Print help ``` + ### ex subcommand + extract the files in the car file to the target directory. + ``` -Usage: car-utils ex [OPTIONS] -c +Extract the car files + +Usage: car-utils ex [OPTIONS] -c Options: - -c the car file for extract - -t the target directory to extract + -c The car file to extract + -t Target directory to extract to -h, --help Print help ``` #### cat subcommand + cat cid content from a car file. + ``` -Usage: car-utils cat -c +View cid content from a car file + +Usage: car-utils cat -c Arguments: - the car file for cat. + the car file to cat. Options: - -c the cid of content for cat. + -c the cid of content to cat. -h, --help Print help ``` diff --git a/src/archive.rs b/src/archive.rs index f2bcf26..aa9f832 100644 --- a/src/archive.rs +++ b/src/archive.rs @@ -1,17 +1,23 @@ +use crate::error::UtilError; use blockless_car::utils::archive_local; use std::path::Path; -use crate::error::UtilError; +#[derive(Debug, clap::Parser)] +pub struct ArchiveCommand { + #[clap(short, help = "the car file for archive.")] + car: String, + + #[clap(short, help = "the source directory to be archived.")] + source: String, +} -/// archive the local file system to car file -/// `target` is the car file -/// `source` is the directory where the archive is prepared. -pub(crate) fn archive_local_fs( - target: impl AsRef, - source: impl AsRef, -) -> Result<(), UtilError> { - let target = target.as_ref(); - let file = std::fs::File::create(target).unwrap(); - archive_local(source, file)?; - Ok(()) +impl ArchiveCommand { + /// archive the local file system to car file + /// `target` is the car file + /// `source` is the directory where the archive is prepared. + pub(crate) fn execute(&self) -> Result<(), UtilError> { + let file = std::fs::File::create(self.car.as_ref() as &Path).unwrap(); // todo handle error + archive_local(self.source.as_ref() as &Path, file)?; + Ok(()) + } } diff --git a/src/cat.rs b/src/cat.rs index 9f34a18..19cf356 100644 --- a/src/cat.rs +++ b/src/cat.rs @@ -1,18 +1,28 @@ -use std::{path::Path, fs::File}; - use crate::error::UtilError; use blockless_car::reader as car_reader; +use std::{fs::File, path::Path}; + +#[derive(Debug, clap::Parser)] +pub struct CatCommand { + #[clap(help = "the car file to cat.")] + car: String, + + #[clap(short, help = "the cid of content to cat.")] + cid: String, +} -pub(crate) fn cat_content(path: impl AsRef, cid: &str) -> Result<(), UtilError> { - let path = path.as_ref(); - if !path.exists() { - return Err(UtilError::new(format!( - "car file [{}] is not exist.", - path.to_str().unwrap() - ))); +impl CatCommand { + pub(crate) fn execute(&self) -> Result<(), UtilError> { + let path: &Path = self.car.as_ref(); + if !path.exists() { + return Err(UtilError::new(format!( + "the car file [{}] does not exist.", + self.car + ))); + } + let file = File::open(path)?; + let mut reader = car_reader::new_v1(file)?; + blockless_car::utils::cat_ipld_str(&mut reader, &self.cid)?; + Ok(()) } - let file = File::open(path)?; - let mut reader = car_reader::new_v1(file)?; - blockless_car::utils::cat_ipld_str(&mut reader, cid)?; - Ok(()) -} \ No newline at end of file +} diff --git a/src/error.rs b/src/error.rs index 36b0339..580b0ab 100644 --- a/src/error.rs +++ b/src/error.rs @@ -2,6 +2,7 @@ use std::{fmt::Display, process::ExitCode}; use blockless_car::error::CarError; +#[derive(Debug)] pub(crate) struct UtilError { pub(crate) err: String, pub(crate) code: u8, diff --git a/src/extract.rs b/src/extract.rs index cde3f96..7279f46 100644 --- a/src/extract.rs +++ b/src/extract.rs @@ -4,22 +4,34 @@ use crate::error::UtilError; use blockless_car::reader::{self as car_reader, CarReader}; use blockless_car::utils::extract_ipld; -/// extract car file to local file system. -/// `car` the car file for extract. -/// `target` target directory to extract. -pub(crate) fn extract_car(car: impl AsRef, target: Option<&String>) -> Result<(), UtilError> { - let path = car.as_ref(); - if !path.exists() { - return Err(UtilError::new(format!( - "car file [{}] is not exist.", - path.to_str().unwrap() - ))); - } - let file = File::open(path)?; - let mut reader = car_reader::new_v1(file)?; - let roots = reader.header().roots(); - for cid in roots { - extract_ipld(&mut reader, cid, target)?; +#[derive(Debug, clap::Parser)] +pub struct ExCommand { + #[clap(short, help = "The car file to extract")] + car: String, + + #[clap(short, help = "Target directory to extract to")] + target: Option, +} + +impl ExCommand { + /// extract car file to local file system. + /// `car` the car file to extract. + /// `target` target directory to extract. + pub(crate) fn execute(&self) -> Result<(), UtilError> { + let path: &Path = self.car.as_ref(); + if !path.exists() { + return Err(UtilError::new(format!( + "car file [{}] is not exist.", + path.to_str().unwrap() + ))); + } + let file = File::open(path)?; + let mut reader = car_reader::new_v1(file)?; + let roots = reader.header().roots(); + for cid in roots { + let target: Option<&Path> = self.target.as_ref().map(|s| s.as_ref()); + extract_ipld(&mut reader, cid, target)?; + } + Ok(()) } - Ok(()) } diff --git a/src/ls.rs b/src/ls.rs index 9f1c20d..830eaac 100644 --- a/src/ls.rs +++ b/src/ls.rs @@ -5,22 +5,31 @@ use std::path::Path; use crate::error::UtilError; -/// list files from car file. -/// `path` is the car file path. -pub(crate) fn list_car_file(path: impl AsRef, is_cid: bool) -> Result<(), UtilError> { - let path = path.as_ref(); - if !path.exists() { - return Err(UtilError::new(format!( - "car file [{}] is not exist.", - path.to_str().unwrap() - ))); - } - let file = File::open(path)?; - let mut reader = car_reader::new_v1(file)?; - if is_cid { - utils::list_cid(&mut reader)?; - } else { - utils::list(&mut reader)?; +#[derive(Debug, clap::Parser)] +pub struct LsCommand { + #[clap(help = "the car file for list.")] + car: String, +} + +impl LsCommand { + /// list files from car file. + /// `path` is the car file path. + pub(crate) fn execute(&self, is_cid: bool) -> Result<(), UtilError> { + // Ok(list_car_file(&self.car, is_cid)?) + let path: &Path = self.car.as_ref(); + if !path.exists() { + return Err(UtilError::new(format!( + "car file [{}] is not exist.", + path.to_str().unwrap() + ))); + } + let file = File::open(path)?; + let mut reader = car_reader::new_v1(file)?; + if is_cid { + utils::list_cid(&mut reader)?; + } else { + utils::list(&mut reader)?; + } + Ok(()) } - Ok(()) } diff --git a/src/main.rs b/src/main.rs index 543a0c0..d5fad2d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,117 +1,63 @@ -use std::process::ExitCode; - -use clap::{Arg, ArgMatches, Command}; - mod archive; +mod cat; mod error; mod extract; mod ls; -mod cat; -use archive::archive_local_fs; -use extract::extract_car; +use clap::{Parser, Subcommand}; + +/// The short version information for car-utils. +/// +/// - The latest version from Cargo.toml +/// +/// # Example +/// +/// ```text +/// v0.1.5 +/// ``` +pub(crate) const SHORT_VERSION: &str = concat!("v", env!("CARGO_PKG_VERSION")); + +#[derive(Debug, Parser)] +#[command(author, version = SHORT_VERSION, long_version = SHORT_VERSION, about = "car-utils", long_about = None)] +struct Cli { + /// The command to run + #[clap(subcommand)] + command: Commands, +} + +/// Commands to be executed +#[derive(Debug, Subcommand)] +pub enum Commands { + /// Archive local file system to a car file + #[command(name = "ar")] + Archive(archive::ArchiveCommand), + + /// View cid content from a car file + #[command(name = "cat")] + Cat(cat::CatCommand), + + /// List the car files + #[command(name = "ls")] + Ls(ls::LsCommand), + + /// List the car cid + #[command(name = "cid")] + Cid(ls::LsCommand), -fn clap_matches() -> ArgMatches { - Command::default() - .version("v0.1.5") - .arg_required_else_help(true) - .arg( - Arg::new("version") - .long("version") - .help("show version.") - ) - .subcommand( - Command::new("ar") - .about("archive local file system to a car file") - .arg(Arg::new("car") - .short('c') - .required(true) - .help("the car file for archive.") - ) - .arg(Arg::new("source") - .short('s') - .required(true) - .help("the source directory to archived") - ) - ) - .subcommand( - Command::new("cat") - .about("cat cid content from a car file") - .arg(Arg::new("car") - .required(true) - .help("the car file for cat.") - ) - .arg(Arg::new("cid") - .short('c') - .required(true) - .help("the cid of content for cat.") - ) - ) - .subcommand( - Command::new("ls") - .about("list the car files") - .arg(Arg::new("car") - .required(true) - .help("the car file for list.") - ) - ) - .subcommand( - Command::new("cid") - .about("list the car cid") - .arg(Arg::new("car") - .required(true) - .help("the car file for list.") - ) - ) - .subcommand( - Command::new("ex") - .about("extract the car files") - .arg(Arg::new("car") - .short('c') - .required(true) - .help("the car file for extract") - ) - .arg(Arg::new("target") - .short('t') - .required(false) - .help("the target directory to extract") - ) - ) - .get_matches() + /// Extract the car files + #[command(name = "ex")] + Ex(extract::ExCommand), } -fn main() -> ExitCode { - let command = clap_matches(); - let result = match command.subcommand() { - Some(("ar", subcommad)) => { - let car = subcommad.get_one::("car").unwrap(); - let source = subcommad.get_one::("source").unwrap(); - archive_local_fs(car, source) - } - Some(("ls", subcommad)) => { - let car = subcommad.get_one::("car").unwrap(); - ls::list_car_file(car, false) - } - Some(("cid", subcommad)) => { - let car = subcommad.get_one::("car").unwrap(); - ls::list_car_file(car, true) - } - Some(("cat", subcommad)) => { - let car = subcommad.get_one::("car").unwrap(); - let cid = subcommad.get_one::("cid").unwrap(); - cat::cat_content(car, cid) - } - Some(("ex", subcommad)) => { - let car = subcommad.get_one::("car").unwrap(); - let target = subcommad.get_one::("target"); - extract_car(car, target) - } - _ => unreachable!("should not be reached."), - }; - match result { - Ok(_) => ExitCode::SUCCESS, - Err(e) => { - eprintln!("{}", e.err); - e.into() - } +fn main() { + let opt = Cli::parse(); + if let Err(err) = match opt.command { + Commands::Archive(command) => command.execute(), + Commands::Cat(command) => command.execute(), + Commands::Ls(command) => command.execute(false), + Commands::Cid(command) => command.execute(true), + Commands::Ex(command) => command.execute(), + } { + eprintln!("Error: {err:?}"); + std::process::exit(1); } }