use crate::args::CommandTrait;
use clap::builder::{PossibleValue, TypedValueParser};
use clap::{Args, Subcommand};
use enum_dispatch::enum_dispatch;
use onerom_gen::{FileFormat, LoadAddress};
#[derive(Clone)]
struct ImageFormatParser;
impl TypedValueParser for ImageFormatParser {
type Value = FileFormat;
fn parse_ref(
&self,
cmd: &clap::Command,
arg: Option<&clap::Arg>,
value: &std::ffi::OsStr,
) -> Result<Self::Value, clap::Error> {
let invalid = || {
let arg = arg
.map(|a| a.to_string())
.unwrap_or_else(|| "--from/--to".into());
cmd.clone().error(
clap::error::ErrorKind::InvalidValue,
format!(
"invalid format '{}' for '{arg}'\n Supported values: {}",
value.to_string_lossy(),
FileFormat::supported_values()
.iter()
.map(|f| f.name())
.collect::<Vec<_>>()
.join(", "),
),
)
};
let text = value.to_str().ok_or_else(invalid)?;
FileFormat::try_from_str(text).ok_or_else(invalid)
}
fn possible_values(&self) -> Option<Box<dyn Iterator<Item = PossibleValue> + '_>> {
Some(Box::new(
FileFormat::supported_values()
.iter()
.map(|f| PossibleValue::new(f.name())),
))
}
}
fn parse_load_address(s: &str) -> Result<LoadAddress, String> {
LoadAddress::parse_str(s).map_err(|e| e.to_string())
}
#[derive(Debug, Args)]
pub struct ImageArgs {
#[command(subcommand)]
pub command: ImageCommands,
}
impl CommandTrait for ImageArgs {
fn requires_device(&self) -> bool {
self.command.requires_device()
}
}
#[enum_dispatch(CommandTrait)]
#[derive(Debug, Subcommand)]
pub enum ImageCommands {
SwapBytes(ImageSwapBytesArgs),
Deinterleave(ImageDeinterleaveArgs),
Convert(ImageConvertArgs),
}
#[derive(Debug, Args)]
pub struct ImageSwapBytesArgs {
#[arg(long, short, visible_alias = "in", value_name = "FILE")]
pub input: String,
#[arg(long, short, visible_alias = "out", value_name = "FILE")]
pub output: String,
}
impl CommandTrait for ImageSwapBytesArgs {
fn requires_device(&self) -> bool {
false
}
}
#[derive(Debug, Args)]
pub struct ImageDeinterleaveArgs {
#[arg(long, short, visible_alias = "in", value_name = "FILE")]
pub input: String,
#[arg(long, short, visible_alias = "out", value_name = "FILE")]
pub output: String,
#[arg(long, value_name = "N")]
pub offset: usize,
#[arg(long, value_name = "N")]
pub stride: usize,
#[arg(long, visible_alias = "unit", value_name = "N", default_value_t = 1)]
pub bytes: usize,
}
impl CommandTrait for ImageDeinterleaveArgs {
fn requires_device(&self) -> bool {
false
}
}
#[derive(Debug, Args)]
pub struct ImageConvertArgs {
#[arg(long, value_name = "FORMAT", value_parser = ImageFormatParser)]
pub from: FileFormat,
#[arg(long, value_name = "FORMAT", value_parser = ImageFormatParser)]
pub to: FileFormat,
#[arg(long, short, visible_alias = "in", value_name = "FILE")]
pub input: String,
#[arg(long, short, visible_alias = "out", value_name = "FILE")]
pub output: String,
#[arg(long, value_name = "ADDR", value_parser = parse_load_address)]
pub load_address: Option<LoadAddress>,
}
impl CommandTrait for ImageConvertArgs {
fn requires_device(&self) -> bool {
false
}
}