pub struct Getopt {
args: Vec<String>,
optstring: String,
pub optind: usize,
pub optopt: char,
pub optarg: Option<String>,
pub opterr: bool,
nextchar_idx: usize,
}
impl Getopt {
pub fn new(optstring: &str, args: Vec<String>) -> Self {
Self {
args,
optstring: optstring.to_string(),
optind: 1,
optopt: '?',
optarg: None,
opterr: true,
nextchar_idx: 0,
}
}
pub fn next(&mut self) -> Option<char> {
self.optarg = None;
if self.optind >= self.args.len() {
return None;
}
let arg = &self.args[self.optind];
if self.nextchar_idx == 0 {
if !arg.starts_with('-') || arg == "-" {
return None;
}
if arg == "--" {
self.optind += 1;
return None;
}
self.nextchar_idx = 1;
}
let c = arg.chars().nth(self.nextchar_idx).unwrap();
self.optopt = c;
self.nextchar_idx += 1;
match self.optstring.find(c) {
None => {
if self.nextchar_idx >= arg.len() {
self.optind += 1;
self.nextchar_idx = 0;
}
if self.opterr && !self.optstring.starts_with(':') {
eprintln!("{}: illegal option -- {}", self.args[0], c);
}
Some('?')
}
Some(idx) => {
if self.optstring.as_bytes().get(idx + 1) == Some(&b':') {
if self.nextchar_idx < arg.len() {
self.optarg = Some(arg[self.nextchar_idx..].to_string());
self.optind += 1;
self.nextchar_idx = 0;
} else {
self.optind += 1;
if self.optind < self.args.len() {
self.optarg = Some(self.args[self.optind].clone());
self.optind += 1;
} else {
self.nextchar_idx = 0;
if self.optstring.starts_with(':') {
return Some(':');
} else {
if self.opterr {
eprintln!("{}: option requires an argument -- {}", self.args[0], c);
}
return Some('?');
}
}
}
self.nextchar_idx = 0;
} else {
if self.nextchar_idx >= arg.len() {
self.optind += 1;
self.nextchar_idx = 0;
}
}
Some(c)
}
}
}
}
#[cfg(test)]
mod tests;