mirror of
https://github.com/PluralKit/PluralKit.git
synced 2026-02-12 00:30:11 +00:00
fix(commands): make flags not match if param was in quotes
This commit is contained in:
parent
a21210f3ce
commit
413b8c1915
3 changed files with 38 additions and 17 deletions
|
|
@ -69,7 +69,7 @@ pub fn parse_command(prefix: String, input: String) -> CommandResult {
|
||||||
loop {
|
loop {
|
||||||
let possible_tokens = local_tree.possible_tokens().cloned().collect::<Vec<_>>();
|
let possible_tokens = local_tree.possible_tokens().cloned().collect::<Vec<_>>();
|
||||||
println!("possible: {:?}", possible_tokens);
|
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);
|
println!("next: {:?}", next);
|
||||||
match next {
|
match next {
|
||||||
Some(Ok((found_token, arg, new_pos))) => {
|
Some(Ok((found_token, arg, new_pos))) => {
|
||||||
|
|
@ -157,39 +157,40 @@ pub fn parse_command(prefix: String, input: String) -> CommandResult {
|
||||||
/// - error when matching
|
/// - error when matching
|
||||||
fn next_token(
|
fn next_token(
|
||||||
possible_tokens: Vec<Token>,
|
possible_tokens: Vec<Token>,
|
||||||
input: SmolStr,
|
input: &str,
|
||||||
current_pos: usize,
|
current_pos: usize,
|
||||||
) -> Option<Result<(Token, Option<TokenMatchedValue>, usize), (Token, TokenMatchError)>> {
|
) -> Option<Result<(Token, Option<TokenMatchedValue>, usize), (Token, TokenMatchError)>> {
|
||||||
// get next parameter, matching quotes
|
// get next parameter, matching quotes
|
||||||
let param = crate::string::next_param(input.clone(), current_pos);
|
let matched = crate::string::next_param(&input, current_pos);
|
||||||
println!("matched: {param:?}\n---");
|
println!("matched: {matched:?}\n---");
|
||||||
|
|
||||||
// try checking if this is a flag
|
// try checking if this is a flag
|
||||||
// todo!: this breaks full text matching if the full text starts with a flag
|
// todo!: this breaks full text matching if the full text starts with a flag
|
||||||
// (but that's kinda already broken anyway)
|
// (but that's kinda already broken anyway)
|
||||||
if let Some((value, new_pos)) = param.clone()
|
if let Some(param) = matched.as_ref()
|
||||||
&& value.starts_with('-')
|
&& param.in_quotes.not()
|
||||||
|
&& param.value.starts_with('-')
|
||||||
{
|
{
|
||||||
return Some(Ok((
|
return Some(Ok((
|
||||||
Token::Flag,
|
Token::Flag,
|
||||||
Some(TokenMatchedValue {
|
Some(TokenMatchedValue {
|
||||||
raw: value,
|
raw: param.value.into(),
|
||||||
param: None,
|
param: None,
|
||||||
}),
|
}),
|
||||||
new_pos,
|
param.next_pos,
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// iterate over tokens and run try_match
|
// iterate over tokens and run try_match
|
||||||
for token in possible_tokens {
|
for token in possible_tokens {
|
||||||
// for FullString just send the whole string
|
// 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) {
|
match token.try_match(input_to_match) {
|
||||||
Some(Ok(value)) => {
|
Some(Ok(value)) => {
|
||||||
return Some(Ok((
|
return Some(Ok((
|
||||||
token,
|
token,
|
||||||
value,
|
value,
|
||||||
param.map(|v| v.1).unwrap_or(current_pos),
|
matched.map(|v| v.next_pos).unwrap_or(current_pos),
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
Some(Err(err)) => {
|
Some(Err(err)) => {
|
||||||
|
|
|
||||||
|
|
@ -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
|
// very very simple quote matching
|
||||||
// quotes need to be at start/end of words, and are ignored if a closing quote is not present
|
// 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
|
// 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 {
|
if input.len() == current_pos {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let leading_whitespace_count =
|
let leading_whitespace_count =
|
||||||
input[..current_pos].len() - input[..current_pos].trim_start().len();
|
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!("stuff: {input} {current_pos} {leading_whitespace_count}");
|
||||||
println!("to match: {substr_to_match}");
|
println!("to match: {substr_to_match}");
|
||||||
|
|
||||||
|
|
@ -68,20 +75,32 @@ pub(super) fn next_param(input: SmolStr, current_pos: usize) -> Option<(SmolStr,
|
||||||
.is_whitespace()
|
.is_whitespace()
|
||||||
{
|
{
|
||||||
// return quoted string, without quotes
|
// 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
|
// 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() {
|
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
|
// if we're here, we went to EOF and didn't match any whitespace
|
||||||
// so we return the whole string
|
// 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,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -88,7 +88,7 @@ impl TokenMatchedValue {
|
||||||
type TryMatchResult = Option<Result<Option<TokenMatchedValue>, TokenMatchError>>;
|
type TryMatchResult = Option<Result<Option<TokenMatchedValue>, TokenMatchError>>;
|
||||||
|
|
||||||
impl Token {
|
impl Token {
|
||||||
pub fn try_match(&self, input: Option<SmolStr>) -> TryMatchResult {
|
pub fn try_match(&self, input: Option<&str>) -> TryMatchResult {
|
||||||
let input = match input {
|
let input = match input {
|
||||||
Some(input) => input,
|
Some(input) => input,
|
||||||
None => {
|
None => {
|
||||||
|
|
@ -305,6 +305,7 @@ impl FromStr for MemberPrivacyTarget {
|
||||||
type Err = ();
|
type Err = ();
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
// todo: this doesnt parse all the possible ways
|
||||||
match s.to_lowercase().as_str() {
|
match s.to_lowercase().as_str() {
|
||||||
"visibility" => Ok(Self::Visibility),
|
"visibility" => Ok(Self::Visibility),
|
||||||
"name" => Ok(Self::Name),
|
"name" => Ok(Self::Name),
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue