diff --git a/Cargo.lock b/Cargo.lock index 9e423fb..30c7578 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -63,15 +63,6 @@ dependencies = [ "libc", ] -[[package]] -name = "ansi_term" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" -dependencies = [ - "winapi", -] - [[package]] name = "anstream" version = "0.6.14" @@ -355,21 +346,6 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "clap" -version = "2.34.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" -dependencies = [ - "ansi_term", - "atty", - "bitflags 1.3.2", - "strsim 0.8.0", - "textwrap", - "unicode-width 0.1.12", - "vec_map", -] - [[package]] name = "clap" version = "4.5.4" @@ -398,7 +374,7 @@ version = "4.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64" dependencies = [ - "heck 0.5.0", + "heck", "proc-macro2", "quote", "syn 2.0.106", @@ -454,7 +430,7 @@ dependencies = [ "encode_unicode", "libc", "once_cell", - "unicode-width 0.2.1", + "unicode-width", "windows-sys 0.61.1", ] @@ -715,6 +691,51 @@ version = "0.15.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" +[[package]] +name = "dvcli" +version = "0.1.1" +dependencies = [ + "async_zip", + "atty", + "bon", + "bytes", + "chrono", + "clap", + "colored", + "colored_json", + "dialoguer", + "dirs", + "exitcode", + "futures", + "httpmock", + "indicatif", + "infer", + "keyring", + "lazy_static", + "libdbus-sys", + "md5", + "mime_guess", + "rand 0.9.0-alpha.1", + "regress", + "reqwest", + "rpassword", + "serde", + "serde_json", + "serde_yaml", + "sha1", + "sha2", + "sqlx", + "tempfile", + "tokio", + "tokio-stream", + "tokio-util", + "typify", + "url", + "uuid 1.12.0", + "variantly", + "walkdir", +] + [[package]] name = "dyn-clone" version = "1.0.17" @@ -1100,15 +1121,6 @@ dependencies = [ "http", ] -[[package]] -name = "heck" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" -dependencies = [ - "unicode-segmentation", -] - [[package]] name = "heck" version = "0.5.0" @@ -1480,7 +1492,7 @@ checksum = "70a646d946d06bedbbc4cac4c218acf4bbf2d87757a784857025f4d447e4e1cd" dependencies = [ "console", "portable-atomic", - "unicode-width 0.2.1", + "unicode-width", "unit-prefix", "web-time", ] @@ -1990,30 +2002,6 @@ dependencies = [ "syn 2.0.106", ] -[[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn 1.0.109", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote", - "version_check", -] - [[package]] name = "proc-macro2" version = "1.0.101" @@ -2245,52 +2233,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "rust-dataverse" -version = "0.1.1" -dependencies = [ - "async_zip", - "atty", - "bon", - "bytes", - "chrono", - "clap 4.5.4", - "colored", - "colored_json", - "dialoguer", - "dirs", - "exitcode", - "futures", - "httpmock", - "indicatif", - "infer", - "keyring", - "lazy_static", - "libdbus-sys", - "md5", - "mime_guess", - "rand 0.9.0-alpha.1", - "regress", - "reqwest", - "rpassword", - "serde", - "serde_json", - "serde_yaml", - "sha1", - "sha2", - "sqlx", - "structopt", - "tempfile", - "tokio", - "tokio-stream", - "tokio-util", - "typify", - "url", - "uuid 1.12.0", - "variantly", - "walkdir", -] - [[package]] name = "rustc-demangle" version = "0.1.24" @@ -2714,7 +2656,7 @@ checksum = "882ceaa29cade31beca7129b6beeb05737f44f82dbe2a9806ecea5a7093d00b7" dependencies = [ "dotenvy", "either", - "heck 0.5.0", + "heck", "hex", "once_cell", "proc-macro2", @@ -2858,12 +2800,6 @@ dependencies = [ "unicode-properties", ] -[[package]] -name = "strsim" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" - [[package]] name = "strsim" version = "0.10.0" @@ -2876,30 +2812,6 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" -[[package]] -name = "structopt" -version = "0.3.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c6b5c64445ba8094a6ab0c3cd2ad323e07171012d9c98b0b15651daf1787a10" -dependencies = [ - "clap 2.34.0", - "lazy_static", - "structopt-derive", -] - -[[package]] -name = "structopt-derive" -version = "0.4.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0" -dependencies = [ - "heck 0.3.3", - "proc-macro-error", - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "subtle" version = "2.6.1" @@ -2972,7 +2884,7 @@ version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fce91f2f0ec87dff7e6bcbbeb267439aa1188703003c6055193c821487400432" dependencies = [ - "unicode-width 0.2.1", + "unicode-width", ] [[package]] @@ -2988,15 +2900,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "textwrap" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" -dependencies = [ - "unicode-width 0.1.12", -] - [[package]] name = "thiserror" version = "2.0.12" @@ -3194,7 +3097,7 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "062879d46aa4c9dfe0d33b035bbaf512da192131645d05deacb7033ec8581a09" dependencies = [ - "heck 0.5.0", + "heck", "log", "proc-macro2", "quote", @@ -3261,18 +3164,6 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0" -[[package]] -name = "unicode-segmentation" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" - -[[package]] -name = "unicode-width" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68f5e5f3158ecfd4b8ff6fe086db7c8467a2dfdac97fe420f2b7c4aa97af66d6" - [[package]] name = "unicode-width" version = "0.2.1" @@ -3355,12 +3246,6 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" -[[package]] -name = "vec_map" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" - [[package]] name = "version_check" version = "0.9.4" diff --git a/Cargo.toml b/Cargo.toml index a28d067..77b4d2b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "rust-dataverse" +name = "dvcli" version = "0.1.1" edition = "2021" repository = "https://github.com/gdcc/rust-dataverse" @@ -27,7 +27,6 @@ serde_json = "1.0.117" serde_yaml = "0.9.34" typify = "0.4.3" colored_json = "5.0.0" -structopt = "0.3.26" atty = "0.2.14" indicatif = "0.18.0" tokio = { version = "1.37.0", features = ["full"] } diff --git a/src/bin/cli.rs b/src/bin/cli.rs index a8a32e8..9bbb560 100644 --- a/src/bin/cli.rs +++ b/src/bin/cli.rs @@ -1,9 +1,9 @@ use std::error::Error; +use clap::Parser; use colored::Colorize; use dataverse::cli::admin::AdminSubCommand; use dataverse::cli::auth::{prompt_for_credentials, AuthProfile, AuthSubCommand}; -use structopt::StructOpt; use dataverse::cli::base::Matcher; use dataverse::cli::collection::CollectionSubCommand; @@ -13,42 +13,59 @@ use dataverse::cli::info::InfoSubCommand; use dataverse::client::BaseClient; use dataverse::search_api::query::SearchQuery; +fn get_styles() -> clap::builder::Styles { + clap::builder::Styles::styled() + .header(clap::builder::styling::AnsiColor::Green.on_default().bold()) + .usage(clap::builder::styling::AnsiColor::Green.on_default().bold()) + .literal(clap::builder::styling::AnsiColor::Cyan.on_default().bold()) + .placeholder(clap::builder::styling::AnsiColor::Magenta.on_default()) +} + static HEADER: &str = r#" --- Dataverse Command Line Interface (DVCLI) --- "#; -#[derive(StructOpt, Debug)] +#[derive(Parser, Debug)] struct GlobalOpts { /// Profile name to use for configuration - #[structopt(short, long)] + #[arg(short, long)] profile: Option, } -#[derive(StructOpt, Debug)] -#[structopt(about = "CLI to interact with Dataverse")] +#[derive(Parser, Debug)] +#[command( + about = "CLI to interact with Dataverse", + styles = get_styles() +)] #[allow(clippy::upper_case_acronyms)] struct CLI { - #[structopt(flatten)] + #[command(flatten)] global: GlobalOpts, - #[structopt(subcommand)] + #[command(subcommand)] cmd: DVCLI, } -#[derive(StructOpt, Debug)] +#[derive(clap::Subcommand, Debug)] #[allow(clippy::upper_case_acronyms)] enum DVCLI { + #[command(subcommand)] Info(InfoSubCommand), + #[command(subcommand)] Collection(CollectionSubCommand), + #[command(subcommand)] Dataset(DatasetSubCommand), + #[command(subcommand)] File(FileSubCommand), Search(SearchQuery), + #[command(subcommand)] Admin(AdminSubCommand), + #[command(subcommand)] Auth(AuthSubCommand), } fn main() { - let cli = CLI::from_args(); + let cli = CLI::parse(); // This is a special case for the Auth command, which is used to set the profile // and does not require a Dataverse instance. diff --git a/src/cli/admin.rs b/src/cli/admin.rs index de7da8c..670eef1 100644 --- a/src/cli/admin.rs +++ b/src/cli/admin.rs @@ -7,7 +7,7 @@ use std::path::PathBuf; -use structopt::StructOpt; +use clap::Subcommand; use tokio::runtime::Runtime; use crate::client::BaseClient; @@ -17,49 +17,43 @@ use crate::native_api::admin::tools; use super::base::{evaluate_and_print_response, Matcher}; /// Subcommands for administrative tasks in a Dataverse instance -#[derive(StructOpt, Debug)] -#[structopt(about = "Handle admin tasks of the Dataverse instance")] +#[derive(Subcommand, Debug)] pub enum AdminSubCommand { - /// Get a list of available storage drivers for the Dataverse instance - #[structopt(about = "Retrieve the storage drivers available for the Dataverse instance")] + /// Retrieve the storage drivers available for the Dataverse instance StorageDrivers {}, - /// Configure a specific storage driver for a collection - #[structopt(about = "Set the storage driver for a collection")] + /// Set the storage driver for a collection SetStorage { /// The storage driver identifier to assign - #[structopt(short, long, help = "Storage driver to set")] + #[arg(short, long, help = "Storage driver to set")] driver: String, /// Collection alias to configure storage for - #[structopt(help = "Alias of the collection to set the storage driver for")] + #[arg(help = "Alias of the collection to set the storage driver for")] alias: String, }, - /// Retrieve the currently configured storage driver for a collection - #[structopt(about = "Get the storage driver for a collection")] + /// Get the storage driver for a collection GetStorage { /// Collection alias to get storage config from - #[structopt(help = "Alias of the collection to get the storage driver for")] + #[arg(help = "Alias of the collection to get the storage driver for")] alias: String, }, - /// Reset a collection's storage driver to the default - #[structopt(about = "Reset the storage driver for a collection")] + /// Reset the storage driver for a collection ResetStorage { /// Collection alias to reset storage for - #[structopt(help = "Alias of the collection to reset the storage driver for")] + #[arg(help = "Alias of the collection to reset the storage driver for")] alias: String, }, - /// Register an external tool - #[structopt(about = "Registers an external tool with the Dataverse instance")] + /// Registers an external tool with the Dataverse instance AddExternalTool { /// The tool manifest to register - #[structopt(help = "Path to the tool manifest file")] + #[arg(help = "Path to the tool manifest file")] manifest: PathBuf, /// Whether to overwrite an existing tool when it already exists. This will delete the existing tool and register a new one. - #[structopt( + #[arg( short, long, help = "Whether to overwrite an existing tool when it already exists. This will delete the existing tool and register a new one." @@ -67,8 +61,7 @@ pub enum AdminSubCommand { overwrite: bool, }, - /// List all external tools - #[structopt(about = "Lists all external tools registered with the Dataverse instance")] + /// Lists all external tools registered with the Dataverse instance ListExternalTools {}, } diff --git a/src/cli/auth.rs b/src/cli/auth.rs index b382e77..8a21dfa 100644 --- a/src/cli/auth.rs +++ b/src/cli/auth.rs @@ -12,7 +12,7 @@ use colored::Colorize; use dialoguer::Input; use keyring::{Entry, Result}; use rpassword::prompt_password; -use structopt::StructOpt; +use clap::Subcommand; use url::Url; use uuid::Uuid; @@ -75,22 +75,20 @@ pub fn prompt_for_credentials() -> std::result::Result<(String, String), Box, /// URL of the Dataverse server to authenticate against - #[structopt(short, long, help = "URL of the Dataverse server")] + #[arg(short, long, help = "URL of the Dataverse server")] url: Option, /// API token used for authentication with the Dataverse server - #[structopt(short, long, help = "API token for authentication")] + #[arg(short, long, help = "API token for authentication")] token: Option, }, } diff --git a/src/cli/collection.rs b/src/cli/collection.rs index 4c76952..b1995d3 100644 --- a/src/cli/collection.rs +++ b/src/cli/collection.rs @@ -8,7 +8,7 @@ use std::path::PathBuf; -use structopt::StructOpt; +use clap::Subcommand; use tokio::runtime::Runtime; use crate::client::BaseClient; @@ -19,18 +19,16 @@ use crate::native_api::collection::{content, delete}; use super::base::{evaluate_and_print_response, parse_file, Matcher}; /// Subcommands for managing collections in a Dataverse instance -#[derive(StructOpt, Debug)] -#[structopt(about = "Handle collections of a Dataverse instance")] +#[derive(Subcommand, Debug)] pub enum CollectionSubCommand { - /// Create a new collection in a parent dataverse - #[structopt(about = "Create a collection")] + /// Create a collection Create { /// Alias of the parent dataverse where the collection will be created - #[structopt(long, short, help = "Alias of the parent dataverse")] + #[arg(long, short, help = "Alias of the parent dataverse")] parent: String, /// Path to a JSON/YAML file containing the collection configuration - #[structopt( + #[arg( long, short, help = "Path to the JSON/YAML file containing the collection body" @@ -38,27 +36,24 @@ pub enum CollectionSubCommand { body: PathBuf, }, - /// Get the content/metadata of a collection - #[structopt(about = "Collection content")] + /// Collection content Content { /// Alias of the collection to get content for - #[structopt(help = "Alias of the collection")] + #[arg(help = "Alias of the collection")] alias: String, }, - /// Publish a collection, making it publicly visible - #[structopt(about = "Publish a collection")] + /// Publish a collection Publish { /// Alias of the collection to publish - #[structopt(help = "Alias of the collection to publish")] + #[arg(help = "Alias of the collection to publish")] alias: String, }, - /// Delete a collection from the Dataverse instance - #[structopt(about = "Delete a collection")] + /// Delete a collection Delete { /// Alias of the collection to delete - #[structopt(help = "Alias of the collection to delete")] + #[arg(help = "Alias of the collection to delete")] alias: String, }, } diff --git a/src/cli/dataset.rs b/src/cli/dataset.rs index 8a9dd5e..357890b 100644 --- a/src/cli/dataset.rs +++ b/src/cli/dataset.rs @@ -12,8 +12,8 @@ use std::fs::File; use std::io::Write; use std::path::{Path, PathBuf}; +use clap::Subcommand; use colored_json::Paint; -use structopt::StructOpt; use tokio::runtime::Runtime; use crate::client::{print_error, BaseClient}; @@ -40,17 +40,33 @@ use crate::{data_access, direct_upload}; use super::base::{evaluate_and_print_response, parse_file, Matcher}; +/// A CLI-friendly wrapper for file uploads that can be easily cloned and parsed +#[derive(Debug, Clone)] +pub struct CliUploadFile(String); + +impl std::str::FromStr for CliUploadFile { + type Err = String; + + fn from_str(s: &str) -> Result { + Ok(CliUploadFile(s.to_string())) + } +} + +impl From for UploadFile { + fn from(cli_file: CliUploadFile) -> Self { + UploadFile::from(cli_file.0.as_str()) + } +} + /// Subcommands for managing datasets in a Dataverse instance -#[derive(StructOpt, Debug)] -#[structopt(about = "Handle datasets of the Dataverse instance")] +#[derive(Subcommand, Debug)] pub enum DatasetSubCommand { - /// Retrieve a dataset's metadata - #[structopt(about = "Retrieve a datasets metadata")] + /// Retrieve a datasets metadata Meta { - #[structopt(help = "(Peristent) identifier of the dataset to retrieve")] + #[arg(help = "(Peristent) identifier of the dataset to retrieve")] id: Identifier, - #[structopt( + #[arg( short, long, help = "Version of the dataset to retrieve. Defaults to ':latest' when there is no API token, and ':draft' when there is an API token." @@ -58,13 +74,12 @@ pub enum DatasetSubCommand { version: Option, }, - /// Create a new dataset in a collection - #[structopt(about = "Create a dataset")] + /// Create a dataset Create { - #[structopt(long, short, help = "Alias of the collection to create the dataset in")] + #[arg(long, short, help = "Alias of the collection to create the dataset in")] collection: String, - #[structopt( + #[arg( long, short, help = "Path to the JSON/YAML file containing the dataset body" @@ -72,13 +87,12 @@ pub enum DatasetSubCommand { body: PathBuf, }, - /// Publish a dataset version - #[structopt(about = "Publishes a dataset")] + /// Publishes a dataset Publish { - #[structopt(help = "Persistent identifier of the dataset to publish")] + #[arg(help = "Persistent identifier of the dataset to publish")] pid: String, - #[structopt( + #[arg( long, short, help = "Version of the dataset to publish (major, minor, updatecurrent)", @@ -87,73 +101,68 @@ pub enum DatasetSubCommand { version: Version, }, - /// Delete a dataset from the Dataverse instance - #[structopt(about = "Deletes a dataset")] + /// Deletes a dataset Delete { - #[structopt(help = "Identifier of the dataset to delete")] + #[arg(help = "Identifier of the dataset to delete")] id: i64, }, - /// Edit dataset metadata - #[structopt(about = "Edit the metadata of a dataset")] + /// Edit the metadata of a dataset Edit { - #[structopt(long, short, help = "Persistent identifier of the dataset to edit")] + #[arg(long, short, help = "Persistent identifier of the dataset to edit")] pid: String, - #[structopt( + #[arg( long, short, help = "Path to the JSON/YAML file containing the metadata to edit" )] body: PathBuf, - #[structopt(long, short, help = "Whether to replace the metadata or not")] + #[arg(long, short, help = "Whether to replace the metadata or not")] replace: bool, }, /// Link a dataset to another collection - #[structopt(about = "Link a dataset to another collection")] Link { - #[structopt(long, short, help = "(Persistent) identifier of the dataset to link")] + #[arg(long, short, help = "(Persistent) identifier of the dataset to link")] id: Identifier, - #[structopt(long, short, help = "Alias of the collection to link the dataset to")] + #[arg(long, short, help = "Alias of the collection to link the dataset to")] collection: String, }, /// Upload a file to a dataset - #[structopt(about = "Upload a file to a dataset")] Upload { - #[structopt( + #[arg( long, short, help = "(Persistent) Identifier of the dataset to upload the file to" )] id: Identifier, - #[structopt(help = "Path or URL to the file to upload")] - path: UploadFile, + #[arg(help = "Path or URL to the file to upload")] + path: CliUploadFile, - #[structopt(short, long, help = "Dataverse path to the file to upload")] + #[arg(short, long, help = "Dataverse path to the file to upload")] dv_path: Option, - #[structopt(long, help = "Path to the JSON/YAML file containing the file body")] + #[arg(long, help = "Path to the JSON/YAML file containing the file body")] body: Option, - #[structopt(long, short, help = "Replace the file if it already exists")] + #[arg(long, short, help = "Replace the file if it already exists")] replace: bool, }, /// Upload a file to a dataset using direct upload - #[structopt(about = "Upload a file to a dataset using direct upload")] DirectUpload { - #[structopt(long, short, help = "Identifier of the dataset to upload the file to")] + #[arg(long, short, help = "Identifier of the dataset to upload the file to")] id: Identifier, - #[structopt(help = "Path to the file to upload")] + #[arg(help = "Path to the file to upload")] paths: Vec, - #[structopt( + #[arg( long, short, help = "Number of files to upload in parallel", @@ -162,17 +171,16 @@ pub enum DatasetSubCommand { parallel: usize, }, - /// Download files from a dataset - #[structopt(about = "Download a file from a dataset")] + /// Download a file from a dataset Download { - #[structopt( + #[arg( short, long, help = "Identifier of the dataset to download the file from" )] id: Option, - #[structopt( + #[arg( short, long, help = "Directory to save the file to", @@ -180,29 +188,28 @@ pub enum DatasetSubCommand { )] out: PathBuf, - #[structopt( + #[arg( short, long, help = "Version of the dataset to download the file from. Defaults to ':latest' when there is no API token, and ':draft' when there is an API token." )] version: Option, - #[structopt(long, help = "Whether to download the entire dataset or a single file")] + #[arg(long, help = "Whether to download the entire dataset or a single file")] complete: bool, - #[structopt( + #[arg( help = "Path/ID/PID to the file to download. Required when downloading a single file." )] path: Option, }, /// List files in a dataset - #[structopt(about = "List files in a dataset")] ListFiles { - #[structopt(help = "Identifier of the dataset to list the files of")] + #[arg(help = "Identifier of the dataset to list the files of")] id: Identifier, - #[structopt( + #[arg( short, long, help = "Version of the dataset to list the files of. Defaults to ':latest' when there is no API token, and ':draft' when there is an API token." @@ -210,13 +217,12 @@ pub enum DatasetSubCommand { version: Option, }, - /// Get the total size of a dataset - #[structopt(about = "Retrieve the size of a dataset")] + /// Retrieve the size of a dataset Size { - #[structopt(help = "Identifier of the dataset to retrieve the size of")] + #[arg(help = "Identifier of the dataset to retrieve the size of")] id: Identifier, - #[structopt( + #[arg( short, long, help = "Version of the dataset to retrieve the size of. Defaults to ':latest' when there is no API token, and ':draft' when there is an API token." @@ -224,42 +230,40 @@ pub enum DatasetSubCommand { version: Option, }, - /// Export a dataset - #[structopt(about = "Export a dataset to a variety of formats")] + /// Export a dataset to a variety of formats Export { - #[structopt(short, long, help = "(Persistent) identifier of the dataset to export")] + #[arg(short, long, help = "(Persistent) identifier of the dataset to export")] id: Identifier, - #[structopt( + #[arg( short, long, help = "Format to use. E.g. 'ddi', 'oai_ddi', 'datacite', etc." )] format: String, - #[structopt(short, long, help = "Path to the file to save the export to")] + #[arg(short, long, help = "Path to the file to save the export to")] out: PathBuf, }, /// Get locks for a dataset - #[structopt(about = "Get locks for a dataset")] Locks { - #[structopt(help = "Identifier of the dataset to get locks for")] + #[arg(help = "Identifier of the dataset to get locks for")] id: Identifier, - #[structopt(short = "t", long = "type", help = "Lock type to set to or filter by")] + #[arg(short = 't', long = "type", help = "Lock type to set to or filter by")] lock_type: Option, - #[structopt( - short = "s", + #[arg( + short = 's', long = "set", help = "Whether to set the lock specified by '-t' on the dataset", conflicts_with = "remove" )] set: bool, - #[structopt( - short = "r", + #[arg( + short = 'r', long = "remove", help = "Whether to remove the lock specified by '-t' from the dataset", conflicts_with = "set" @@ -268,26 +272,25 @@ pub enum DatasetSubCommand { }, /// Submit a dataset for review - #[structopt(about = "Submit a dataset for review")] Review { - #[structopt(help = "Identifier of the dataset to submit for review")] + #[arg(help = "Identifier of the dataset to submit for review")] id: Identifier, - #[structopt( + #[arg( long, short, help = "Submit the dataset for review", conflicts_with = "reason", - required_unless = "reason" + required_unless_present = "reason" )] submit: bool, - #[structopt( + #[arg( long = "reason", short, help = "The reason for returning the dataset to the author", conflicts_with = "submit", - required_unless = "submit" + required_unless_present = "submit" )] reason: Option, }, @@ -345,7 +348,7 @@ impl Matcher for DatasetSubCommand { dv_path, } => { let body = Self::prepare_upload_body(body, &dv_path); - let mut path = path; + let mut path: UploadFile = path.into(); if let FileSource::RemoteUrl(_) = &path.file { if let Some(dv_path) = &dv_path { diff --git a/src/cli/file.rs b/src/cli/file.rs index 0599dc3..b634639 100644 --- a/src/cli/file.rs +++ b/src/cli/file.rs @@ -7,7 +7,7 @@ use std::path::PathBuf; -use structopt::StructOpt; +use clap::Subcommand; use crate::data_access; use crate::data_access::datafile::DataFilePath; @@ -20,46 +20,42 @@ use crate::{client::BaseClient, native_api::dataset::upload::UploadBody}; use super::base::{evaluate_and_print_response, parse_file, Matcher}; /// Subcommands for managing files in a Dataverse instance -#[derive(StructOpt, Debug)] -#[structopt(about = "Handle files of a Dataverse instance")] +#[derive(Subcommand, Debug)] pub enum FileSubCommand { - /// Retrieves metadata for a specific file - #[structopt(about = "Get file metadata")] + /// Get file metadata Meta { - #[structopt(help = "Identifier of the file to get metadata for")] + #[arg(help = "Identifier of the file to get metadata for")] id: Identifier, }, - /// Replaces an existing file with a new version - #[structopt(about = "Replace a file")] + /// Replace a file Replace { - #[structopt(help = "Path to the file to replace")] + #[arg(help = "Path to the file to replace")] path: PathBuf, - #[structopt(long, short, help = "Identifier of the of the file to replace")] + #[arg(long, short, help = "Identifier of the of the file to replace")] id: String, - #[structopt( + #[arg( long, short, help = "Path to the JSON/YAML file containing the file body" )] body: Option, - #[structopt(long, short, help = "Force the replacement of the file")] + #[arg(long, short, help = "Force the replacement of the file")] force: bool, }, - /// Downloads a file from a dataset - #[structopt(about = "Download a file")] + /// Download a file Download { - #[structopt(help = "Identifier of the file to download")] + #[arg(help = "Identifier of the file to download")] file_id: DataFilePath, - #[structopt(short, long, help = "Path to save the file to")] + #[arg(short, long, help = "Path to save the file to")] path: PathBuf, - #[structopt(short, long, help = "Version of the dataset to download the file from")] + #[arg(short, long, help = "Version of the dataset to download the file from")] version: Option, }, } diff --git a/src/cli/info.rs b/src/cli/info.rs index f036761..128b1a2 100644 --- a/src/cli/info.rs +++ b/src/cli/info.rs @@ -8,9 +8,9 @@ use crate::client::BaseClient; use crate::native_api; +use clap::Subcommand; use colored::Colorize; use lazy_static::lazy_static; -use structopt::StructOpt; use super::base::{evaluate_and_print_response, Matcher}; @@ -27,15 +27,12 @@ You can use the key to export a dataset using the following command: } /// Subcommands for retrieving Dataverse instance information -#[derive(StructOpt, Debug)] -#[structopt(about = "Retrieve information about the Dataverse instance")] +#[derive(Subcommand, Debug)] pub enum InfoSubCommand { - /// Retrieves the version of the Dataverse instance - #[structopt(about = "Retrieve the version of the Dataverse instance")] + /// Retrieve the version of the Dataverse instance Version, - /// Retrieves the exporters of the Dataverse instance - #[structopt(about = "Retrieve the exporters of the Dataverse instance")] + /// Retrieve the exporters of the Dataverse instance Exporters, } diff --git a/src/data_access/datafile.rs b/src/data_access/datafile.rs index 9ebaf8f..7d96d48 100644 --- a/src/data_access/datafile.rs +++ b/src/data_access/datafile.rs @@ -142,7 +142,7 @@ async fn single_stream( } /// Represents different ways to identify a data file. -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum DataFilePath { /// A file path as a string. Path(String), diff --git a/src/search_api/query.rs b/src/search_api/query.rs index 95d0b9c..6f7e724 100644 --- a/src/search_api/query.rs +++ b/src/search_api/query.rs @@ -2,9 +2,9 @@ use std::collections::HashMap; use std::fmt; use std::str::FromStr; +use clap::Args; +use lazy_static::lazy_static; use serde::{Deserialize, Serialize}; -use structopt::lazy_static::lazy_static; -use structopt::StructOpt; use tokio::runtime::Runtime; use crate::cli::base::{evaluate_and_print_response, Matcher}; @@ -72,63 +72,62 @@ macro_rules! insert_multiple_if_some { /// This struct encapsulates all possible parameters that can be used when /// searching a Dataverse instance. It provides a flexible way to construct /// search queries with different filtering, sorting, and pagination options. -#[derive(Debug, Serialize, Deserialize, StructOpt, Default)] -#[structopt(about = "Search a Dataverse instance")] +#[derive(Debug, Serialize, Deserialize, Args, Default)] pub struct SearchQuery { /// The search query string. This is the main search term. - #[structopt(short = "q", long = "query", help = "The search query string")] + #[arg(short = 'q', long = "query", help = "The search query string")] pub q: String, /// The type of search to perform (dataverse, dataset, or file). - #[structopt(short = "t", long = "type", help = "The type of search")] + #[arg(short = 't', long = "type", help = "The type of search")] pub search_type: Option>, // Represents "type" in the query parameters /// The subtree to search within, limiting results to a specific dataverse. - #[structopt(long, help = "The subtree to search within")] + #[arg(long, help = "The subtree to search within")] pub subtree: Option>, /// The field to sort results by (name or date). - #[structopt(long, help = "The field to sort by")] + #[arg(long, help = "The field to sort by")] pub sort: Option, /// The order of sorting (ascending or descending). - #[structopt(long, help = "The order of sorting")] + #[arg(long, help = "The order of sorting")] pub order: Option, /// The number of results to return per page. - #[structopt(long, help = "The number of results per page")] + #[arg(long, help = "The number of results per page")] pub per_page: Option, /// The starting index of the results for pagination. - #[structopt(long, help = "The starting index of the results")] + #[arg(long, help = "The starting index of the results")] pub start: Option, /// Whether to show relevance scores in the search results. - #[structopt(long, help = "Whether to show relevance scores")] + #[arg(long, help = "Whether to show relevance scores")] pub show_relevance: Option, /// Whether to show facets in the search results. - #[structopt(long, help = "Whether to show facets")] + #[arg(long, help = "Whether to show facets")] pub show_facets: Option, /// Filter queries to narrow down search results. - #[structopt(long = "filter", help = "The filter query")] + #[arg(long = "filter", help = "The filter query")] pub fq: Option>, /// Whether to show entity IDs in the search results. - #[structopt(long, help = "Whether to show entity IDs")] + #[arg(long, help = "Whether to show entity IDs")] pub show_entity_ids: Option, /// The geographic point for geo-spatial searches. - #[structopt(long, help = "The geographic point")] + #[arg(long, help = "The geographic point")] pub geo_point: Option, /// The geographic radius for geo-spatial searches. - #[structopt(long, help = "The geographic radius")] + #[arg(long, help = "The geographic radius")] pub geo_radius: Option, /// The metadata fields to search within, limiting the search scope. - #[structopt(long = "fields", help = "The metadata fields to search within")] + #[arg(long = "fields", help = "The metadata fields to search within")] pub metadata_fields: Option>, } @@ -217,7 +216,7 @@ impl Matcher for SearchQuery { /// - Dataverse: Search for dataverse containers /// - Dataset: Search for datasets /// - File: Search for individual files -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Serialize, Deserialize, Clone)] pub enum SearchType { /// Search for dataverse containers Dataverse, @@ -255,7 +254,7 @@ impl FromStr for SearchType { /// This enum defines the available fields that can be used for sorting search results: /// - Name: Sort by name /// - Date: Sort by date -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Serialize, Deserialize, Clone)] pub enum SortField { /// Sort by name Name, @@ -290,7 +289,7 @@ impl FromStr for SortField { /// This enum defines the available sort orders: /// - Asc: Ascending order (A-Z, oldest to newest) /// - Desc: Descending order (Z-A, newest to oldest) -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Serialize, Deserialize, Clone)] pub enum Order { /// Ascending order Asc,