use crate::{Error, Opt};
use core::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Lossy<'a>(pub &'a [u8]);
impl fmt::Display for Lossy<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for chunk in self.0.utf8_chunks() {
f.write_str(chunk.valid())?;
for byte in chunk.invalid() {
write!(f, "\\x{byte:02X}")?;
}
}
Ok(())
}
}
pub trait Input<'a>: getargs::Argument + 'a {
fn bytes(self) -> &'a [u8];
fn text(self) -> Result<&'a str, &'a [u8]>;
fn short(short: Self::ShortOpt) -> char;
fn split_once(self, delimiter: char) -> Option<(Self, Self)>;
}
impl<'a, 'b: 'a> Input<'a> for &'b str {
fn bytes(self) -> &'a [u8] {
self.as_bytes()
}
fn text(self) -> Result<&'a str, &'a [u8]> {
Ok(self)
}
fn short(short: char) -> char {
short
}
fn split_once(self, delimiter: char) -> Option<(Self, Self)> {
str::split_once(self, delimiter)
}
}
impl<'a, 'b: 'a> Input<'a> for &'b [u8] {
fn bytes(self) -> &'a [u8] {
self
}
fn text(self) -> Result<&'a str, &'a [u8]> {
core::str::from_utf8(self).map_err(|_| self)
}
fn short(short: u8) -> char {
if short.is_ascii() {
char::from(short)
} else {
char::REPLACEMENT_CHARACTER
}
}
fn split_once(self, delimiter: char) -> Option<(Self, Self)> {
let mut buffer = [0; 4];
let delimiter = delimiter.encode_utf8(&mut buffer).as_bytes();
let index = self
.windows(delimiter.len())
.position(|window| window == delimiter)?;
Some((&self[..index], &self[index + delimiter.len()..]))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Arg<'a, A> {
Short(char),
Long(&'a str),
Positional(A),
}
pub fn text<'a, V: Input<'a>>(value: V, name: &'static str) -> Result<&'a str, Error<'a>> {
value.text().map_err(|value| Error::InvalidUtf8 {
name: Some(name),
value,
})
}
pub fn unnamed<'a, V: Input<'a>>(value: V) -> Result<&'a str, Error<'a>> {
value
.text()
.map_err(|value| Error::InvalidUtf8 { name: None, value })
}
pub fn bytes<'a, V: Input<'a>>(value: V) -> &'a [u8] {
value.bytes()
}
pub fn arg<'a, A: Input<'a>>(argument: getargs::Arg<A>) -> Result<Arg<'a, A>, Error<'a>> {
Ok(match argument {
getargs::Arg::Short(short) => Arg::Short(A::short(short)),
getargs::Arg::Long(long) => Arg::Long(unnamed(long)?),
getargs::Arg::Positional(value) => Arg::Positional(value),
})
}
pub fn opt<'a, A: Input<'a>>(option: getargs::Opt<A>) -> Result<Opt<'a>, Error<'a>> {
Ok(match option {
getargs::Opt::Short(short) => Opt::Short(A::short(short)),
getargs::Opt::Long(long) => Opt::Long(unnamed(long)?),
})
}
pub fn unexpected_argument<'a, V: Input<'a>>(value: V) -> Error<'a> {
match value.text() {
Ok(value) => Error::UnexpectedArgument(value),
Err(value) => Error::InvalidUtf8 { name: None, value },
}
}
pub fn unknown_subcommand<'a, V: Input<'a>>(value: V, names: &[&[&'static str]]) -> Error<'a> {
match value.text() {
Ok(name) => Error::UnknownSubcommand {
name,
suggestion: crate::suggest::suggest(name, names),
},
Err(value) => Error::InvalidUtf8 { name: None, value },
}
}