diff --git a/cdx-cli/src/cli.rs b/cdx-cli/src/cli.rs new file mode 100644 index 0000000..28b1a68 --- /dev/null +++ b/cdx-cli/src/cli.rs @@ -0,0 +1,411 @@ +//! CLI argument definitions. + +use clap::{Parser, Subcommand}; +use clap_complete::Shell; +use std::path::PathBuf; + +#[derive(Parser)] +#[command(name = "cdx")] +#[command(author, version, about = "Codex Document Format CLI", long_about = None)] +#[command(propagate_version = true)] +pub struct Cli { + #[command(subcommand)] + pub command: Commands, + + /// Increase output verbosity + #[arg(short, long, global = true)] + pub verbose: bool, + + /// Suppress non-error output + #[arg(short, long, global = true)] + pub quiet: bool, + + /// Output as JSON (for scripting) + #[arg(long, global = true)] + pub json: bool, + + /// Color output control + #[arg(long, value_enum, default_value = "auto", global = true)] + pub color: ColorChoice, +} + +#[derive(Clone, Copy, PartialEq, Eq, clap::ValueEnum)] +pub enum ColorChoice { + Auto, + Always, + Never, +} + +#[derive(Subcommand)] +pub enum Commands { + /// Create a new Codex document + Create { + /// Document title (required) + #[arg(short, long)] + title: String, + + /// Author name(s) + #[arg(short, long)] + author: Vec, + + /// Initial state + #[arg(long, default_value = "draft")] + state: String, + + /// Input content file (markdown, text) + #[arg(short, long)] + input: Option, + + /// Output file + output: PathBuf, + }, + + /// Validate document structure and hashes + Validate { + /// Codex document to validate + file: PathBuf, + }, + + /// Display document information + Inspect { + /// Codex document to inspect + file: PathBuf, + + /// Show detailed block information + #[arg(long)] + blocks: bool, + + /// Show signature details + #[arg(long)] + signatures: bool, + + /// Show provenance chain + #[arg(long)] + provenance: bool, + }, + + /// Show comprehensive document status + Status { + /// Codex document to check + file: PathBuf, + }, + + /// Add a digital signature + Sign { + /// Codex document to sign + file: PathBuf, + + /// Private key file (PEM format) + #[arg(short, long)] + key: PathBuf, + + /// Signer name + #[arg(short, long)] + name: String, + + /// Signer email + #[arg(short, long)] + email: Option, + + /// Signature algorithm + #[arg(short, long, default_value = "ES256")] + algorithm: String, + + /// Output file (default: overwrite input) + #[arg(short, long)] + output: Option, + }, + + /// Verify signatures and integrity + Verify { + /// Codex document to verify + file: PathBuf, + + /// Public key file(s) for signature verification + #[arg(short, long)] + key: Vec, + }, + + /// Extract content or assets + Extract { + /// Codex document to extract from + file: PathBuf, + + /// Output directory for extraction + #[arg(short, long, default_value = ".")] + output: PathBuf, + + /// Extract content as JSON + #[arg(long)] + content: bool, + + /// Extract as plain text + #[arg(long)] + text: bool, + + /// Extract specific asset + #[arg(long)] + asset: Option, + + /// Extract all assets + #[arg(long)] + all_assets: bool, + }, + + /// Generate shell completions + Completions { + /// Shell to generate completions for + shell: Shell, + }, + + /// Submit document for review (draft → review) + #[command(name = "submit-review")] + SubmitReview { + /// Codex document to submit + file: PathBuf, + + /// Output file (default: overwrite input) + #[arg(short, long)] + output: Option, + }, + + /// Freeze document (review → frozen) + Freeze { + /// Codex document to freeze + file: PathBuf, + + /// Output file (default: overwrite input) + #[arg(short, long)] + output: Option, + }, + + /// Publish document (frozen → published) + Publish { + /// Codex document to publish + file: PathBuf, + + /// Output file (default: overwrite input) + #[arg(short, long)] + output: Option, + }, + + /// Revert document to draft (review → draft) + Revert { + /// Codex document to revert + file: PathBuf, + + /// Output file (default: overwrite input) + #[arg(short, long)] + output: Option, + }, + + /// Fork document to create new version with lineage + Fork { + /// Codex document to fork + file: PathBuf, + + /// Output file for the forked document + #[arg(short, long)] + output: PathBuf, + + /// Note describing the changes + #[arg(short, long)] + note: Option, + }, + + /// Generate a Merkle proof for a block + Prove { + /// Codex document + file: PathBuf, + + /// Block ID to prove + #[arg(long, conflicts_with = "block_index")] + block_id: Option, + + /// Block index to prove (0-based) + #[arg(long, conflicts_with = "block_id")] + block_index: Option, + + /// Output file for the proof JSON + #[arg(short, long)] + output: Option, + }, + + /// Verify a Merkle proof against a document + #[command(name = "verify-proof")] + VerifyProof { + /// Codex document + file: PathBuf, + + /// Proof JSON file + proof: PathBuf, + }, + + /// Show document lineage (ancestor chain) + #[command(name = "show-lineage")] + ShowLineage { + /// Codex document + file: PathBuf, + }, + + /// Display document metadata + #[command(name = "get-metadata")] + GetMetadata { + /// Codex document + file: PathBuf, + }, + + /// Set document metadata fields + #[command(name = "set-metadata")] + SetMetadata { + /// Codex document + file: PathBuf, + + /// Set title + #[arg(long)] + title: Option, + + /// Set creator(s) + #[arg(long)] + creator: Vec, + + /// Set subject(s) + #[arg(long)] + subject: Vec, + + /// Set description + #[arg(long)] + description: Option, + + /// Set publisher + #[arg(long)] + publisher: Option, + + /// Set language (BCP 47 code) + #[arg(long)] + language: Option, + + /// Set rights statement + #[arg(long)] + rights: Option, + + /// Output file (default: overwrite input) + #[arg(short, long)] + output: Option, + }, + + /// Pack a directory or JSON into a .cdx archive + Pack { + /// Input directory or JSON file + input: PathBuf, + + /// Output .cdx file + #[arg(short, long)] + output: PathBuf, + + /// Input is combined JSON from Pandoc writer + #[arg(long)] + from_json: bool, + }, + + /// Compare two Codex documents + Diff { + /// First document + file1: PathBuf, + + /// Second document + file2: PathBuf, + }, + + /// Show timestamps in a document + #[command(name = "show-timestamps")] + ShowTimestamps { + /// Codex document + file: PathBuf, + }, + + /// Verify timestamps in a document + #[command(name = "verify-timestamps")] + VerifyTimestamps { + /// Codex document + file: PathBuf, + }, + + /// Add a timestamp record to a document + #[command(name = "add-timestamp")] + AddTimestamp { + /// Codex document + file: PathBuf, + + /// Timestamp method (rfc3161, bitcoin, ethereum, opentimestamps) + #[arg(long)] + method: String, + + /// Timestamp authority URL or name + #[arg(long)] + authority: String, + + /// Base64-encoded timestamp token + #[arg(long)] + token: String, + + /// Timestamp time (RFC 3339 format, defaults to now) + #[arg(long)] + time: Option, + + /// Transaction ID (for blockchain timestamps) + #[arg(long)] + transaction_id: Option, + + /// Output file (default: overwrite input) + #[arg(short, long)] + output: Option, + }, + + /// Acquire a timestamp from a timestamp authority + #[command(name = "timestamp-acquire")] + TimestampAcquire { + /// Codex document to timestamp + file: PathBuf, + + /// Timestamp method (rfc3161, ots, auto) + #[arg(short, long, default_value = "auto")] + method: Option, + + /// TSA server URL (for rfc3161, uses defaults if not specified) + #[arg(short, long)] + server: Option, + + /// Output file (default: overwrite input) + #[arg(short, long)] + output: Option, + }, + + /// Encrypt a document with password-based encryption + Encrypt { + /// Codex document to encrypt + file: PathBuf, + + /// Password (will prompt if not provided) + #[arg(short, long)] + password: Option, + + /// Output file (default: overwrite input) + #[arg(short, long)] + output: Option, + }, + + /// Decrypt a password-encrypted document + Decrypt { + /// Codex document to decrypt + file: PathBuf, + + /// Password (will prompt if not provided) + #[arg(short, long)] + password: Option, + + /// Output file (default: overwrite input) + #[arg(short, long)] + output: Option, + }, +} diff --git a/cdx-cli/src/dispatcher.rs b/cdx-cli/src/dispatcher.rs new file mode 100644 index 0000000..014f9ea --- /dev/null +++ b/cdx-cli/src/dispatcher.rs @@ -0,0 +1,193 @@ +//! Command dispatch. + +use anyhow::Result; +use clap::CommandFactory; +use clap_complete::generate; +use std::io; + +use crate::cli::{Cli, Commands}; +use crate::commands; +use crate::output; + +#[allow(clippy::too_many_lines)] // flat match dispatching each CLI subcommand — no shared logic to extract +pub fn run_command(command: Commands, output_config: &output::OutputConfig) -> Result<()> { + match command { + Commands::Create { + title, + author, + state, + input, + output: output_path, + } => commands::create::run(&title, &author, &state, input, &output_path, output_config), + + Commands::Validate { file } => commands::validate::run(&file, output_config), + + Commands::Inspect { + file, + blocks, + signatures, + provenance, + } => commands::inspect::run(&file, blocks, signatures, provenance, output_config), + + Commands::Status { file } => commands::status::run(&file, output_config), + + Commands::Sign { + file, + key, + name, + email, + algorithm, + output: output_path, + } => commands::sign::run( + &file, + &key, + &name, + email, + &algorithm, + output_path, + output_config, + ), + + Commands::Verify { file, key } => commands::verify::run(&file, &key, output_config), + + Commands::Extract { + file, + output: output_path, + content, + text, + asset, + all_assets, + } => commands::extract::run( + &file, + &output_path, + content, + text, + asset.as_deref(), + all_assets, + output_config, + ), + + Commands::Completions { shell } => { + generate(shell, &mut Cli::command(), "cdx", &mut io::stdout()); + Ok(()) + } + + Commands::SubmitReview { file, output } => { + commands::review::run(&file, output, output_config) + } + + Commands::Freeze { file, output } => commands::freeze::run(&file, output, output_config), + + Commands::Publish { file, output } => commands::publish::run(&file, output, output_config), + + Commands::Revert { file, output } => commands::revert::run(&file, output, output_config), + + Commands::Fork { file, output, note } => { + commands::fork::run(&file, &output, note, output_config) + } + + Commands::Prove { + file, + block_id, + block_index, + output, + } => commands::prove::run_prove(&file, block_id, block_index, output, output_config), + + Commands::VerifyProof { file, proof } => { + commands::prove::run_verify_proof(&file, &proof, output_config) + } + + Commands::ShowLineage { file } => commands::prove::run_show_lineage(&file, output_config), + + Commands::GetMetadata { file } => { + commands::metadata::run_get_metadata(&file, output_config) + } + + Commands::SetMetadata { + file, + title, + creator, + subject, + description, + publisher, + language, + rights, + output, + } => { + let params = commands::metadata::SetMetadataParams { + file, + title, + creator, + subject, + description, + publisher, + language, + rights, + output, + }; + commands::metadata::run_set_metadata(¶ms, output_config) + } + + Commands::Pack { + input, + output: output_path, + from_json, + } => commands::pack::run(&input, &output_path, from_json, output_config), + + Commands::Diff { file1, file2 } => commands::diff::run(&file1, &file2, output_config), + + Commands::ShowTimestamps { file } => { + commands::timestamp::run_show_timestamps(&file, output_config) + } + + Commands::VerifyTimestamps { file } => { + commands::timestamp::run_verify_timestamps(&file, output_config) + } + + Commands::AddTimestamp { + file, + method, + authority, + token, + time, + transaction_id, + output, + } => { + let params = commands::timestamp::AddTimestampParams { + file, + method, + authority, + token, + time, + transaction_id, + _output: output, + }; + commands::timestamp::run_add_timestamp(¶ms, output_config) + } + + Commands::TimestampAcquire { + file, + method, + server, + output, + } => commands::timestamp::run_acquire_timestamp( + &file, + method.as_deref(), + server.as_deref(), + output, + output_config, + ), + + Commands::Encrypt { + file, + password, + output, + } => commands::encrypt::run(&file, password, output, output_config), + + Commands::Decrypt { + file, + password, + output, + } => commands::decrypt::run(&file, password, output, output_config), + } +} diff --git a/cdx-cli/src/main.rs b/cdx-cli/src/main.rs index bcfb0d1..bc049d9 100644 --- a/cdx-cli/src/main.rs +++ b/cdx-cli/src/main.rs @@ -5,430 +5,22 @@ #![warn(clippy::pedantic)] #![allow(clippy::module_name_repetitions)] +mod cli; mod commands; +mod dispatcher; mod output; -use anyhow::Result; -use clap::{CommandFactory, Parser, Subcommand}; -use clap_complete::{generate, Shell}; +use clap::Parser; use colored::Colorize; -use std::io; -use std::path::PathBuf; - -#[derive(Parser)] -#[command(name = "cdx")] -#[command(author, version, about = "Codex Document Format CLI", long_about = None)] -#[command(propagate_version = true)] -struct Cli { - #[command(subcommand)] - command: Commands, - - /// Increase output verbosity - #[arg(short, long, global = true)] - verbose: bool, - - /// Suppress non-error output - #[arg(short, long, global = true)] - quiet: bool, - - /// Output as JSON (for scripting) - #[arg(long, global = true)] - json: bool, - - /// Color output control - #[arg(long, value_enum, default_value = "auto", global = true)] - color: ColorChoice, -} - -#[derive(Clone, Copy, PartialEq, Eq, clap::ValueEnum)] -enum ColorChoice { - Auto, - Always, - Never, -} - -#[derive(Subcommand)] -enum Commands { - /// Create a new Codex document - Create { - /// Document title (required) - #[arg(short, long)] - title: String, - - /// Author name(s) - #[arg(short, long)] - author: Vec, - - /// Initial state - #[arg(long, default_value = "draft")] - state: String, - - /// Input content file (markdown, text) - #[arg(short, long)] - input: Option, - - /// Output file - output: PathBuf, - }, - - /// Validate document structure and hashes - Validate { - /// Codex document to validate - file: PathBuf, - }, - - /// Display document information - Inspect { - /// Codex document to inspect - file: PathBuf, - - /// Show detailed block information - #[arg(long)] - blocks: bool, - - /// Show signature details - #[arg(long)] - signatures: bool, - - /// Show provenance chain - #[arg(long)] - provenance: bool, - }, - - /// Show comprehensive document status - Status { - /// Codex document to check - file: PathBuf, - }, - - /// Add a digital signature - Sign { - /// Codex document to sign - file: PathBuf, - - /// Private key file (PEM format) - #[arg(short, long)] - key: PathBuf, - - /// Signer name - #[arg(short, long)] - name: String, - - /// Signer email - #[arg(short, long)] - email: Option, - - /// Signature algorithm - #[arg(short, long, default_value = "ES256")] - algorithm: String, - - /// Output file (default: overwrite input) - #[arg(short, long)] - output: Option, - }, - - /// Verify signatures and integrity - Verify { - /// Codex document to verify - file: PathBuf, - - /// Public key file(s) for signature verification - #[arg(short, long)] - key: Vec, - }, - - /// Extract content or assets - Extract { - /// Codex document to extract from - file: PathBuf, - - /// Output directory for extraction - #[arg(short, long, default_value = ".")] - output: PathBuf, - - /// Extract content as JSON - #[arg(long)] - content: bool, - - /// Extract as plain text - #[arg(long)] - text: bool, - - /// Extract specific asset - #[arg(long)] - asset: Option, - - /// Extract all assets - #[arg(long)] - all_assets: bool, - }, - - /// Generate shell completions - Completions { - /// Shell to generate completions for - shell: Shell, - }, - - /// Submit document for review (draft → review) - #[command(name = "submit-review")] - SubmitReview { - /// Codex document to submit - file: PathBuf, - - /// Output file (default: overwrite input) - #[arg(short, long)] - output: Option, - }, - - /// Freeze document (review → frozen) - Freeze { - /// Codex document to freeze - file: PathBuf, - - /// Output file (default: overwrite input) - #[arg(short, long)] - output: Option, - }, - - /// Publish document (frozen → published) - Publish { - /// Codex document to publish - file: PathBuf, - - /// Output file (default: overwrite input) - #[arg(short, long)] - output: Option, - }, - - /// Revert document to draft (review → draft) - Revert { - /// Codex document to revert - file: PathBuf, - - /// Output file (default: overwrite input) - #[arg(short, long)] - output: Option, - }, - - /// Fork document to create new version with lineage - Fork { - /// Codex document to fork - file: PathBuf, - - /// Output file for the forked document - #[arg(short, long)] - output: PathBuf, - - /// Note describing the changes - #[arg(short, long)] - note: Option, - }, - - /// Generate a Merkle proof for a block - Prove { - /// Codex document - file: PathBuf, - - /// Block ID to prove - #[arg(long, conflicts_with = "block_index")] - block_id: Option, - - /// Block index to prove (0-based) - #[arg(long, conflicts_with = "block_id")] - block_index: Option, - - /// Output file for the proof JSON - #[arg(short, long)] - output: Option, - }, - - /// Verify a Merkle proof against a document - #[command(name = "verify-proof")] - VerifyProof { - /// Codex document - file: PathBuf, - - /// Proof JSON file - proof: PathBuf, - }, - - /// Show document lineage (ancestor chain) - #[command(name = "show-lineage")] - ShowLineage { - /// Codex document - file: PathBuf, - }, - - /// Display document metadata - #[command(name = "get-metadata")] - GetMetadata { - /// Codex document - file: PathBuf, - }, - - /// Set document metadata fields - #[command(name = "set-metadata")] - SetMetadata { - /// Codex document - file: PathBuf, - - /// Set title - #[arg(long)] - title: Option, - - /// Set creator(s) - #[arg(long)] - creator: Vec, - - /// Set subject(s) - #[arg(long)] - subject: Vec, - - /// Set description - #[arg(long)] - description: Option, - - /// Set publisher - #[arg(long)] - publisher: Option, - - /// Set language (BCP 47 code) - #[arg(long)] - language: Option, - - /// Set rights statement - #[arg(long)] - rights: Option, - - /// Output file (default: overwrite input) - #[arg(short, long)] - output: Option, - }, - - /// Pack a directory or JSON into a .cdx archive - Pack { - /// Input directory or JSON file - input: PathBuf, - - /// Output .cdx file - #[arg(short, long)] - output: PathBuf, - - /// Input is combined JSON from Pandoc writer - #[arg(long)] - from_json: bool, - }, - - /// Compare two Codex documents - Diff { - /// First document - file1: PathBuf, - - /// Second document - file2: PathBuf, - }, - - /// Show timestamps in a document - #[command(name = "show-timestamps")] - ShowTimestamps { - /// Codex document - file: PathBuf, - }, - - /// Verify timestamps in a document - #[command(name = "verify-timestamps")] - VerifyTimestamps { - /// Codex document - file: PathBuf, - }, - - /// Add a timestamp record to a document - #[command(name = "add-timestamp")] - AddTimestamp { - /// Codex document - file: PathBuf, - - /// Timestamp method (rfc3161, bitcoin, ethereum, opentimestamps) - #[arg(long)] - method: String, - - /// Timestamp authority URL or name - #[arg(long)] - authority: String, - - /// Base64-encoded timestamp token - #[arg(long)] - token: String, - - /// Timestamp time (RFC 3339 format, defaults to now) - #[arg(long)] - time: Option, - - /// Transaction ID (for blockchain timestamps) - #[arg(long)] - transaction_id: Option, - - /// Output file (default: overwrite input) - #[arg(short, long)] - output: Option, - }, - - /// Acquire a timestamp from a timestamp authority - #[command(name = "timestamp-acquire")] - TimestampAcquire { - /// Codex document to timestamp - file: PathBuf, - - /// Timestamp method (rfc3161, ots, auto) - #[arg(short, long, default_value = "auto")] - method: Option, - - /// TSA server URL (for rfc3161, uses defaults if not specified) - #[arg(short, long)] - server: Option, - - /// Output file (default: overwrite input) - #[arg(short, long)] - output: Option, - }, - - /// Encrypt a document with password-based encryption - Encrypt { - /// Codex document to encrypt - file: PathBuf, - - /// Password (will prompt if not provided) - #[arg(short, long)] - password: Option, - - /// Output file (default: overwrite input) - #[arg(short, long)] - output: Option, - }, - - /// Decrypt a password-encrypted document - Decrypt { - /// Codex document to decrypt - file: PathBuf, - - /// Password (will prompt if not provided) - #[arg(short, long)] - password: Option, - - /// Output file (default: overwrite input) - #[arg(short, long)] - output: Option, - }, -} fn main() { - let cli = Cli::parse(); + let cli = cli::Cli::parse(); // Configure color output match cli.color { - ColorChoice::Always => colored::control::set_override(true), - ColorChoice::Never => colored::control::set_override(false), - ColorChoice::Auto => {} + cli::ColorChoice::Always => colored::control::set_override(true), + cli::ColorChoice::Never => colored::control::set_override(false), + cli::ColorChoice::Auto => {} } let output_config = output::OutputConfig { @@ -437,7 +29,7 @@ fn main() { json: cli.json, }; - let result = run_command(cli.command, &output_config); + let result = dispatcher::run_command(cli.command, &output_config); if let Err(e) = result { if !cli.quiet { @@ -453,186 +45,3 @@ fn main() { std::process::exit(1); } } - -#[allow(clippy::too_many_lines)] // flat match dispatching each CLI subcommand — no shared logic to extract -fn run_command(command: Commands, output_config: &output::OutputConfig) -> Result<()> { - match command { - Commands::Create { - title, - author, - state, - input, - output: output_path, - } => commands::create::run(&title, &author, &state, input, &output_path, output_config), - - Commands::Validate { file } => commands::validate::run(&file, output_config), - - Commands::Inspect { - file, - blocks, - signatures, - provenance, - } => commands::inspect::run(&file, blocks, signatures, provenance, output_config), - - Commands::Status { file } => commands::status::run(&file, output_config), - - Commands::Sign { - file, - key, - name, - email, - algorithm, - output: output_path, - } => commands::sign::run( - &file, - &key, - &name, - email, - &algorithm, - output_path, - output_config, - ), - - Commands::Verify { file, key } => commands::verify::run(&file, &key, output_config), - - Commands::Extract { - file, - output: output_path, - content, - text, - asset, - all_assets, - } => commands::extract::run( - &file, - &output_path, - content, - text, - asset.as_deref(), - all_assets, - output_config, - ), - - Commands::Completions { shell } => { - generate(shell, &mut Cli::command(), "cdx", &mut io::stdout()); - Ok(()) - } - - Commands::SubmitReview { file, output } => { - commands::review::run(&file, output, output_config) - } - - Commands::Freeze { file, output } => commands::freeze::run(&file, output, output_config), - - Commands::Publish { file, output } => commands::publish::run(&file, output, output_config), - - Commands::Revert { file, output } => commands::revert::run(&file, output, output_config), - - Commands::Fork { file, output, note } => { - commands::fork::run(&file, &output, note, output_config) - } - - Commands::Prove { - file, - block_id, - block_index, - output, - } => commands::prove::run_prove(&file, block_id, block_index, output, output_config), - - Commands::VerifyProof { file, proof } => { - commands::prove::run_verify_proof(&file, &proof, output_config) - } - - Commands::ShowLineage { file } => commands::prove::run_show_lineage(&file, output_config), - - Commands::GetMetadata { file } => { - commands::metadata::run_get_metadata(&file, output_config) - } - - Commands::SetMetadata { - file, - title, - creator, - subject, - description, - publisher, - language, - rights, - output, - } => { - let params = commands::metadata::SetMetadataParams { - file, - title, - creator, - subject, - description, - publisher, - language, - rights, - output, - }; - commands::metadata::run_set_metadata(¶ms, output_config) - } - - Commands::Pack { - input, - output: output_path, - from_json, - } => commands::pack::run(&input, &output_path, from_json, output_config), - - Commands::Diff { file1, file2 } => commands::diff::run(&file1, &file2, output_config), - - Commands::ShowTimestamps { file } => { - commands::timestamp::run_show_timestamps(&file, output_config) - } - - Commands::VerifyTimestamps { file } => { - commands::timestamp::run_verify_timestamps(&file, output_config) - } - - Commands::AddTimestamp { - file, - method, - authority, - token, - time, - transaction_id, - output, - } => { - let params = commands::timestamp::AddTimestampParams { - file, - method, - authority, - token, - time, - transaction_id, - _output: output, - }; - commands::timestamp::run_add_timestamp(¶ms, output_config) - } - - Commands::TimestampAcquire { - file, - method, - server, - output, - } => commands::timestamp::run_acquire_timestamp( - &file, - method.as_deref(), - server.as_deref(), - output, - output_config, - ), - - Commands::Encrypt { - file, - password, - output, - } => commands::encrypt::run(&file, password, output, output_config), - - Commands::Decrypt { - file, - password, - output, - } => commands::decrypt::run(&file, password, output, output_config), - } -}