use sdk_raw::commands::CommandLine;
use std::ffi::CStr;
use std::str::FromStr;
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
pub enum ArgError {
#[error("missing argument {position}")]
Missing {
position: usize,
},
#[error("argument {position} is not valid UTF-8")]
NotUtf8 {
position: usize,
},
#[error("argument {position} (`{value}`) is not a valid {expected}")]
Invalid {
position: usize,
value: String,
expected: &'static str,
},
}
#[doc(alias("CCommand"))]
#[derive(Clone, Copy)]
pub struct CommandArgs<'d> {
line: &'d CommandLine,
}
impl<'d> CommandArgs<'d> {
pub(crate) const fn new(line: &'d CommandLine) -> Self {
Self { line }
}
#[doc(alias("GetCommandString"))]
pub fn command_line(self) -> &'d CStr {
self.line.line()
}
#[doc(alias("Arg"))]
pub fn get(self, index: usize) -> Option<&'d CStr> {
self.line.arg(index.checked_add(1)?)
}
pub fn get_str(self, index: usize) -> Result<&'d str, ArgError> {
let position = index.saturating_add(1);
self.get(index)
.ok_or(ArgError::Missing { position })?
.to_str()
.map_err(|_| ArgError::NotUtf8 { position })
}
pub fn is_empty(self) -> bool {
self.len() == 0
}
pub fn iter(self) -> impl Iterator<Item = &'d CStr> + 'd {
(0..self.len()).filter_map(move |index| self.get(index))
}
#[doc(alias("ArgC"))]
pub fn len(self) -> usize {
self.line.argc() - 1
}
pub fn name(self) -> &'d CStr {
self.line.arg(0).unwrap_or_default()
}
pub fn parse<T: FromStr>(self, index: usize) -> Result<T, ArgError> {
let value = self.get_str(index)?;
value.parse().map_err(|_| ArgError::Invalid {
position: index + 1,
value: value.to_owned(),
expected: std::any::type_name::<T>(),
})
}
#[doc(alias("ArgS"))]
pub fn raw_args(self) -> &'d CStr {
self.line.raw_args()
}
}
impl std::fmt::Debug for CommandArgs<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("CommandArgs")
.field("name", &self.name())
.field("args", &self.iter().collect::<Vec<_>>())
.finish()
}
}