use crate::command_options::{
Occurrence, OptionError, OptionErrorKind, OptionSpec, OptionValue, ParseMode, ParsedOptions,
};
use crate::command_options_value::{Spelling, take_value};
use std::ffi::OsString;
fn find_short(table: &[OptionSpec], letter: u8) -> Option<OptionSpec> {
for spec in table {
if spec.short == Some(letter) {
return Some(*spec);
}
}
return None;
}
fn spells_long_option(table: &[OptionSpec], cluster: &[u8]) -> bool {
if cluster.len() < 3 {
return false;
}
if cluster.starts_with(b"no-") {
return true;
}
for spec in table {
if let Some(long) = spec.long {
if long.as_bytes().starts_with(cluster) {
return true;
}
}
}
return false;
}
fn unknown_letter(
table: &[OptionSpec],
token: &[u8],
position: usize,
index: usize,
mode: ParseMode,
parsed: &mut ParsedOptions,
) -> Result<bool, OptionError> {
if position == 1 && spells_long_option(table, &token[1..]) {
return Err(OptionError {
kind: OptionErrorKind::SingleDashLongOption,
token: index,
});
}
if token[position] == b'h' {
return Err(OptionError {
kind: OptionErrorKind::HelpRequested,
token: index,
});
}
if mode.keep_unknown {
parsed.unknown.push(index);
return Ok(false);
}
return Err(OptionError {
kind: OptionErrorKind::UnknownOption,
token: index,
});
}
pub(crate) fn scan_short_cluster(
arguments: &[OsString],
index: usize,
table: &[OptionSpec],
mode: ParseMode,
parsed: &mut ParsedOptions,
) -> Result<bool, OptionError> {
let token: &[u8] = arguments[index].as_encoded_bytes();
let mut position: usize = 1;
while position < token.len() {
let found: Option<OptionSpec> = find_short(table, token[position]);
let spec: OptionSpec = if let Some(declared) = found {
declared
} else {
return unknown_letter(table, token, position, index, mode, parsed);
};
position += 1;
let attached: Option<usize> = if position < token.len() {
Some(position)
} else {
None
};
let value: Option<OptionValue> =
take_value(arguments, index, spec.arity, attached, Spelling::Short)?;
parsed.occurrences.push(Occurrence {
id: spec.id,
negated: false,
value,
token: index,
});
if let Some(taken) = value {
return Ok(matches!(taken, OptionValue::Detached { .. }));
}
if position == 2 && spells_long_option(table, &token[1..]) {
return Err(OptionError {
kind: OptionErrorKind::SingleDashLongOption,
token: index,
});
}
}
return Ok(false);
}
#[cfg(test)]
#[path = "command_options_short_tests.rs"]
mod tests;