fix(commands): make flags not match if param was in quotes

This commit is contained in:
dusk 2025-01-12 19:30:21 +09:00
parent a21210f3ce
commit 413b8c1915
No known key found for this signature in database
3 changed files with 38 additions and 17 deletions

View file

@ -69,7 +69,7 @@ pub fn parse_command(prefix: String, input: String) -> CommandResult {
loop {
let possible_tokens = local_tree.possible_tokens().cloned().collect::<Vec<_>>();
println!("possible: {:?}", possible_tokens);
let next = next_token(possible_tokens.clone(), input.clone(), current_pos);
let next = next_token(possible_tokens.clone(), &input, current_pos);
println!("next: {:?}", next);
match next {
Some(Ok((found_token, arg, new_pos))) => {
@ -157,39 +157,40 @@ pub fn parse_command(prefix: String, input: String) -> CommandResult {
/// - error when matching
fn next_token(
possible_tokens: Vec<Token>,
input: SmolStr,
input: &str,
current_pos: usize,
) -> Option<Result<(Token, Option<TokenMatchedValue>, usize), (Token, TokenMatchError)>> {
// get next parameter, matching quotes
let param = crate::string::next_param(input.clone(), current_pos);
println!("matched: {param:?}\n---");
let matched = crate::string::next_param(&input, current_pos);
println!("matched: {matched:?}\n---");
// try checking if this is a flag
// todo!: this breaks full text matching if the full text starts with a flag
// (but that's kinda already broken anyway)
if let Some((value, new_pos)) = param.clone()
&& value.starts_with('-')
if let Some(param) = matched.as_ref()
&& param.in_quotes.not()
&& param.value.starts_with('-')
{
return Some(Ok((
Token::Flag,
Some(TokenMatchedValue {
raw: value,
raw: param.value.into(),
param: None,
}),
new_pos,
param.next_pos,
)));
}
// iterate over tokens and run try_match
for token in possible_tokens {
// for FullString just send the whole string
let input_to_match = param.clone().map(|v| v.0);
let input_to_match = matched.as_ref().map(|v| v.value);
match token.try_match(input_to_match) {
Some(Ok(value)) => {
return Some(Ok((
token,
value,
param.map(|v| v.1).unwrap_or(current_pos),
matched.map(|v| v.next_pos).unwrap_or(current_pos),
)))
}
Some(Err(err)) => {

View file

@ -42,17 +42,24 @@ lazy_static::lazy_static! {
};
}
#[derive(Debug)]
pub(super) struct MatchedParam<'a> {
pub(super) value: &'a str,
pub(super) next_pos: usize,
pub(super) in_quotes: bool,
}
// very very simple quote matching
// quotes need to be at start/end of words, and are ignored if a closing quote is not present
// WTB POSIX quoting: https://pubs.opengroup.org/onlinepubs/9799919799/utilities/V3_chap02.html
pub(super) fn next_param(input: SmolStr, current_pos: usize) -> Option<(SmolStr, usize)> {
pub(super) fn next_param<'a>(input: &'a str, current_pos: usize) -> Option<MatchedParam<'a>> {
if input.len() == current_pos {
return None;
}
let leading_whitespace_count =
input[..current_pos].len() - input[..current_pos].trim_start().len();
let substr_to_match: SmolStr = input[current_pos + leading_whitespace_count..].into();
let substr_to_match = &input[current_pos + leading_whitespace_count..];
println!("stuff: {input} {current_pos} {leading_whitespace_count}");
println!("to match: {substr_to_match}");
@ -68,20 +75,32 @@ pub(super) fn next_param(input: SmolStr, current_pos: usize) -> Option<(SmolStr,
.is_whitespace()
{
// return quoted string, without quotes
return Some((substr_to_match[1..pos - 1].into(), current_pos + pos + 1));
return Some(MatchedParam {
value: &substr_to_match[1..pos - 1],
next_pos: current_pos + pos + 1,
in_quotes: true,
});
}
}
}
}
// find next whitespace character
for (pos, char) in substr_to_match.clone().char_indices() {
for (pos, char) in substr_to_match.char_indices() {
if char.is_whitespace() {
return Some((substr_to_match[..pos].into(), current_pos + pos + 1));
return Some(MatchedParam {
value: &substr_to_match[..pos],
next_pos: current_pos + pos + 1,
in_quotes: false,
});
}
}
// if we're here, we went to EOF and didn't match any whitespace
// so we return the whole string
Some((substr_to_match.clone(), current_pos + substr_to_match.len()))
Some(MatchedParam {
value: substr_to_match,
next_pos: current_pos + substr_to_match.len(),
in_quotes: false,
})
}

View file

@ -88,7 +88,7 @@ impl TokenMatchedValue {
type TryMatchResult = Option<Result<Option<TokenMatchedValue>, TokenMatchError>>;
impl Token {
pub fn try_match(&self, input: Option<SmolStr>) -> TryMatchResult {
pub fn try_match(&self, input: Option<&str>) -> TryMatchResult {
let input = match input {
Some(input) => input,
None => {
@ -305,6 +305,7 @@ impl FromStr for MemberPrivacyTarget {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
// todo: this doesnt parse all the possible ways
match s.to_lowercase().as_str() {
"visibility" => Ok(Self::Visibility),
"name" => Ok(Self::Name),