mirror of
https://github.com/PluralKit/PluralKit.git
synced 2026-02-15 10:10:12 +00:00
refactor(commands): use smolstr, use a decl macro to get rid of all the borrows while creating commands
This commit is contained in:
parent
11842e7637
commit
405ac11d74
5 changed files with 101 additions and 59 deletions
26
Cargo.lock
generated
26
Cargo.lock
generated
|
|
@ -445,6 +445,15 @@ dependencies = [
|
||||||
"generic-array",
|
"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]]
|
[[package]]
|
||||||
name = "bumpalo"
|
name = "bumpalo"
|
||||||
version = "3.12.0"
|
version = "3.12.0"
|
||||||
|
|
@ -528,6 +537,12 @@ version = "1.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg_aliases"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "chrono"
|
name = "chrono"
|
||||||
version = "0.4.38"
|
version = "0.4.38"
|
||||||
|
|
@ -554,6 +569,7 @@ name = "commands"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
|
"smol_str",
|
||||||
"uniffi",
|
"uniffi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -3489,6 +3505,16 @@ dependencies = [
|
||||||
"serde",
|
"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]]
|
[[package]]
|
||||||
name = "socket2"
|
name = "socket2"
|
||||||
version = "0.4.7"
|
version = "0.4.7"
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ crate-type = ["cdylib"]
|
||||||
lazy_static = { workspace = true }
|
lazy_static = { workspace = true }
|
||||||
|
|
||||||
uniffi = { version = "0.25" }
|
uniffi = { version = "0.25" }
|
||||||
|
smol_str = "0.3.2"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
uniffi = { version = "0.25", features = [ "build" ] }
|
uniffi = { version = "0.25", features = [ "build" ] }
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ uniffi::include_scaffolding!("commands");
|
||||||
|
|
||||||
mod string;
|
mod string;
|
||||||
mod token;
|
mod token;
|
||||||
|
use smol_str::SmolStr;
|
||||||
use token::*;
|
use token::*;
|
||||||
|
|
||||||
// todo!: move all this stuff into a different file
|
// todo!: move all this stuff into a different file
|
||||||
|
|
@ -74,77 +75,84 @@ struct Command {
|
||||||
cb: String,
|
cb: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn command(tokens: &[&Token], help: &str, cb: &str) -> Command {
|
fn command(tokens: impl IntoIterator<Item = Token>, help: impl ToString, cb: impl ToString) -> Command {
|
||||||
Command {
|
Command {
|
||||||
tokens: tokens.iter().map(|&x| x.clone()).collect(),
|
tokens: tokens.into_iter().collect(),
|
||||||
help: help.to_string(),
|
help: help.to_string(),
|
||||||
cb: cb.to_string(),
|
cb: cb.to_string(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
macro_rules! command {
|
||||||
|
([$($v:expr),+], $help:expr, $cb:expr) => {
|
||||||
|
$crate::command([$($v.clone()),*], $help, $cb)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
mod commands {
|
mod commands {
|
||||||
|
use smol_str::SmolStr;
|
||||||
|
|
||||||
use super::Token;
|
use super::Token;
|
||||||
|
|
||||||
use super::command;
|
fn cmd(value: impl Into<SmolStr>) -> Token {
|
||||||
use super::Token::*;
|
Token::Value(vec![value.into()])
|
||||||
|
|
||||||
fn cmd(value: &str) -> Token {
|
|
||||||
Token::Value(vec![value.to_string()])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn cmd_with_alias(value: &[&str]) -> Token {
|
pub fn cmd_with_alias(value: impl IntoIterator<Item = impl Into<SmolStr>>) -> Token {
|
||||||
Token::Value(value.iter().map(|x| x.to_string()).collect())
|
Token::Value(value.into_iter().map(Into::into).collect())
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo: this needs to have less ampersands -alyssa
|
// todo: this needs to have less ampersands -alyssa
|
||||||
pub fn happy() -> Vec<super::Command> {
|
pub fn happy() -> Vec<super::Command> {
|
||||||
let system = &cmd_with_alias(&["system", "s"]);
|
use Token::*;
|
||||||
let member = &cmd_with_alias(&["member", "m"]);
|
|
||||||
let description = &cmd_with_alias(&["description", "desc"]);
|
let system = cmd_with_alias(["system", "s"]);
|
||||||
let privacy = &cmd_with_alias(&["privacy", "priv"]);
|
let member = cmd_with_alias(["member", "m"]);
|
||||||
|
let description = cmd_with_alias(["description", "desc"]);
|
||||||
|
let privacy = cmd_with_alias(["privacy", "priv"]);
|
||||||
vec![
|
vec![
|
||||||
command(&[&cmd("help")], "help", "Shows the help command"),
|
command!([cmd("help")], "help", "Shows the help command"),
|
||||||
command(
|
command!(
|
||||||
&[system],
|
[system],
|
||||||
"system_show",
|
"system_show",
|
||||||
"Shows information about your system",
|
"Shows information about your system"
|
||||||
),
|
),
|
||||||
command(&[system, &cmd("new")], "system_new", "Creates a new system"),
|
command!([system, cmd("new")], "system_new", "Creates a new system"),
|
||||||
command(
|
command!(
|
||||||
&[member, &cmd_with_alias(&["new", "n"])],
|
[member, cmd_with_alias(["new", "n"])],
|
||||||
"member_new",
|
"member_new",
|
||||||
"Creates a new system member",
|
"Creates a new system member"
|
||||||
),
|
),
|
||||||
command(
|
command!(
|
||||||
&[member, &MemberRef],
|
[member, MemberRef],
|
||||||
"member_show",
|
"member_show",
|
||||||
"Shows information about a member",
|
"Shows information about a member"
|
||||||
),
|
),
|
||||||
command(
|
command!(
|
||||||
&[member, &MemberRef, description],
|
[member, MemberRef, description],
|
||||||
"member_desc_show",
|
"member_desc_show",
|
||||||
"Shows a member's description",
|
"Shows a member's description"
|
||||||
),
|
),
|
||||||
command(
|
command!(
|
||||||
&[member, &MemberRef, description, &FullString],
|
[member, MemberRef, description, FullString],
|
||||||
"member_desc_update",
|
"member_desc_update",
|
||||||
"Changes a member's description",
|
"Changes a member's description"
|
||||||
),
|
),
|
||||||
command(
|
command!(
|
||||||
&[member, &MemberRef, privacy],
|
[member, MemberRef, privacy],
|
||||||
"member_privacy_show",
|
"member_privacy_show",
|
||||||
"Displays a member's current privacy settings",
|
"Displays a member's current privacy settings"
|
||||||
),
|
),
|
||||||
command(
|
command!(
|
||||||
&[
|
[
|
||||||
member,
|
member,
|
||||||
&MemberRef,
|
MemberRef,
|
||||||
privacy,
|
privacy,
|
||||||
&MemberPrivacyTarget,
|
MemberPrivacyTarget,
|
||||||
&PrivacyLevel,
|
PrivacyLevel
|
||||||
],
|
],
|
||||||
"member_privacy_update",
|
"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
|
/// - optionally a short-circuit error
|
||||||
fn next_token(
|
fn next_token(
|
||||||
possible_tokens: Vec<Token>,
|
possible_tokens: Vec<Token>,
|
||||||
input: String,
|
input: SmolStr,
|
||||||
current_pos: usize,
|
current_pos: usize,
|
||||||
) -> Result<(Token, Option<String>, usize), Option<String>> {
|
) -> Result<(Token, Option<SmolStr>, usize), Option<SmolStr>> {
|
||||||
// get next parameter, matching quotes
|
// get next parameter, matching quotes
|
||||||
let param = crate::string::next_param(input.clone(), current_pos);
|
let param = crate::string::next_param(input.clone(), current_pos);
|
||||||
println!("matched: {param:?}\n---");
|
println!("matched: {param:?}\n---");
|
||||||
|
|
@ -203,7 +211,7 @@ fn next_token(
|
||||||
{
|
{
|
||||||
return Ok((
|
return Ok((
|
||||||
Token::Flag,
|
Token::Flag,
|
||||||
Some(value.trim_start_matches('-').to_string()),
|
Some(value.trim_start_matches('-').into()),
|
||||||
new_pos,
|
new_pos,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
@ -230,6 +238,7 @@ fn next_token(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_command(input: String) -> CommandResult {
|
fn parse_command(input: String) -> CommandResult {
|
||||||
|
let input: SmolStr = input.into();
|
||||||
let mut local_tree: TreeBranch = COMMAND_TREE.clone();
|
let mut local_tree: TreeBranch = COMMAND_TREE.clone();
|
||||||
|
|
||||||
// end position of all currently matched tokens
|
// end position of all currently matched tokens
|
||||||
|
|
@ -247,13 +256,13 @@ fn parse_command(input: String) -> CommandResult {
|
||||||
Ok((found_token, arg, new_pos)) => {
|
Ok((found_token, arg, new_pos)) => {
|
||||||
current_pos = new_pos;
|
current_pos = new_pos;
|
||||||
if let Token::Flag = found_token {
|
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
|
// don't try matching flags as tree elements
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(arg) = arg {
|
if let Some(arg) = arg {
|
||||||
args.push(arg);
|
args.push(arg.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(next_tree) = local_tree.branches.get(&found_token) {
|
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)) => {
|
Err(Some(short_circuit)) => {
|
||||||
return CommandResult::Err {
|
return CommandResult::Err {
|
||||||
error: short_circuit,
|
error: short_circuit.into(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use smol_str::SmolStr;
|
||||||
|
|
||||||
lazy_static::lazy_static! {
|
lazy_static::lazy_static! {
|
||||||
// Dictionary of (left, right) quote pairs
|
// Dictionary of (left, right) quote pairs
|
||||||
// Each char in the string is an individual quote, multi-char strings imply "one of the following chars"
|
// 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
|
// 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 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 {
|
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 = 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!("stuff: {input} {current_pos} {leading_whitespace_count}");
|
||||||
println!("to match: {substr_to_match}");
|
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 quoted string, without quotes
|
||||||
return Some((
|
return Some((
|
||||||
substr_to_match[1..pos - 1].to_string(),
|
substr_to_match[1..pos - 1].into(),
|
||||||
current_pos + pos + 1,
|
current_pos + pos + 1,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
@ -78,7 +80,7 @@ pub fn next_param(input: String, current_pos: usize) -> Option<(String, usize)>
|
||||||
// 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.clone().char_indices() {
|
||||||
if char.is_whitespace() {
|
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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
use smol_str::SmolStr;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Eq, Hash, PartialEq)]
|
#[derive(Debug, Clone, Eq, Hash, PartialEq)]
|
||||||
pub enum Token {
|
pub enum Token {
|
||||||
/// Token used to represent a finished command (i.e. no more parameters required)
|
/// Token used to represent a finished command (i.e. no more parameters required)
|
||||||
|
|
@ -5,10 +7,10 @@ pub enum Token {
|
||||||
Empty,
|
Empty,
|
||||||
|
|
||||||
/// A bot-defined value ("member" in `pk;member MyName`)
|
/// A bot-defined value ("member" in `pk;member MyName`)
|
||||||
Value(Vec<String>),
|
Value(Vec<SmolStr>),
|
||||||
/// A command defined by multiple values
|
/// A command defined by multiple values
|
||||||
// todo!
|
// todo!
|
||||||
MultiValue(Vec<Vec<String>>),
|
MultiValue(Vec<Vec<SmolStr>>),
|
||||||
|
|
||||||
FullString,
|
FullString,
|
||||||
|
|
||||||
|
|
@ -26,20 +28,20 @@ pub enum Token {
|
||||||
pub enum TokenMatchResult {
|
pub enum TokenMatchResult {
|
||||||
NoMatch,
|
NoMatch,
|
||||||
/// Token matched, optionally with a value.
|
/// Token matched, optionally with a value.
|
||||||
Match(Option<String>),
|
Match(Option<SmolStr>),
|
||||||
}
|
}
|
||||||
|
|
||||||
// move this somewhere else
|
// move this somewhere else
|
||||||
lazy_static::lazy_static!(
|
lazy_static::lazy_static!(
|
||||||
static ref MEMBER_PRIVACY_TARGETS: Vec<String> = vec![
|
static ref MEMBER_PRIVACY_TARGETS: Vec<SmolStr> = [
|
||||||
"visibility".to_string(),
|
"visibility",
|
||||||
"name".to_string(),
|
"name",
|
||||||
"todo".to_string()
|
"todo",
|
||||||
];
|
].into_iter().map(SmolStr::new_static).collect();
|
||||||
);
|
);
|
||||||
|
|
||||||
impl Token {
|
impl Token {
|
||||||
pub fn try_match(&self, input: Option<String>) -> TokenMatchResult {
|
pub fn try_match(&self, input: Option<SmolStr>) -> TokenMatchResult {
|
||||||
// short circuit on empty things
|
// short circuit on empty things
|
||||||
if matches!(self, Self::Empty) && input.is_none() {
|
if matches!(self, Self::Empty) && input.is_none() {
|
||||||
return TokenMatchResult::Match(None);
|
return TokenMatchResult::Match(None);
|
||||||
|
|
@ -66,7 +68,9 @@ impl Token {
|
||||||
Self::FullString => return TokenMatchResult::Match(Some(input)),
|
Self::FullString => return TokenMatchResult::Match(Some(input)),
|
||||||
Self::MemberRef => return TokenMatchResult::Match(Some(input)),
|
Self::MemberRef => return TokenMatchResult::Match(Some(input)),
|
||||||
Self::MemberPrivacyTarget
|
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))
|
return TokenMatchResult::Match(Some(input))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue