use crate::FrameId;
use clap::ArgGroup;
use clap::Args;
use clap::Parser;
use clap::Subcommand;
use clap::ValueEnum;
use clap_complete::Shell;
use std::path::PathBuf;
use tokio::fs::read_to_string;
#[derive(Clone, Debug, PartialEq, Subcommand)]
pub enum Command {
Complete {
#[clap(flatten)]
options: CompleteOptions,
#[clap(value_enum)]
shell: Shell,
},
Get {
#[clap(flatten)]
frame: Frame,
#[clap(required = true)]
input: PathBuf,
#[clap(flatten)]
options: GetOptions,
},
Print {
#[clap(flatten)]
frames: Frames,
#[clap(required = true)]
inputs: Vec<PathBuf>,
#[clap(flatten)]
options: PrintOptions,
},
Remove {
#[clap(flatten)]
frames: Frames,
#[clap(required = true)]
inputs: Vec<PathBuf>,
},
Set {
#[clap(flatten)]
frame: Frame,
#[clap(flatten)]
frame_content: FrameContent,
#[clap(required = true)]
inputs: Vec<PathBuf>,
},
}
#[derive(Clone, Debug, PartialEq, ValueEnum)]
enum FrameAlias {
Album,
Artist,
Date,
Disc,
Genre,
Picture,
Subtitle,
Title,
Track,
}
#[derive(Clone, Debug, PartialEq, ValueEnum)]
enum FrameCompilation {
All,
Basic,
NotBasic,
}
#[derive(Clone, Debug, Parser, PartialEq)]
pub struct Arguments {
#[clap(subcommand)]
pub command: Command,
}
#[derive(Args, Clone, Debug, PartialEq)]
pub struct CompleteOptions {
#[clap(long, require_equals = true, short, value_name = "VALUE")]
pub output: Option<PathBuf>,
}
#[derive(Args, Clone, Debug, PartialEq)]
#[clap(group = ArgGroup::new("FrameAG").required(true))]
pub struct Frame {
#[clap(
group = "FrameAG",
long,
require_equals = true,
short,
value_enum,
value_name = "VALUE"
)]
alias: Option<FrameAlias>,
#[clap(
group = "FrameAG",
long,
require_equals = true,
short,
value_enum,
value_name = "VALUE"
)]
id: Option<FrameId>,
}
#[derive(Args, Clone, Debug, PartialEq)]
#[clap(group = ArgGroup::new("FrameContentAG").required(true))]
pub struct FrameContent {
#[clap(
group = "FrameContentAG",
long,
require_equals = true,
short,
value_enum,
value_name = "VALUE"
)]
path: Option<PathBuf>,
#[clap(
group = "FrameContentAG",
long,
require_equals = true,
short,
value_enum,
value_name = "VALUE"
)]
text: Option<String>,
}
#[derive(Args, Clone, Debug, PartialEq)]
#[clap(group = ArgGroup::new("FramesAG").multiple(true).required(true))]
pub struct Frames {
#[clap(
group = "FramesAG",
long,
require_equals = true,
short,
value_delimiter = ',',
value_enum,
value_name = "VALUES"
)]
aliases: Option<Vec<FrameAlias>>,
#[clap(
group = "FramesAG",
long,
require_equals = true,
short,
value_enum,
value_name = "VALUE"
)]
compilation: Option<FrameCompilation>,
#[clap(
group = "FramesAG",
long,
require_equals = true,
short,
value_delimiter = ',',
value_enum,
value_name = "VALUES"
)]
ids: Option<Vec<FrameId>>,
}
#[derive(Args, Clone, Debug, PartialEq)]
pub struct GetOptions {
#[clap(long, require_equals = true, short, value_name = "VALUE")]
pub output: Option<PathBuf>,
}
#[derive(Args, Clone, Debug, PartialEq)]
pub struct PrintOptions {
#[clap(long, short)]
pub existing: bool,
}
impl Frame {
pub fn into_frame_id(self) -> FrameId {
if let Some(i) = self.id {
return i;
}
self.alias.unwrap().as_frame_id()
}
}
impl FrameAlias {
fn as_frame_id(&self) -> FrameId {
match self {
Self::Album => FrameId::Talb,
Self::Artist => FrameId::Tpe1,
Self::Date => FrameId::Tdrc,
Self::Disc => FrameId::Tpos,
Self::Genre => FrameId::Tcon,
Self::Picture => FrameId::Apic,
Self::Subtitle => FrameId::Tit3,
Self::Title => FrameId::Tit2,
Self::Track => FrameId::Trck,
}
}
}
impl FrameCompilation {
fn as_frame_id_slice(&self) -> &[FrameId] {
match self {
Self::All => &[
FrameId::Aenc,
FrameId::Apic,
FrameId::Aspi,
FrameId::Comm,
FrameId::Comr,
FrameId::Encr,
FrameId::Equ2,
FrameId::Etco,
FrameId::Geob,
FrameId::Grid,
FrameId::Link,
FrameId::Mcdi,
FrameId::Mllt,
FrameId::Owne,
FrameId::Priv,
FrameId::Pcnt,
FrameId::Popm,
FrameId::Poss,
FrameId::Rbuf,
FrameId::Rva2,
FrameId::Rvrb,
FrameId::Seek,
FrameId::Sign,
FrameId::Sylt,
FrameId::Sytc,
FrameId::Talb,
FrameId::Tbpm,
FrameId::Tcom,
FrameId::Tcon,
FrameId::Tcop,
FrameId::Tden,
FrameId::Tdly,
FrameId::Tdor,
FrameId::Tdrc,
FrameId::Tdrl,
FrameId::Tdtg,
FrameId::Tenc,
FrameId::Text,
FrameId::Tflt,
FrameId::Tipl,
FrameId::Tit1,
FrameId::Tit2,
FrameId::Tit3,
FrameId::Tkey,
FrameId::Tlan,
FrameId::Tlen,
FrameId::Tmcl,
FrameId::Tmed,
FrameId::Tmoo,
FrameId::Toal,
FrameId::Tofn,
FrameId::Toly,
FrameId::Tope,
FrameId::Town,
FrameId::Tpe1,
FrameId::Tpe2,
FrameId::Tpe3,
FrameId::Tpe4,
FrameId::Tpos,
FrameId::Tpro,
FrameId::Tpub,
FrameId::Trck,
FrameId::Trsn,
FrameId::Trso,
FrameId::Tsoa,
FrameId::Tsop,
FrameId::Tsot,
FrameId::Tsrc,
FrameId::Tsse,
FrameId::Tsst,
FrameId::Txxx,
FrameId::Ufid,
FrameId::User,
FrameId::Uslt,
FrameId::Wcom,
FrameId::Wcop,
FrameId::Woaf,
FrameId::Woar,
FrameId::Woas,
FrameId::Wors,
FrameId::Wpay,
FrameId::Wpub,
FrameId::Wxxx,
],
Self::Basic => &[
FrameId::Apic,
FrameId::Talb,
FrameId::Tcon,
FrameId::Tdrc,
FrameId::Tit2,
FrameId::Tit3,
FrameId::Tpe1,
FrameId::Tpos,
FrameId::Trck,
],
Self::NotBasic => &[
FrameId::Aenc,
FrameId::Aspi,
FrameId::Comm,
FrameId::Comr,
FrameId::Encr,
FrameId::Equ2,
FrameId::Etco,
FrameId::Geob,
FrameId::Grid,
FrameId::Link,
FrameId::Mcdi,
FrameId::Mllt,
FrameId::Owne,
FrameId::Priv,
FrameId::Pcnt,
FrameId::Popm,
FrameId::Poss,
FrameId::Rbuf,
FrameId::Rva2,
FrameId::Rvrb,
FrameId::Seek,
FrameId::Sign,
FrameId::Sylt,
FrameId::Sytc,
FrameId::Tbpm,
FrameId::Tcom,
FrameId::Tcop,
FrameId::Tden,
FrameId::Tdly,
FrameId::Tdor,
FrameId::Tdrl,
FrameId::Tdtg,
FrameId::Tenc,
FrameId::Text,
FrameId::Tflt,
FrameId::Tipl,
FrameId::Tit1,
FrameId::Tkey,
FrameId::Tlan,
FrameId::Tlen,
FrameId::Tmcl,
FrameId::Tmed,
FrameId::Tmoo,
FrameId::Toal,
FrameId::Tofn,
FrameId::Toly,
FrameId::Tope,
FrameId::Town,
FrameId::Tpe2,
FrameId::Tpe3,
FrameId::Tpe4,
FrameId::Tpro,
FrameId::Tpub,
FrameId::Trsn,
FrameId::Trso,
FrameId::Tsoa,
FrameId::Tsop,
FrameId::Tsot,
FrameId::Tsrc,
FrameId::Tsse,
FrameId::Tsst,
FrameId::Txxx,
FrameId::Ufid,
FrameId::User,
FrameId::Uslt,
FrameId::Wcom,
FrameId::Wcop,
FrameId::Woaf,
FrameId::Woar,
FrameId::Woas,
FrameId::Wors,
FrameId::Wpay,
FrameId::Wpub,
FrameId::Wxxx,
],
}
}
}
impl FrameContent {
pub async fn try_into_string(self) -> Result<String, String> {
if let Some(path) = self.path {
return Ok(read_to_string(path).await.map_err(|e| e.to_string())?);
}
Ok(self.text.unwrap())
}
}
impl Frames {
pub fn into_frame_id_vec(self) -> Vec<FrameId> {
let mut ids = Vec::new();
if let Some(i) = self.ids {
ids.extend(i);
}
if let Some(aliases) = self.aliases {
ids.extend(
aliases
.iter()
.map(|a| a.as_frame_id())
.filter(|i| !ids.contains(i))
.collect::<Vec<_>>(),
);
}
if let Some(compilation) = self.compilation {
ids.extend(
compilation
.as_frame_id_slice()
.iter()
.map(|i| i.to_owned())
.filter(|i| !ids.contains(i))
.collect::<Vec<_>>(),
);
}
ids
}
}