mirror of
https://github.com/PluralKit/PluralKit.git
synced 2026-02-13 17:20:14 +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"
|
edition = "2021"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
crate-type = ["cdylib"]
|
crate-type = ["cdylib", "lib"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
lazy_static = { workspace = true }
|
lazy_static = { workspace = true }
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,8 @@ pub mod server_config;
|
||||||
pub mod switch;
|
pub mod switch;
|
||||||
pub mod system;
|
pub mod system;
|
||||||
|
|
||||||
|
use std::fmt::Display;
|
||||||
|
|
||||||
use smol_str::SmolStr;
|
use smol_str::SmolStr;
|
||||||
|
|
||||||
use crate::{
|
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_export]
|
||||||
macro_rules! command {
|
macro_rules! command {
|
||||||
([$($v:expr),+], $cb:expr, $help:expr) => {
|
([$($v:expr),+], $cb:expr, $help:expr) => {
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,12 @@ pub fn cmds() -> impl Iterator<Item = Command> {
|
||||||
"Shows the autoproxy timeout"
|
"Shows the autoproxy timeout"
|
||||||
),
|
),
|
||||||
command!(
|
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",
|
"cfg_ap_timeout_update",
|
||||||
"Sets the autoproxy timeout"
|
"Sets the autoproxy timeout"
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -3,21 +3,9 @@ use super::*;
|
||||||
pub fn cmds() -> impl Iterator<Item = Command> {
|
pub fn cmds() -> impl Iterator<Item = Command> {
|
||||||
let help = ["help", "h"];
|
let help = ["help", "h"];
|
||||||
[
|
[
|
||||||
command!(
|
command!([help], "help", "Shows the help command"),
|
||||||
[help],
|
command!([help, "commands"], "help_commands", "help commands"),
|
||||||
"help",
|
command!([help, "proxy"], "help_proxy", "help proxy"),
|
||||||
"Shows the help command"
|
|
||||||
),
|
|
||||||
command!(
|
|
||||||
[help, "commands"],
|
|
||||||
"help_commands",
|
|
||||||
"help commands"
|
|
||||||
),
|
|
||||||
command!(
|
|
||||||
[help, "proxy"],
|
|
||||||
"help_proxy",
|
|
||||||
"help proxy"
|
|
||||||
),
|
|
||||||
]
|
]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,12 @@ pub fn cmds() -> impl Iterator<Item = Command> {
|
||||||
"Shows a member's description"
|
"Shows a member's description"
|
||||||
),
|
),
|
||||||
command!(
|
command!(
|
||||||
[member, MemberRef("target"), description, FullString("description")],
|
[
|
||||||
|
member,
|
||||||
|
MemberRef("target"),
|
||||||
|
description,
|
||||||
|
FullString("description")
|
||||||
|
],
|
||||||
"member_desc_update",
|
"member_desc_update",
|
||||||
"Changes a member's description"
|
"Changes a member's description"
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -40,9 +40,9 @@ pub enum CommandResult {
|
||||||
pub enum Parameter {
|
pub enum Parameter {
|
||||||
MemberRef { member: String },
|
MemberRef { member: String },
|
||||||
SystemRef { system: String },
|
SystemRef { system: String },
|
||||||
MemberPrivacyTarget { target: String },
|
MemberPrivacyTarget { target: String },
|
||||||
PrivacyLevel { level: String },
|
PrivacyLevel { level: String },
|
||||||
OpaqueString { raw: String },
|
OpaqueString { raw: String },
|
||||||
Toggle { toggle: bool },
|
Toggle { toggle: bool },
|
||||||
Reset,
|
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 last token is a common incorrect unquote (multi-member names etc)
|
||||||
// todo: check if this is a system name in pk;s command
|
// todo: check if this is a system name in pk;s command
|
||||||
return CommandResult::Err {
|
return CommandResult::Err {
|
||||||
|
|
@ -161,8 +162,14 @@ fn next_token(
|
||||||
// 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 = param.clone().map(|v| v.0);
|
||||||
match token.try_match(input_to_match) {
|
match token.try_match(input_to_match) {
|
||||||
TokenMatchResult::Match(value) => return Ok((token, value, param.map(|v| v.1).unwrap_or(current_pos))),
|
TokenMatchResult::Match(value) => {
|
||||||
TokenMatchResult::MissingParameter { name } => return Err(Some(format_smolstr!("Missing parameter `{name}` in command `{input} [{name}]`."))),
|
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 => {}
|
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};
|
use smol_str::{SmolStr, ToSmolStr};
|
||||||
|
|
||||||
|
|
@ -33,6 +33,8 @@ pub enum Token {
|
||||||
PrivacyLevel(ParamName),
|
PrivacyLevel(ParamName),
|
||||||
|
|
||||||
/// on, off; yes, no; true, false
|
/// on, off; yes, no; true, false
|
||||||
|
Enable(ParamName),
|
||||||
|
Disable(ParamName),
|
||||||
Toggle(ParamName),
|
Toggle(ParamName),
|
||||||
|
|
||||||
/// reset, clear, default
|
/// reset, clear, default
|
||||||
|
|
@ -100,11 +102,15 @@ impl Token {
|
||||||
| Self::SystemRef(param_name)
|
| Self::SystemRef(param_name)
|
||||||
| Self::PrivacyLevel(param_name)
|
| Self::PrivacyLevel(param_name)
|
||||||
| Self::Toggle(param_name)
|
| Self::Toggle(param_name)
|
||||||
|
| Self::Enable(param_name)
|
||||||
|
| Self::Disable(param_name)
|
||||||
| Self::Reset(param_name) => MissingParameter { name: param_name },
|
| Self::Reset(param_name) => MissingParameter { name: param_name },
|
||||||
Self::Any(tokens) => tokens.is_empty().then_some(NoMatch).unwrap_or_else(|| {
|
Self::Any(tokens) => {
|
||||||
let mut results = tokens.iter().map(|t| t.try_match(None));
|
tokens.is_empty().then_some(NoMatch).unwrap_or_else(|| {
|
||||||
results.find(|r| !matches!(r, NoMatch)).unwrap_or(NoMatch)
|
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
|
// everything else doesnt match if no input anyway
|
||||||
Token::Value(_) => NoMatch,
|
Token::Value(_) => NoMatch,
|
||||||
Token::Flag => NoMatch,
|
Token::Flag => NoMatch,
|
||||||
|
|
@ -167,12 +173,30 @@ impl Token {
|
||||||
),
|
),
|
||||||
Err(_) => NoMatch,
|
Err(_) => NoMatch,
|
||||||
},
|
},
|
||||||
|
Self::Toggle(param_name) => match Enable::from_str(input)
|
||||||
Self::Toggle(param_name) => match Toggle::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(
|
Ok(t) => TokenMatchResult::new_match_param(
|
||||||
input,
|
input,
|
||||||
param_name,
|
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,
|
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.
|
/// Convenience trait to convert types into [`Token`]s.
|
||||||
pub trait ToToken {
|
pub trait ToToken {
|
||||||
fn to_token(&self) -> Token;
|
fn to_token(&self) -> Token;
|
||||||
|
|
@ -218,7 +273,13 @@ impl ToToken for [Token] {
|
||||||
pub enum MemberPrivacyTarget {
|
pub enum MemberPrivacyTarget {
|
||||||
Visibility,
|
Visibility,
|
||||||
Name,
|
Name,
|
||||||
// todo
|
Description,
|
||||||
|
Banner,
|
||||||
|
Avatar,
|
||||||
|
Birthday,
|
||||||
|
Pronouns,
|
||||||
|
Proxy,
|
||||||
|
Metadata,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsRef<str> for MemberPrivacyTarget {
|
impl AsRef<str> for MemberPrivacyTarget {
|
||||||
|
|
@ -226,6 +287,13 @@ impl AsRef<str> for MemberPrivacyTarget {
|
||||||
match self {
|
match self {
|
||||||
Self::Visibility => "visibility",
|
Self::Visibility => "visibility",
|
||||||
Self::Name => "name",
|
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 = ();
|
type Err = ();
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
match s {
|
match s.to_lowercase().as_str() {
|
||||||
"visibility" => Ok(Self::Visibility),
|
"visibility" => Ok(Self::Visibility),
|
||||||
"name" => Ok(Self::Name),
|
"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(()),
|
_ => 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)]
|
#[derive(Debug, Clone, Eq, Hash, PartialEq)]
|
||||||
pub struct Reset;
|
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
|
set -x
|
||||||
commandslib="''${1:-}"
|
commandslib="''${1:-}"
|
||||||
if [ "$commandslib" == "" ]; then
|
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"
|
commandslib="obj/libcommands.so"
|
||||||
else
|
else
|
||||||
cp -f "$commandslib" obj/
|
cp -f "$commandslib" obj/
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue