From 413b8c19155764b8a32c608e3cce98e216037629 Mon Sep 17 00:00:00 2001 From: dusk Date: Sun, 12 Jan 2025 19:30:21 +0900 Subject: [PATCH] fix(commands): make flags not match if param was in quotes --- crates/commands/src/lib.rs | 21 +++++++++++---------- crates/commands/src/string.rs | 31 +++++++++++++++++++++++++------ crates/commands/src/token.rs | 3 ++- 3 files changed, 38 insertions(+), 17 deletions(-) diff --git a/crates/commands/src/lib.rs b/crates/commands/src/lib.rs index bef98461..2f6aa4ff 100644 --- a/crates/commands/src/lib.rs +++ b/crates/commands/src/lib.rs @@ -69,7 +69,7 @@ pub fn parse_command(prefix: String, input: String) -> CommandResult { loop { let possible_tokens = local_tree.possible_tokens().cloned().collect::>(); 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, - input: SmolStr, + input: &str, current_pos: usize, ) -> Option, 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)) => { diff --git a/crates/commands/src/string.rs b/crates/commands/src/string.rs index e8a1ffb3..1ca2b2c4 100644 --- a/crates/commands/src/string.rs +++ b/crates/commands/src/string.rs @@ -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> { 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, + }) } diff --git a/crates/commands/src/token.rs b/crates/commands/src/token.rs index e705a359..bdc30013 100644 --- a/crates/commands/src/token.rs +++ b/crates/commands/src/token.rs @@ -88,7 +88,7 @@ impl TokenMatchedValue { type TryMatchResult = Option, TokenMatchError>>; impl Token { - pub fn try_match(&self, input: Option) -> 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 { + // todo: this doesnt parse all the possible ways match s.to_lowercase().as_str() { "visibility" => Ok(Self::Visibility), "name" => Ok(Self::Name),