mirror of
https://github.com/PluralKit/PluralKit.git
synced 2026-02-04 13:06:50 +00:00
feat(commands): implement Display traits for Token and Command to have some basic 'doc gen', split Toggle into Enable and Disable
This commit is contained in:
parent
482c923507
commit
4f7e9c22a1
9 changed files with 187 additions and 56 deletions
|
|
@ -4,7 +4,7 @@ version = "0.1.0"
|
|||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
crate-type = ["cdylib", "lib"]
|
||||
|
||||
[dependencies]
|
||||
lazy_static = { workspace = true }
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@ pub mod server_config;
|
|||
pub mod switch;
|
||||
pub mod system;
|
||||
|
||||
use std::fmt::Display;
|
||||
|
||||
use smol_str::SmolStr;
|
||||
|
||||
use crate::{
|
||||
|
|
@ -47,6 +49,18 @@ impl Command {
|
|||
}
|
||||
}
|
||||
|
||||
impl Display for Command {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
for (idx, token) in self.tokens.iter().enumerate() {
|
||||
write!(f, "{}", token)?;
|
||||
if idx < self.tokens.len() - 1 {
|
||||
write!(f, " ")?;
|
||||
}
|
||||
}
|
||||
write!(f, " - {}", self.help)
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! command {
|
||||
([$($v:expr),+], $cb:expr, $help:expr) => {
|
||||
|
|
|
|||
|
|
@ -23,7 +23,12 @@ pub fn cmds() -> impl Iterator<Item = Command> {
|
|||
"Shows the autoproxy timeout"
|
||||
),
|
||||
command!(
|
||||
[cfg, autoproxy, ["timeout", "tm"], [Toggle("toggle"), Reset("reset"), FullString("timeout")]],
|
||||
[
|
||||
cfg,
|
||||
autoproxy,
|
||||
["timeout", "tm"],
|
||||
[Disable("toggle"), Reset("reset"), FullString("timeout")]
|
||||
],
|
||||
"cfg_ap_timeout_update",
|
||||
"Sets the autoproxy timeout"
|
||||
),
|
||||
|
|
|
|||
|
|
@ -3,21 +3,9 @@ use super::*;
|
|||
pub fn cmds() -> impl Iterator<Item = Command> {
|
||||
let help = ["help", "h"];
|
||||
[
|
||||
command!(
|
||||
[help],
|
||||
"help",
|
||||
"Shows the help command"
|
||||
),
|
||||
command!(
|
||||
[help, "commands"],
|
||||
"help_commands",
|
||||
"help commands"
|
||||
),
|
||||
command!(
|
||||
[help, "proxy"],
|
||||
"help_proxy",
|
||||
"help proxy"
|
||||
),
|
||||
command!([help], "help", "Shows the help command"),
|
||||
command!([help, "commands"], "help_commands", "help commands"),
|
||||
command!([help, "proxy"], "help_proxy", "help proxy"),
|
||||
]
|
||||
.into_iter()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,7 +30,12 @@ pub fn cmds() -> impl Iterator<Item = Command> {
|
|||
"Shows a member's description"
|
||||
),
|
||||
command!(
|
||||
[member, MemberRef("target"), description, FullString("description")],
|
||||
[
|
||||
member,
|
||||
MemberRef("target"),
|
||||
description,
|
||||
FullString("description")
|
||||
],
|
||||
"member_desc_update",
|
||||
"Changes a member's description"
|
||||
),
|
||||
|
|
|
|||
|
|
@ -40,9 +40,9 @@ pub enum CommandResult {
|
|||
pub enum Parameter {
|
||||
MemberRef { member: String },
|
||||
SystemRef { system: String },
|
||||
MemberPrivacyTarget { target: String },
|
||||
PrivacyLevel { level: String },
|
||||
OpaqueString { raw: String },
|
||||
MemberPrivacyTarget { target: String },
|
||||
PrivacyLevel { level: String },
|
||||
OpaqueString { raw: String },
|
||||
Toggle { toggle: bool },
|
||||
Reset,
|
||||
}
|
||||
|
|
@ -109,6 +109,7 @@ fn parse_command(input: String) -> CommandResult {
|
|||
},
|
||||
};
|
||||
}
|
||||
|
||||
// todo: check if last token is a common incorrect unquote (multi-member names etc)
|
||||
// todo: check if this is a system name in pk;s command
|
||||
return CommandResult::Err {
|
||||
|
|
@ -161,8 +162,14 @@ fn next_token(
|
|||
// for FullString just send the whole string
|
||||
let input_to_match = param.clone().map(|v| v.0);
|
||||
match token.try_match(input_to_match) {
|
||||
TokenMatchResult::Match(value) => return Ok((token, value, param.map(|v| v.1).unwrap_or(current_pos))),
|
||||
TokenMatchResult::MissingParameter { name } => return Err(Some(format_smolstr!("Missing parameter `{name}` in command `{input} [{name}]`."))),
|
||||
TokenMatchResult::Match(value) => {
|
||||
return Ok((token, value, param.map(|v| v.1).unwrap_or(current_pos)))
|
||||
}
|
||||
TokenMatchResult::MissingParameter { name } => {
|
||||
return Err(Some(format_smolstr!(
|
||||
"Missing parameter `{name}` in command `{input} [{name}]`."
|
||||
)))
|
||||
}
|
||||
TokenMatchResult::NoMatch => {}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
7
crates/commands/src/main.rs
Normal file
7
crates/commands/src/main.rs
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
use commands::commands as cmds;
|
||||
|
||||
fn main() {
|
||||
for command in cmds::all() {
|
||||
println!("{}", command);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
use std::str::FromStr;
|
||||
use std::{fmt::Display, ops::Not, str::FromStr};
|
||||
|
||||
use smol_str::{SmolStr, ToSmolStr};
|
||||
|
||||
|
|
@ -33,6 +33,8 @@ pub enum Token {
|
|||
PrivacyLevel(ParamName),
|
||||
|
||||
/// on, off; yes, no; true, false
|
||||
Enable(ParamName),
|
||||
Disable(ParamName),
|
||||
Toggle(ParamName),
|
||||
|
||||
/// reset, clear, default
|
||||
|
|
@ -100,11 +102,15 @@ impl Token {
|
|||
| Self::SystemRef(param_name)
|
||||
| Self::PrivacyLevel(param_name)
|
||||
| Self::Toggle(param_name)
|
||||
| Self::Enable(param_name)
|
||||
| Self::Disable(param_name)
|
||||
| Self::Reset(param_name) => MissingParameter { name: param_name },
|
||||
Self::Any(tokens) => tokens.is_empty().then_some(NoMatch).unwrap_or_else(|| {
|
||||
let mut results = tokens.iter().map(|t| t.try_match(None));
|
||||
results.find(|r| !matches!(r, NoMatch)).unwrap_or(NoMatch)
|
||||
}),
|
||||
Self::Any(tokens) => {
|
||||
tokens.is_empty().then_some(NoMatch).unwrap_or_else(|| {
|
||||
let mut results = tokens.iter().map(|t| t.try_match(None));
|
||||
results.find(|r| !matches!(r, NoMatch)).unwrap_or(NoMatch)
|
||||
})
|
||||
}
|
||||
// everything else doesnt match if no input anyway
|
||||
Token::Value(_) => NoMatch,
|
||||
Token::Flag => NoMatch,
|
||||
|
|
@ -167,12 +173,30 @@ impl Token {
|
|||
),
|
||||
Err(_) => NoMatch,
|
||||
},
|
||||
|
||||
Self::Toggle(param_name) => match Toggle::from_str(input) {
|
||||
Self::Toggle(param_name) => match Enable::from_str(input)
|
||||
.map(Into::<bool>::into)
|
||||
.or_else(|_| Disable::from_str(input).map(Into::<bool>::into))
|
||||
{
|
||||
Ok(toggle) => TokenMatchResult::new_match_param(
|
||||
input,
|
||||
param_name,
|
||||
Parameter::Toggle { toggle },
|
||||
),
|
||||
Err(_) => NoMatch,
|
||||
},
|
||||
Self::Enable(param_name) => match Enable::from_str(input) {
|
||||
Ok(t) => TokenMatchResult::new_match_param(
|
||||
input,
|
||||
param_name,
|
||||
Parameter::Toggle { toggle: t.0 },
|
||||
Parameter::Toggle { toggle: t.into() },
|
||||
),
|
||||
Err(_) => NoMatch,
|
||||
},
|
||||
Self::Disable(param_name) => match Disable::from_str(input) {
|
||||
Ok(t) => TokenMatchResult::new_match_param(
|
||||
input,
|
||||
param_name,
|
||||
Parameter::Toggle { toggle: t.into() },
|
||||
),
|
||||
Err(_) => NoMatch,
|
||||
},
|
||||
|
|
@ -185,6 +209,37 @@ impl Token {
|
|||
}
|
||||
}
|
||||
|
||||
impl Display for Token {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Token::Empty => write!(f, ""),
|
||||
Token::Any(vec) => {
|
||||
write!(f, "(")?;
|
||||
for (i, token) in vec.iter().enumerate() {
|
||||
if i != 0 {
|
||||
write!(f, " | ")?;
|
||||
}
|
||||
write!(f, "{}", token)?;
|
||||
}
|
||||
write!(f, ")")
|
||||
}
|
||||
Token::Value(vec) if vec.is_empty().not() => write!(f, "{}", vec.first().unwrap()),
|
||||
Token::Value(_) => Ok(()), // if value token has no values (lol), don't print anything
|
||||
// todo: it might not be the best idea to directly use param name here (what if we want to display something else but keep the name? or translations?)
|
||||
Token::FullString(param_name) => write!(f, "[{}]", param_name),
|
||||
Token::MemberRef(param_name) => write!(f, "<{}>", param_name),
|
||||
Token::SystemRef(param_name) => write!(f, "<{}>", param_name),
|
||||
Token::MemberPrivacyTarget(param_name) => write!(f, "[{}]", param_name),
|
||||
Token::PrivacyLevel(param_name) => write!(f, "[{}]", param_name),
|
||||
Token::Enable(_) => write!(f, "on"),
|
||||
Token::Disable(_) => write!(f, "off"),
|
||||
Token::Toggle(_) => write!(f, "on/off"),
|
||||
Token::Reset(_) => write!(f, "reset"),
|
||||
Token::Flag => unreachable!("flag tokens should never be in command definitions"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Convenience trait to convert types into [`Token`]s.
|
||||
pub trait ToToken {
|
||||
fn to_token(&self) -> Token;
|
||||
|
|
@ -218,7 +273,13 @@ impl ToToken for [Token] {
|
|||
pub enum MemberPrivacyTarget {
|
||||
Visibility,
|
||||
Name,
|
||||
// todo
|
||||
Description,
|
||||
Banner,
|
||||
Avatar,
|
||||
Birthday,
|
||||
Pronouns,
|
||||
Proxy,
|
||||
Metadata,
|
||||
}
|
||||
|
||||
impl AsRef<str> for MemberPrivacyTarget {
|
||||
|
|
@ -226,6 +287,13 @@ impl AsRef<str> for MemberPrivacyTarget {
|
|||
match self {
|
||||
Self::Visibility => "visibility",
|
||||
Self::Name => "name",
|
||||
Self::Description => "description",
|
||||
Self::Banner => "banner",
|
||||
Self::Avatar => "avatar",
|
||||
Self::Birthday => "birthday",
|
||||
Self::Pronouns => "pronouns",
|
||||
Self::Proxy => "proxy",
|
||||
Self::Metadata => "metadata",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -235,9 +303,16 @@ impl FromStr for MemberPrivacyTarget {
|
|||
type Err = ();
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s {
|
||||
match s.to_lowercase().as_str() {
|
||||
"visibility" => Ok(Self::Visibility),
|
||||
"name" => Ok(Self::Name),
|
||||
"description" => Ok(Self::Description),
|
||||
"banner" => Ok(Self::Banner),
|
||||
"avatar" => Ok(Self::Avatar),
|
||||
"birthday" => Ok(Self::Birthday),
|
||||
"pronouns" => Ok(Self::Pronouns),
|
||||
"proxy" => Ok(Self::Proxy),
|
||||
"metadata" => Ok(Self::Metadata),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
|
|
@ -270,28 +345,6 @@ impl FromStr for PrivacyLevel {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, Hash, PartialEq)]
|
||||
pub struct Toggle(bool);
|
||||
|
||||
impl AsRef<str> for Toggle {
|
||||
fn as_ref(&self) -> &str {
|
||||
// on / off better than others for docs and stuff?
|
||||
self.0.then_some("on").unwrap_or("off")
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Toggle {
|
||||
type Err = ();
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s {
|
||||
"on" | "yes" | "true" | "enable" | "enabled" => Ok(Self(true)),
|
||||
"off" | "no" | "false" | "disable" | "disabled" => Ok(Self(false)),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, Hash, PartialEq)]
|
||||
pub struct Reset;
|
||||
|
||||
|
|
@ -311,3 +364,55 @@ impl FromStr for Reset {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, Hash, PartialEq)]
|
||||
pub struct Enable;
|
||||
|
||||
impl AsRef<str> for Enable {
|
||||
fn as_ref(&self) -> &str {
|
||||
"on"
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Enable {
|
||||
type Err = ();
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s {
|
||||
"on" | "yes" | "true" | "enable" | "enabled" => Ok(Self),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<bool> for Enable {
|
||||
fn into(self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, Hash, PartialEq)]
|
||||
pub struct Disable;
|
||||
|
||||
impl AsRef<str> for Disable {
|
||||
fn as_ref(&self) -> &str {
|
||||
"off"
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Disable {
|
||||
type Err = ();
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s {
|
||||
"off" | "no" | "false" | "disable" | "disabled" => Ok(Self),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<bool> for Disable {
|
||||
fn into(self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@
|
|||
set -x
|
||||
commandslib="''${1:-}"
|
||||
if [ "$commandslib" == "" ]; then
|
||||
cargo -Z unstable-options build --package commands --release --artifact-dir obj/
|
||||
cargo -Z unstable-options build --package commands --lib --release --artifact-dir obj/
|
||||
commandslib="obj/libcommands.so"
|
||||
else
|
||||
cp -f "$commandslib" obj/
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue