use crate::error::{OjphError, Result};
pub struct CliInterpreter {
args: Vec<String>,
available: Vec<bool>,
}
impl CliInterpreter {
pub fn init(args: Vec<String>) -> Self {
let len = args.len();
Self {
args,
available: vec![true; len],
}
}
pub fn find_argument(&self, name: &str) -> Option<usize> {
self.args
.iter()
.enumerate()
.find(|(i, a)| *a == name && self.available[*i])
.map(|(i, _)| i)
}
pub fn get_next_value(&mut self, index: usize) -> Result<&str> {
let next = index + 1;
if next >= self.args.len() || !self.available[next] {
return Err(OjphError::InvalidParam(format!(
"expected value after argument '{}'",
self.args.get(index).unwrap_or(&String::new()),
)));
}
self.available[index] = false;
self.available[next] = false;
Ok(&self.args[next])
}
pub fn release_argument(&mut self, index: usize) {
if index < self.available.len() {
self.available[index] = false;
}
}
pub fn is_exhausted(&self) -> bool {
self.available.iter().all(|&a| !a)
}
pub fn first_unconsumed(&self) -> Option<&str> {
self.args
.iter()
.enumerate()
.find(|(i, _)| self.available[*i])
.map(|(_, a)| a.as_str())
}
pub fn reinterpret_i32(&mut self, index: usize) -> Result<i32> {
let s = self.get_next_value(index)?;
s.parse::<i32>()
.map_err(|_| OjphError::InvalidParam(format!("cannot parse '{}' as i32", s)))
}
pub fn reinterpret_u32(&mut self, index: usize) -> Result<u32> {
let s = self.get_next_value(index)?;
s.parse::<u32>()
.map_err(|_| OjphError::InvalidParam(format!("cannot parse '{}' as u32", s)))
}
pub fn reinterpret_f32(&mut self, index: usize) -> Result<f32> {
let s = self.get_next_value(index)?;
s.parse::<f32>()
.map_err(|_| OjphError::InvalidParam(format!("cannot parse '{}' as f32", s)))
}
pub fn reinterpret_bool(&mut self, index: usize) -> Result<bool> {
let s = self.get_next_value(index)?;
match s.to_ascii_lowercase().as_str() {
"true" | "1" | "yes" => Ok(true),
"false" | "0" | "no" => Ok(false),
_ => Err(OjphError::InvalidParam(format!(
"cannot parse '{}' as bool",
s
))),
}
}
pub fn reinterpret_string(&mut self, index: usize) -> Result<String> {
self.get_next_value(index).map(|s| s.to_owned())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn find_and_consume() {
let args = vec![
"-i".to_string(),
"input.j2c".to_string(),
"-o".to_string(),
"output.ppm".to_string(),
];
let mut cli = CliInterpreter::init(args);
let idx = cli.find_argument("-i").unwrap();
let val = cli.reinterpret_string(idx).unwrap();
assert_eq!(val, "input.j2c");
let idx = cli.find_argument("-o").unwrap();
let val = cli.reinterpret_string(idx).unwrap();
assert_eq!(val, "output.ppm");
assert!(cli.is_exhausted());
}
}