diff --git a/Cargo.lock b/Cargo.lock index 444d0a91..62650368 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -445,6 +445,15 @@ dependencies = [ "generic-array", ] +[[package]] +name = "borsh" +version = "1.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2506947f73ad44e344215ccd6403ac2ae18cd8e046e581a441bf8d199f257f03" +dependencies = [ + "cfg_aliases", +] + [[package]] name = "bumpalo" version = "3.12.0" @@ -528,6 +537,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + [[package]] name = "chrono" version = "0.4.38" @@ -554,6 +569,7 @@ name = "commands" version = "0.1.0" dependencies = [ "lazy_static", + "smol_str", "uniffi", ] @@ -3489,6 +3505,16 @@ dependencies = [ "serde", ] +[[package]] +name = "smol_str" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9676b89cd56310a87b93dec47b11af744f34d5fc9f367b829474eec0a891350d" +dependencies = [ + "borsh", + "serde", +] + [[package]] name = "socket2" version = "0.4.7" diff --git a/crates/commands/Cargo.toml b/crates/commands/Cargo.toml index efff1dea..65e28ad4 100644 --- a/crates/commands/Cargo.toml +++ b/crates/commands/Cargo.toml @@ -10,6 +10,7 @@ crate-type = ["cdylib"] lazy_static = { workspace = true } uniffi = { version = "0.25" } +smol_str = "0.3.2" [build-dependencies] uniffi = { version = "0.25", features = [ "build" ] } diff --git a/crates/commands/src/lib.rs b/crates/commands/src/lib.rs index 6b787403..a98bdd9e 100644 --- a/crates/commands/src/lib.rs +++ b/crates/commands/src/lib.rs @@ -7,6 +7,7 @@ uniffi::include_scaffolding!("commands"); mod string; mod token; +use smol_str::SmolStr; use token::*; // todo!: move all this stuff into a different file @@ -74,77 +75,84 @@ struct Command { cb: String, } -fn command(tokens: &[&Token], help: &str, cb: &str) -> Command { +fn command(tokens: impl IntoIterator, help: impl ToString, cb: impl ToString) -> Command { Command { - tokens: tokens.iter().map(|&x| x.clone()).collect(), + tokens: tokens.into_iter().collect(), help: help.to_string(), cb: cb.to_string(), } } +macro_rules! command { + ([$($v:expr),+], $help:expr, $cb:expr) => { + $crate::command([$($v.clone()),*], $help, $cb) + }; +} + mod commands { + use smol_str::SmolStr; + use super::Token; - use super::command; - use super::Token::*; - - fn cmd(value: &str) -> Token { - Token::Value(vec![value.to_string()]) + fn cmd(value: impl Into) -> Token { + Token::Value(vec![value.into()]) } - pub fn cmd_with_alias(value: &[&str]) -> Token { - Token::Value(value.iter().map(|x| x.to_string()).collect()) + pub fn cmd_with_alias(value: impl IntoIterator>) -> Token { + Token::Value(value.into_iter().map(Into::into).collect()) } // todo: this needs to have less ampersands -alyssa pub fn happy() -> Vec { - let system = &cmd_with_alias(&["system", "s"]); - let member = &cmd_with_alias(&["member", "m"]); - let description = &cmd_with_alias(&["description", "desc"]); - let privacy = &cmd_with_alias(&["privacy", "priv"]); + use Token::*; + + let system = cmd_with_alias(["system", "s"]); + let member = cmd_with_alias(["member", "m"]); + let description = cmd_with_alias(["description", "desc"]); + let privacy = cmd_with_alias(["privacy", "priv"]); vec![ - command(&[&cmd("help")], "help", "Shows the help command"), - command( - &[system], + command!([cmd("help")], "help", "Shows the help command"), + command!( + [system], "system_show", - "Shows information about your system", + "Shows information about your system" ), - command(&[system, &cmd("new")], "system_new", "Creates a new system"), - command( - &[member, &cmd_with_alias(&["new", "n"])], + command!([system, cmd("new")], "system_new", "Creates a new system"), + command!( + [member, cmd_with_alias(["new", "n"])], "member_new", - "Creates a new system member", + "Creates a new system member" ), - command( - &[member, &MemberRef], + command!( + [member, MemberRef], "member_show", - "Shows information about a member", + "Shows information about a member" ), - command( - &[member, &MemberRef, description], + command!( + [member, MemberRef, description], "member_desc_show", - "Shows a member's description", + "Shows a member's description" ), - command( - &[member, &MemberRef, description, &FullString], + command!( + [member, MemberRef, description, FullString], "member_desc_update", - "Changes a member's description", + "Changes a member's description" ), - command( - &[member, &MemberRef, privacy], + command!( + [member, MemberRef, privacy], "member_privacy_show", - "Displays a member's current privacy settings", + "Displays a member's current privacy settings" ), - command( - &[ + command!( + [ member, - &MemberRef, + MemberRef, privacy, - &MemberPrivacyTarget, - &PrivacyLevel, + MemberPrivacyTarget, + PrivacyLevel ], "member_privacy_update", - "Changes a member's privacy settings", + "Changes a member's privacy settings" ), ] } @@ -188,9 +196,9 @@ pub struct ParsedCommand { /// - optionally a short-circuit error fn next_token( possible_tokens: Vec, - input: String, + input: SmolStr, current_pos: usize, -) -> Result<(Token, Option, usize), Option> { +) -> Result<(Token, Option, usize), Option> { // get next parameter, matching quotes let param = crate::string::next_param(input.clone(), current_pos); println!("matched: {param:?}\n---"); @@ -203,7 +211,7 @@ fn next_token( { return Ok(( Token::Flag, - Some(value.trim_start_matches('-').to_string()), + Some(value.trim_start_matches('-').into()), new_pos, )); } @@ -230,6 +238,7 @@ fn next_token( } fn parse_command(input: String) -> CommandResult { + let input: SmolStr = input.into(); let mut local_tree: TreeBranch = COMMAND_TREE.clone(); // end position of all currently matched tokens @@ -247,13 +256,13 @@ fn parse_command(input: String) -> CommandResult { Ok((found_token, arg, new_pos)) => { current_pos = new_pos; if let Token::Flag = found_token { - flags.insert(arg.unwrap(), None); + flags.insert(arg.unwrap().into(), None); // don't try matching flags as tree elements continue; } if let Some(arg) = arg { - args.push(arg); + args.push(arg.into()); } if let Some(next_tree) = local_tree.branches.get(&found_token) { @@ -280,7 +289,7 @@ fn parse_command(input: String) -> CommandResult { } Err(Some(short_circuit)) => { return CommandResult::Err { - error: short_circuit, + error: short_circuit.into(), }; } } diff --git a/crates/commands/src/string.rs b/crates/commands/src/string.rs index 0ea7659a..dd48fbf0 100644 --- a/crates/commands/src/string.rs +++ b/crates/commands/src/string.rs @@ -1,5 +1,7 @@ use std::collections::HashMap; +use smol_str::SmolStr; + lazy_static::lazy_static! { // Dictionary of (left, right) quote pairs // Each char in the string is an individual quote, multi-char strings imply "one of the following chars" @@ -43,14 +45,14 @@ lazy_static::lazy_static! { // 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 fn next_param(input: String, current_pos: usize) -> Option<(String, usize)> { +pub(super) fn next_param(input: SmolStr, current_pos: usize) -> Option<(SmolStr, usize)> { 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 = input[current_pos + leading_whitespace_count..].to_string(); + let substr_to_match: SmolStr = input[current_pos + leading_whitespace_count..].into(); println!("stuff: {input} {current_pos} {leading_whitespace_count}"); println!("to match: {substr_to_match}"); @@ -67,7 +69,7 @@ pub fn next_param(input: String, current_pos: usize) -> Option<(String, usize)> { // return quoted string, without quotes return Some(( - substr_to_match[1..pos - 1].to_string(), + substr_to_match[1..pos - 1].into(), current_pos + pos + 1, )); } @@ -78,7 +80,7 @@ pub fn next_param(input: String, current_pos: usize) -> Option<(String, usize)> // find next whitespace character for (pos, char) in substr_to_match.clone().char_indices() { if char.is_whitespace() { - return Some((substr_to_match[..pos].to_string(), current_pos + pos + 1)); + return Some((substr_to_match[..pos].into(), current_pos + pos + 1)); } } diff --git a/crates/commands/src/token.rs b/crates/commands/src/token.rs index 9d952a24..e66ca9d4 100644 --- a/crates/commands/src/token.rs +++ b/crates/commands/src/token.rs @@ -1,3 +1,5 @@ +use smol_str::SmolStr; + #[derive(Debug, Clone, Eq, Hash, PartialEq)] pub enum Token { /// Token used to represent a finished command (i.e. no more parameters required) @@ -5,10 +7,10 @@ pub enum Token { Empty, /// A bot-defined value ("member" in `pk;member MyName`) - Value(Vec), + Value(Vec), /// A command defined by multiple values // todo! - MultiValue(Vec>), + MultiValue(Vec>), FullString, @@ -26,20 +28,20 @@ pub enum Token { pub enum TokenMatchResult { NoMatch, /// Token matched, optionally with a value. - Match(Option), + Match(Option), } // move this somewhere else lazy_static::lazy_static!( - static ref MEMBER_PRIVACY_TARGETS: Vec = vec![ - "visibility".to_string(), - "name".to_string(), - "todo".to_string() - ]; + static ref MEMBER_PRIVACY_TARGETS: Vec = [ + "visibility", + "name", + "todo", + ].into_iter().map(SmolStr::new_static).collect(); ); impl Token { - pub fn try_match(&self, input: Option) -> TokenMatchResult { + pub fn try_match(&self, input: Option) -> TokenMatchResult { // short circuit on empty things if matches!(self, Self::Empty) && input.is_none() { return TokenMatchResult::Match(None); @@ -66,7 +68,9 @@ impl Token { Self::FullString => return TokenMatchResult::Match(Some(input)), Self::MemberRef => return TokenMatchResult::Match(Some(input)), Self::MemberPrivacyTarget - if MEMBER_PRIVACY_TARGETS.contains(&input.trim().to_string()) => + if MEMBER_PRIVACY_TARGETS + .iter() + .any(|target| target.eq(input.trim())) => { return TokenMatchResult::Match(Some(input)) }