use std::fmt::{self, Display, Formatter};
use std::num::ParseIntError;
use std::path::PathBuf;
use std::str::FromStr;
use chrono::{DateTime, Local};
use clap::{ArgAction, Args, Parser, Subcommand, ValueEnum};
use enum_dispatch::enum_dispatch;
use strum_macros::{Display as EnumDisplay, EnumString};
use crate::ocfl::{ErrorCode, VersionNum, WarnCode};
#[derive(Debug, Parser)]
#[command(name = "rocfl", author = "Peter Winckles <pwinckles@pm.me>", version)]
pub struct RocflArgs {
#[arg(short, long, value_name = "NAME")]
pub name: Option<String>,
#[arg(short, long, value_name = "ROOT_PATH")]
pub root: Option<String>,
#[arg(short, long, value_name = "STAGING_PATH")]
pub staging_root: Option<String>,
#[arg(short = 'R', long, value_name = "REGION")]
pub region: Option<String>,
#[arg(short, long, value_name = "BUCKET")]
pub bucket: Option<String>,
#[arg(short, long, value_name = "ENDPOINT")]
pub endpoint: Option<String>,
#[arg(short, long, value_name = "PROFILE")]
pub profile: Option<String>,
#[arg(short, long)]
pub quiet: bool,
#[arg(short = 'v', long)]
pub verbose: bool,
#[arg(short = 'S', long)]
pub no_styles: bool,
#[command(subcommand)]
pub command: Command,
}
#[enum_dispatch(Cmd)]
#[derive(Subcommand, Debug)]
pub enum Command {
#[command(name = "config")]
Config(ConfigCmd),
#[command(name = "ls")]
List(ListCmd),
#[command(name = "log")]
Log(LogCmd),
#[command(name = "show")]
Show(ShowCmd),
#[command(name = "diff")]
Diff(DiffCmd),
#[command(name = "cat")]
Cat(CatCmd),
#[command(name = "init")]
Init(InitCmd),
#[command(name = "new")]
New(NewCmd),
#[command(name = "cp")]
Copy(CopyCmd),
#[command(name = "mv")]
Move(MoveCmd),
#[command(name = "rm")]
Remove(RemoveCmd),
#[command(name = "reset")]
Reset(ResetCmd),
#[command(name = "commit")]
Commit(CommitCmd),
#[command(name = "status")]
Status(StatusCmd),
#[command(name = "purge")]
Purge(PurgeCmd),
#[command(name = "validate")]
Validate(ValidateCmd),
#[command(name = "info")]
Info(InfoCmd),
#[command(name = "upgrade")]
Upgrade(UpgradeCmd),
}
#[derive(Args, Debug)]
pub struct ConfigCmd {}
#[derive(Args, Debug)]
pub struct ListCmd {
#[arg(short = 'D', long)]
pub logical_dirs: bool,
#[arg(short, long)]
pub long: bool,
#[arg(short, long)]
pub physical: bool,
#[arg(short, long)]
pub digest: bool,
#[arg(short = 'H', long)]
pub header: bool,
#[arg(short, long)]
pub tsv: bool,
#[arg(short = 'S', long, conflicts_with = "version")]
pub staged: bool,
#[arg(short, long, value_name = "VERSION")]
pub version: Option<VersionNum>,
#[arg(
value_enum,
short,
long,
value_name = "FIELD",
default_value = "default",
ignore_case = true
)]
pub sort: Field,
#[arg(short, long)]
pub reverse: bool,
#[arg(short, long)]
pub objects: bool,
#[arg(value_name = "OBJ_ID")]
pub object_id: Option<String>,
#[arg(value_name = "PATH")]
pub path: Option<String>,
}
#[derive(Args, Debug)]
pub struct LogCmd {
#[arg(short, long)]
pub compact: bool,
#[arg(short, long)]
pub header: bool,
#[arg(short, long)]
pub tsv: bool,
#[arg(short, long)]
pub reverse: bool,
#[arg(short, long, value_name = "NUM", default_value_t)]
pub num: Num,
#[arg(value_name = "OBJ_ID")]
pub object_id: String,
#[arg(value_name = "PATH")]
pub path: Option<String>,
}
#[derive(Args, Debug)]
pub struct ShowCmd {
#[arg(short = 'S', long, conflicts_with = "version")]
pub staged: bool,
#[arg(short, long)]
pub minimal: bool,
#[arg(value_name = "OBJ_ID")]
pub object_id: String,
#[arg(value_name = "VERSION")]
pub version: Option<VersionNum>,
}
#[derive(Args, Debug)]
pub struct DiffCmd {
#[arg(value_name = "OBJ_ID")]
pub object_id: String,
#[arg(value_name = "LEFT_VERSION")]
pub left: VersionNum,
#[arg(value_name = "RIGHT_VERSION")]
pub right: VersionNum,
}
#[derive(Args, Debug)]
pub struct CatCmd {
#[arg(short = 'S', long, conflicts_with = "version")]
pub staged: bool,
#[arg(short, long, value_name = "VERSION")]
pub version: Option<VersionNum>,
#[arg(value_name = "OBJ_ID")]
pub object_id: String,
#[arg(value_name = "PATH")]
pub path: String,
}
#[derive(Args, Debug)]
pub struct InitCmd {
#[arg(
value_enum,
short = 'v',
long,
value_name = "SPEC_VERSION",
default_value = "1.1",
ignore_case = true
)]
pub spec_version: SpecVersion,
#[arg(short, long, value_name = "LAYOUT_CONFIG")]
pub config_file: Option<PathBuf>,
#[arg(
value_enum,
short,
long,
value_name = "LAYOUT",
default_value = "0004-hashed-n-tuple-storage-layout",
ignore_case = true
)]
pub layout: Layout,
}
#[derive(Args, Debug)]
pub struct UpgradeCmd {
#[arg(
value_enum,
short = 'v',
long,
value_name = "SPEC_VERSION",
ignore_case = true
)]
pub spec_version: SpecVersion,
#[arg(short, long)]
pub pretty_print: bool,
#[arg(short = 'n', long, value_name = "NAME")]
pub user_name: Option<String>,
#[arg(short = 'a', long, value_name = "ADDRESS")]
pub user_address: Option<String>,
#[arg(short, long, value_name = "MESSAGE")]
pub message: Option<String>,
#[arg(short, long, value_name = "TIMESTAMP")]
pub created: Option<DateTime<Local>>,
#[arg(value_name = "OBJ_ID")]
pub object_id: Option<String>,
}
#[derive(Args, Debug)]
pub struct NewCmd {
#[arg(
value_enum,
short = 'v',
long,
value_name = "SPEC_VERSION",
ignore_case = true
)]
pub spec_version: Option<SpecVersion>,
#[arg(
value_enum,
short,
long,
value_name = "ALGORITHM",
default_value = "sha512",
ignore_case = true
)]
pub digest_algorithm: DigestAlgorithm,
#[arg(short, long, value_name = "PATH", default_value = "content")]
pub content_directory: String,
#[arg(short, long, value_name = "WIDTH", default_value = "0")]
pub zero_padding: u32,
#[arg(value_name = "OBJ_ID")]
pub object_id: String,
}
#[derive(Args, Debug)]
pub struct CopyCmd {
#[arg(short, long)]
pub recursive: bool,
#[arg(short, long)]
pub internal: bool,
#[arg(short, long, value_name = "VERSION", requires = "internal")]
pub version: Option<VersionNum>,
#[arg(value_name = "OBJ_ID")]
pub object_id: String,
#[arg(value_name = "SRC", required = true)]
pub source: Vec<String>,
#[arg(value_name = "DST", last = true)]
pub destination: String,
}
#[derive(Args, Debug)]
pub struct MoveCmd {
#[arg(short, long)]
pub internal: bool,
#[arg(value_name = "OBJ_ID")]
pub object_id: String,
#[arg(value_name = "SRC", required = true)]
pub source: Vec<String>,
#[arg(value_name = "DST", last = true)]
pub destination: String,
}
#[derive(Args, Debug)]
pub struct RemoveCmd {
#[arg(short, long)]
pub recursive: bool,
#[arg(value_name = "OBJ_ID")]
pub object_id: String,
#[arg(value_name = "PATH", required = true)]
pub paths: Vec<String>,
}
#[derive(Args, Debug)]
pub struct CommitCmd {
#[arg(short, long)]
pub pretty_print: bool,
#[arg(short = 'n', long, value_name = "NAME")]
pub user_name: Option<String>,
#[arg(short = 'a', long, value_name = "ADDRESS")]
pub user_address: Option<String>,
#[arg(short, long, value_name = "MESSAGE")]
pub message: Option<String>,
#[arg(short, long, value_name = "TIMESTAMP")]
pub created: Option<DateTime<Local>>,
#[arg(short = 'r', long, value_name = "OBJ_ROOT")]
pub object_root: Option<String>,
#[arg(value_name = "OBJ_ID")]
pub object_id: String,
}
#[derive(Args, Debug)]
pub struct ResetCmd {
#[arg(short, long)]
pub recursive: bool,
#[arg(value_name = "OBJ_ID")]
pub object_id: String,
#[arg(value_name = "PATH")]
pub paths: Vec<String>,
}
#[derive(Args, Debug)]
pub struct StatusCmd {
#[arg(value_name = "OBJ_ID")]
pub object_id: Option<String>,
}
#[derive(Args, Debug)]
pub struct PurgeCmd {
#[arg(short, long)]
pub force: bool,
#[arg(value_name = "OBJ_ID")]
pub object_id: String,
}
#[derive(Args, Debug)]
pub struct ValidateCmd {
#[arg(short, long)]
pub paths: bool,
#[arg(short, long)]
pub no_fixity_check: bool,
#[arg(
value_enum,
short,
long,
value_name = "LEVEL",
default_value = "info",
ignore_case = true
)]
pub level: Level,
#[arg(
short = 'w',
long,
value_name = "CODE",
action = ArgAction::Append,
num_args = 1,
ignore_case = true
)]
pub suppress_warning: Vec<WarnCode>,
#[arg(
short = 'e',
long,
value_name = "CODE",
action = ArgAction::Append,
num_args = 1,
ignore_case = true
)]
pub suppress_error: Vec<ErrorCode>,
#[arg(value_name = "OBJ_ID/PATH")]
pub object_ids: Vec<String>,
}
#[derive(Args, Debug)]
pub struct InfoCmd {
#[arg(short = 'S', long)]
pub staged: bool,
#[arg(value_name = "OBJ_ID")]
pub object_id: Option<String>,
}
#[derive(Debug, Copy, Clone)]
pub struct Num(pub usize);
#[derive(ValueEnum, Debug, Clone, Copy, Eq, PartialEq)]
pub enum Field {
Default,
Name,
Version,
Updated,
Physical,
Digest,
None,
}
#[derive(ValueEnum, Debug, Clone, Copy, EnumString, EnumDisplay)]
pub enum Layout {
#[strum(serialize = "None", serialize = "none")]
#[value(name = "none")]
None,
#[strum(serialize = "0002-flat-direct-storage-layout")]
#[value(name = "0002-flat-direct-storage-layout")]
FlatDirect,
#[strum(serialize = "0004-hashed-n-tuple-storage-layout")]
#[value(name = "0004-hashed-n-tuple-storage-layout")]
HashedNTuple,
#[strum(serialize = "0003-hash-and-id-n-tuple-storage-layout")]
#[value(name = "0003-hash-and-id-n-tuple-storage-layout")]
HashedNTupleObjectId,
#[strum(serialize = "0006-flat-omit-prefix-storage-layout")]
#[value(name = "0006-flat-omit-prefix-storage-layout")]
FlatOmitPrefix,
#[strum(serialize = "0007-n-tuple-omit-prefix-storage-layout")]
#[value(name = "0007-n-tuple-omit-prefix-storage-layout")]
NTupleOmitPrefix,
}
#[derive(ValueEnum, Debug, Clone, Copy)]
pub enum DigestAlgorithm {
Sha256,
Sha512,
}
#[derive(ValueEnum, Debug, Clone, Copy, Eq, PartialEq)]
pub enum Level {
Info,
Warn,
Error,
}
#[derive(ValueEnum, Debug, Clone, Copy, EnumString, EnumDisplay)]
pub enum SpecVersion {
#[strum(serialize = "1.0")]
#[value(name = "1.0")]
Ocfl1_0,
#[strum(serialize = "1.1")]
#[value(name = "1.1")]
Ocfl1_1,
}
impl Default for Num {
fn default() -> Self {
Self(usize::MAX)
}
}
impl FromStr for Num {
type Err = ParseIntError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(Num(usize::from_str(s)?))
}
}
impl Display for Num {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}