use super::*;
#[derive(Clone, PartialEq, Eq, Debug, Default)]
pub struct KeyValueArgs(pub std::collections::HashMap<String, String>);
impl KeyValueArgs {
pub fn get(&self, key: &str) -> Option<&str> {
self.0.get(key).map(|x| x.as_str())
}
fn pop_single_key_value_pair(args: &str) -> Option<(&str, (String, String))> {
if args.is_empty() {
return None;
}
let mut key = String::new();
let mut inside_string = false;
let mut escaping = false;
let mut chars = args.trim_start().chars();
loop {
let c = chars.next()?;
if escaping {
key.push(c);
escaping = false;
} else if !inside_string && c.is_whitespace() {
return None;
} else if c == '"' {
inside_string = !inside_string;
} else if c == '\\' {
escaping = true;
} else if !inside_string && c == '=' {
break;
} else if !inside_string && c.is_ascii_punctuation() {
return None;
} else {
key.push(c);
}
}
let args = chars.as_str();
let (args, value) = super::pop_string(args).unwrap_or((args, String::new()));
Some((args, (key, value)))
}
fn pop_from(mut args: &str) -> (&str, Self) {
let mut pairs = std::collections::HashMap::new();
while let Some((remaining_args, (key, value))) = Self::pop_single_key_value_pair(args) {
args = remaining_args;
pairs.insert(key, value);
}
(args, Self(pairs))
}
}
#[async_trait::async_trait]
impl<'a> PopArgument<'a> for KeyValueArgs {
async fn pop_from(
args: &'a str,
attachment_index: usize,
_: &serenity::Context,
_: &serenity::Message,
) -> Result<(&'a str, usize, Self), (Box<dyn std::error::Error + Send + Sync>, Option<String>)>
{
let (a, b) = Self::pop_from(args);
Ok((a, attachment_index, b))
}
}
#[cfg(test)]
#[test]
fn test_key_value_args() {
for &(string, pairs, remaining_args) in &[
(
r#"key1=value1 key2=value2"#,
&[("key1", "value1"), ("key2", "value2")][..],
"",
),
(
r#""key 1"=value\ 1 key\ 2="value 2""#,
&[("key 1", "value 1"), ("key 2", "value 2")],
"",
),
(
r#"key1"=value1 key2=value2"#,
&[],
r#"key1"=value1 key2=value2"#,
),
(r#"dummyval"#, &[], "dummyval"),
(r#"dummyval="#, &[("dummyval", "")], ""),
] {
let (args, kv_args) = KeyValueArgs::pop_from(string);
assert_eq!(
kv_args.0,
pairs
.iter()
.map(|&(k, v)| (k.to_owned(), v.to_owned()))
.collect(),
);
assert_eq!(args, remaining_args);
}
}