mirror of
https://github.com/PluralKit/PluralKit.git
synced 2026-02-14 09:40:10 +00:00
refactor(commands): rewrite how parameters are handled so they work same across cmd params / flag params, and make it easier to add new parameters
This commit is contained in:
parent
a1f7656276
commit
07541d9926
8 changed files with 443 additions and 363 deletions
|
|
@ -22,11 +22,7 @@ use std::fmt::{Debug, Display};
|
||||||
|
|
||||||
use smol_str::SmolStr;
|
use smol_str::SmolStr;
|
||||||
|
|
||||||
use crate::{
|
use crate::{any, command, flag::Flag, parameter::*, token::Token};
|
||||||
command,
|
|
||||||
flag::{Flag, FlagValue},
|
|
||||||
token::Token,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Command {
|
pub struct Command {
|
||||||
|
|
@ -51,17 +47,7 @@ impl Command {
|
||||||
let mut was_parameter = true;
|
let mut was_parameter = true;
|
||||||
for (idx, token) in tokens.iter().enumerate().rev() {
|
for (idx, token) in tokens.iter().enumerate().rev() {
|
||||||
match token {
|
match token {
|
||||||
Token::OpaqueRemainder(_)
|
Token::Parameter(_, _) | Token::Any(_) => {
|
||||||
| Token::OpaqueString(_)
|
|
||||||
| Token::MemberRef(_)
|
|
||||||
| Token::MemberPrivacyTarget(_)
|
|
||||||
| Token::SystemRef(_)
|
|
||||||
| Token::PrivacyLevel(_)
|
|
||||||
| Token::Toggle(_)
|
|
||||||
| Token::Enable(_)
|
|
||||||
| Token::Disable(_)
|
|
||||||
| Token::Reset(_)
|
|
||||||
| Token::Any(_) => {
|
|
||||||
parse_flags_before = idx;
|
parse_flags_before = idx;
|
||||||
was_parameter = true;
|
was_parameter = true;
|
||||||
}
|
}
|
||||||
|
|
@ -92,7 +78,7 @@ impl Command {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn value_flag(mut self, name: impl Into<SmolStr>, value: FlagValue) -> Self {
|
pub fn value_flag(mut self, name: impl Into<SmolStr>, value: impl Parameter + 'static) -> Self {
|
||||||
self.flags.push(Flag::new(name).with_value(value));
|
self.flags.push(Flag::new(name).with_value(value));
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,6 @@
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
pub fn cmds() -> impl Iterator<Item = Command> {
|
pub fn cmds() -> impl Iterator<Item = Command> {
|
||||||
use Token::*;
|
|
||||||
|
|
||||||
let cfg = ["config", "cfg"];
|
let cfg = ["config", "cfg"];
|
||||||
let autoproxy = ["autoproxy", "ap"];
|
let autoproxy = ["autoproxy", "ap"];
|
||||||
|
|
||||||
|
|
@ -13,7 +11,7 @@ pub fn cmds() -> impl Iterator<Item = Command> {
|
||||||
"Shows autoproxy status for the account"
|
"Shows autoproxy status for the account"
|
||||||
),
|
),
|
||||||
command!(
|
command!(
|
||||||
[cfg, autoproxy, ["account", "ac"], Toggle("toggle")],
|
[cfg, autoproxy, ["account", "ac"], Toggle],
|
||||||
"cfg_ap_account_update",
|
"cfg_ap_account_update",
|
||||||
"Toggles autoproxy for the account"
|
"Toggles autoproxy for the account"
|
||||||
),
|
),
|
||||||
|
|
@ -27,7 +25,7 @@ pub fn cmds() -> impl Iterator<Item = Command> {
|
||||||
cfg,
|
cfg,
|
||||||
autoproxy,
|
autoproxy,
|
||||||
["timeout", "tm"],
|
["timeout", "tm"],
|
||||||
[Disable("toggle"), Reset("reset"), OpaqueString("timeout")] // todo: we should parse duration / time values
|
any!(Disable, Reset, ("timeout", OpaqueString::SINGLE)) // todo: we should parse duration / time values
|
||||||
],
|
],
|
||||||
"cfg_ap_timeout_update",
|
"cfg_ap_timeout_update",
|
||||||
"Sets the autoproxy timeout"
|
"Sets the autoproxy timeout"
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,6 @@
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
pub fn cmds() -> impl Iterator<Item = Command> {
|
pub fn cmds() -> impl Iterator<Item = Command> {
|
||||||
use Token::*;
|
|
||||||
|
|
||||||
let member = ["member", "m"];
|
let member = ["member", "m"];
|
||||||
let description = ["description", "desc"];
|
let description = ["description", "desc"];
|
||||||
let privacy = ["privacy", "priv"];
|
let privacy = ["privacy", "priv"];
|
||||||
|
|
@ -10,49 +8,49 @@ pub fn cmds() -> impl Iterator<Item = Command> {
|
||||||
|
|
||||||
[
|
[
|
||||||
command!(
|
command!(
|
||||||
[member, new, OpaqueString("name")],
|
[member, new, ("name", OpaqueString::SINGLE)],
|
||||||
"member_new",
|
"member_new",
|
||||||
"Creates a new system member"
|
"Creates a new system member"
|
||||||
),
|
),
|
||||||
command!(
|
command!(
|
||||||
[member, MemberRef("target")],
|
[member, MemberRef],
|
||||||
"member_show",
|
"member_show",
|
||||||
"Shows information about a member"
|
"Shows information about a member"
|
||||||
)
|
)
|
||||||
.value_flag("pt", FlagValue::OpaqueString),
|
.value_flag("pt", Disable),
|
||||||
command!(
|
command!(
|
||||||
[member, MemberRef("target"), description],
|
[member, MemberRef, description],
|
||||||
"member_desc_show",
|
"member_desc_show",
|
||||||
"Shows a member's description"
|
"Shows a member's description"
|
||||||
),
|
),
|
||||||
command!(
|
command!(
|
||||||
[
|
[
|
||||||
member,
|
member,
|
||||||
MemberRef("target"),
|
MemberRef,
|
||||||
description,
|
description,
|
||||||
OpaqueRemainder("description")
|
("description", OpaqueString::REMAINDER)
|
||||||
],
|
],
|
||||||
"member_desc_update",
|
"member_desc_update",
|
||||||
"Changes a member's description"
|
"Changes a member's description"
|
||||||
),
|
),
|
||||||
command!(
|
command!(
|
||||||
[member, MemberRef("target"), 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("target"),
|
MemberRef,
|
||||||
privacy,
|
privacy,
|
||||||
MemberPrivacyTarget("privacy_target"),
|
MemberPrivacyTarget,
|
||||||
PrivacyLevel("new_privacy_level")
|
("new_privacy_level", PrivacyLevel)
|
||||||
],
|
],
|
||||||
"member_privacy_update",
|
"member_privacy_update",
|
||||||
"Changes a member's privacy settings"
|
"Changes a member's privacy settings"
|
||||||
),
|
),
|
||||||
command!(
|
command!(
|
||||||
[member, MemberRef("target"), "soulscream"],
|
[member, MemberRef, "soulscream"],
|
||||||
"member_soulscream",
|
"member_soulscream",
|
||||||
"todo"
|
"todo"
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,6 @@
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
pub fn cmds() -> impl Iterator<Item = Command> {
|
pub fn cmds() -> impl Iterator<Item = Command> {
|
||||||
use Token::*;
|
|
||||||
|
|
||||||
let system = ["system", "s"];
|
let system = ["system", "s"];
|
||||||
let new = ["new", "n"];
|
let new = ["new", "n"];
|
||||||
|
|
||||||
|
|
@ -14,7 +12,7 @@ pub fn cmds() -> impl Iterator<Item = Command> {
|
||||||
),
|
),
|
||||||
command!([system, new], "system_new", "Creates a new system"),
|
command!([system, new], "system_new", "Creates a new system"),
|
||||||
command!(
|
command!(
|
||||||
[system, new, OpaqueString("name")],
|
[system, new, ("name", OpaqueString::SINGLE)],
|
||||||
"system_new",
|
"system_new",
|
||||||
"Creates a new system"
|
"Creates a new system"
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -1,50 +1,27 @@
|
||||||
use std::fmt::Display;
|
use std::{fmt::Display, sync::Arc};
|
||||||
|
|
||||||
use smol_str::SmolStr;
|
use smol_str::SmolStr;
|
||||||
|
|
||||||
use crate::Parameter;
|
use crate::{parameter::Parameter, Parameter as FfiParam};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub enum FlagValue {
|
|
||||||
OpaqueString,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FlagValue {
|
|
||||||
fn try_match(&self, input: &str) -> Result<Parameter, FlagValueMatchError> {
|
|
||||||
if input.is_empty() {
|
|
||||||
return Err(FlagValueMatchError::ValueMissing);
|
|
||||||
}
|
|
||||||
|
|
||||||
match self {
|
|
||||||
Self::OpaqueString => Ok(Parameter::OpaqueString { raw: input.into() }),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for FlagValue {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
match self {
|
|
||||||
FlagValue::OpaqueString => write!(f, "value"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum FlagValueMatchError {
|
pub enum FlagValueMatchError {
|
||||||
ValueMissing,
|
ValueMissing,
|
||||||
|
InvalidValue { raw: SmolStr, msg: SmolStr },
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Flag {
|
pub struct Flag {
|
||||||
name: SmolStr,
|
name: SmolStr,
|
||||||
value: Option<FlagValue>,
|
value: Option<Arc<dyn Parameter>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for Flag {
|
impl Display for Flag {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
write!(f, "-{}", self.name)?;
|
write!(f, "-{}", self.name)?;
|
||||||
if let Some(value) = self.value.as_ref() {
|
if let Some(value) = self.value.as_ref() {
|
||||||
write!(f, "={value}")?;
|
write!(f, "=")?;
|
||||||
|
value.format(f, value.default_name())?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
@ -55,7 +32,7 @@ pub enum FlagMatchError {
|
||||||
ValueMatchFailed(FlagValueMatchError),
|
ValueMatchFailed(FlagValueMatchError),
|
||||||
}
|
}
|
||||||
|
|
||||||
type TryMatchFlagResult = Option<Result<Option<Parameter>, FlagMatchError>>;
|
type TryMatchFlagResult = Option<Result<Option<FfiParam>, FlagMatchError>>;
|
||||||
|
|
||||||
impl Flag {
|
impl Flag {
|
||||||
pub fn new(name: impl Into<SmolStr>) -> Self {
|
pub fn new(name: impl Into<SmolStr>) -> Self {
|
||||||
|
|
@ -65,8 +42,8 @@ impl Flag {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_value(mut self, value: FlagValue) -> Self {
|
pub fn with_value(mut self, param: impl Parameter + 'static) -> Self {
|
||||||
self.value = Some(value);
|
self.value = Some(Arc::new(param));
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -74,17 +51,13 @@ impl Flag {
|
||||||
&self.name
|
&self.name
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn value(&self) -> Option<&FlagValue> {
|
|
||||||
self.value.as_ref()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn try_match(&self, input_name: &str, input_value: Option<&str>) -> TryMatchFlagResult {
|
pub fn try_match(&self, input_name: &str, input_value: Option<&str>) -> TryMatchFlagResult {
|
||||||
// if not matching flag then skip anymore matching
|
// if not matching flag then skip anymore matching
|
||||||
if self.name != input_name {
|
if self.name != input_name {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
// get token to try matching with, if flag doesn't have one then that means it is matched (it is without any value)
|
// get token to try matching with, if flag doesn't have one then that means it is matched (it is without any value)
|
||||||
let Some(value) = self.value() else {
|
let Some(value) = self.value.as_deref() else {
|
||||||
return Some(Ok(None));
|
return Some(Ok(None));
|
||||||
};
|
};
|
||||||
// check if we have a non-empty flag value, we return error if not (because flag requested a value)
|
// check if we have a non-empty flag value, we return error if not (because flag requested a value)
|
||||||
|
|
@ -94,9 +67,14 @@ impl Flag {
|
||||||
)));
|
)));
|
||||||
};
|
};
|
||||||
// try matching the value
|
// try matching the value
|
||||||
match value.try_match(input_value) {
|
match value.match_value(input_value) {
|
||||||
Ok(param) => Some(Ok(Some(param))),
|
Ok(param) => Some(Ok(Some(param))),
|
||||||
Err(err) => Some(Err(FlagMatchError::ValueMatchFailed(err))),
|
Err(err) => Some(Err(FlagMatchError::ValueMatchFailed(
|
||||||
|
FlagValueMatchError::InvalidValue {
|
||||||
|
raw: input_value.into(),
|
||||||
|
msg: err,
|
||||||
|
},
|
||||||
|
))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
pub mod commands;
|
pub mod commands;
|
||||||
mod flag;
|
mod flag;
|
||||||
|
mod parameter;
|
||||||
mod string;
|
mod string;
|
||||||
mod token;
|
mod token;
|
||||||
mod tree;
|
mod tree;
|
||||||
|
|
@ -91,7 +92,7 @@ pub fn parse_command(prefix: String, input: String) -> CommandResult {
|
||||||
if let Some(next_tree) = local_tree.get_branch(&found_token) {
|
if let Some(next_tree) = local_tree.get_branch(&found_token) {
|
||||||
local_tree = next_tree.clone();
|
local_tree = next_tree.clone();
|
||||||
} else {
|
} else {
|
||||||
panic!("found token could not match tree, at {input}");
|
panic!("found token {found_token:?} could not match tree, at {input}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some(Err((token, err))) => {
|
Some(Err((token, err))) => {
|
||||||
|
|
@ -114,6 +115,9 @@ pub fn parse_command(prefix: String, input: String) -> CommandResult {
|
||||||
write!(&mut msg, " in command `{prefix}{input} {token}`.").expect("oom");
|
write!(&mut msg, " in command `{prefix}{input} {token}`.").expect("oom");
|
||||||
msg
|
msg
|
||||||
}
|
}
|
||||||
|
TokenMatchError::ParameterMatchError { input: raw, msg } => {
|
||||||
|
format!("Parameter `{raw}` in command `{prefix}{input}` could not be parsed: {msg}.")
|
||||||
|
}
|
||||||
};
|
};
|
||||||
return CommandResult::Err { error: error_msg };
|
return CommandResult::Err { error: error_msg };
|
||||||
}
|
}
|
||||||
|
|
@ -163,17 +167,23 @@ pub fn parse_command(prefix: String, input: String) -> CommandResult {
|
||||||
flags.insert(name.into(), value);
|
flags.insert(name.into(), value);
|
||||||
}
|
}
|
||||||
Err((flag, err)) => {
|
Err((flag, err)) => {
|
||||||
match err {
|
let error = match err {
|
||||||
FlagMatchError::ValueMatchFailed(FlagValueMatchError::ValueMissing) => {
|
FlagMatchError::ValueMatchFailed(FlagValueMatchError::ValueMissing) => {
|
||||||
return CommandResult::Err {
|
format!(
|
||||||
error: format!(
|
"Flag `-{name}` in command `{prefix}{input}` is missing a value, try passing `{flag}`.",
|
||||||
"Flag `-{name}` in command `{prefix}{input}` is missing a value, try passing `-{name}={value}`.",
|
name = flag.name()
|
||||||
name = flag.name(),
|
)
|
||||||
value = flag.value().expect("value missing error cant happen without a value"),
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
FlagMatchError::ValueMatchFailed(
|
||||||
|
FlagValueMatchError::InvalidValue { msg, raw },
|
||||||
|
) => {
|
||||||
|
format!(
|
||||||
|
"Flag `-{name}` in command `{prefix}{input}` has a value (`{raw}`) that could not be parsed: {msg}.",
|
||||||
|
name = flag.name()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return CommandResult::Err { error };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -282,7 +292,8 @@ fn next_token<'a>(
|
||||||
|
|
||||||
// iterate over tokens and run try_match
|
// iterate over tokens and run try_match
|
||||||
for token in possible_tokens {
|
for token in possible_tokens {
|
||||||
let is_match_remaining_token = |token: &Token| matches!(token, Token::OpaqueRemainder(_));
|
let is_match_remaining_token =
|
||||||
|
|token: &Token| matches!(token, Token::Parameter(_, param) if param.remainder());
|
||||||
// check if this is a token that matches the rest of the input
|
// check if this is a token that matches the rest of the input
|
||||||
let match_remaining = is_match_remaining_token(token)
|
let match_remaining = is_match_remaining_token(token)
|
||||||
// check for Any here if it has a "match remainder" token in it
|
// check for Any here if it has a "match remainder" token in it
|
||||||
|
|
@ -296,6 +307,7 @@ fn next_token<'a>(
|
||||||
});
|
});
|
||||||
match token.try_match(input_to_match) {
|
match token.try_match(input_to_match) {
|
||||||
Some(Ok(value)) => {
|
Some(Ok(value)) => {
|
||||||
|
println!("matched token: {}", token);
|
||||||
let next_pos = match matched {
|
let next_pos = match matched {
|
||||||
// return last possible pos if we matched remaining,
|
// return last possible pos if we matched remaining,
|
||||||
Some(_) if match_remaining => input.len(),
|
Some(_) if match_remaining => input.len(),
|
||||||
|
|
|
||||||
314
crates/commands/src/parameter.rs
Normal file
314
crates/commands/src/parameter.rs
Normal file
|
|
@ -0,0 +1,314 @@
|
||||||
|
use std::{fmt::Debug, str::FromStr};
|
||||||
|
|
||||||
|
use smol_str::SmolStr;
|
||||||
|
|
||||||
|
use crate::{ParamName, Parameter as FfiParam};
|
||||||
|
|
||||||
|
pub trait Parameter: Debug + Send + Sync {
|
||||||
|
fn remainder(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
fn default_name(&self) -> ParamName;
|
||||||
|
fn format(&self, f: &mut std::fmt::Formatter, name: &str) -> std::fmt::Result;
|
||||||
|
fn match_value(&self, input: &str) -> Result<FfiParam, SmolStr>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Eq, Hash, PartialEq)]
|
||||||
|
pub struct OpaqueString(bool);
|
||||||
|
|
||||||
|
impl OpaqueString {
|
||||||
|
pub const SINGLE: Self = Self(false);
|
||||||
|
pub const REMAINDER: Self = Self(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parameter for OpaqueString {
|
||||||
|
fn remainder(&self) -> bool {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_name(&self) -> ParamName {
|
||||||
|
"string"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn format(&self, f: &mut std::fmt::Formatter, name: &str) -> std::fmt::Result {
|
||||||
|
write!(f, "[{name}]")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn match_value(&self, input: &str) -> Result<FfiParam, SmolStr> {
|
||||||
|
Ok(FfiParam::OpaqueString { raw: input.into() })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Eq, Hash, PartialEq)]
|
||||||
|
pub struct MemberRef;
|
||||||
|
|
||||||
|
impl Parameter for MemberRef {
|
||||||
|
fn default_name(&self) -> ParamName {
|
||||||
|
"member"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn format(&self, f: &mut std::fmt::Formatter, _: &str) -> std::fmt::Result {
|
||||||
|
write!(f, "<target member>")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn match_value(&self, input: &str) -> Result<FfiParam, SmolStr> {
|
||||||
|
Ok(FfiParam::MemberRef {
|
||||||
|
member: input.into(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Eq, Hash, PartialEq)]
|
||||||
|
pub struct SystemRef;
|
||||||
|
|
||||||
|
impl Parameter for SystemRef {
|
||||||
|
fn default_name(&self) -> ParamName {
|
||||||
|
"system"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn format(&self, f: &mut std::fmt::Formatter, _: &str) -> std::fmt::Result {
|
||||||
|
write!(f, "<target system>")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn match_value(&self, input: &str) -> Result<FfiParam, SmolStr> {
|
||||||
|
Ok(FfiParam::SystemRef {
|
||||||
|
system: input.into(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Eq, Hash, PartialEq)]
|
||||||
|
pub struct MemberPrivacyTarget;
|
||||||
|
|
||||||
|
pub enum MemberPrivacyTargetKind {
|
||||||
|
Visibility,
|
||||||
|
Name,
|
||||||
|
Description,
|
||||||
|
Banner,
|
||||||
|
Avatar,
|
||||||
|
Birthday,
|
||||||
|
Pronouns,
|
||||||
|
Proxy,
|
||||||
|
Metadata,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsRef<str> for MemberPrivacyTargetKind {
|
||||||
|
fn as_ref(&self) -> &str {
|
||||||
|
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",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for MemberPrivacyTargetKind {
|
||||||
|
// todo: figure out how to represent these errors best
|
||||||
|
type Err = SmolStr;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
// todo: this doesnt parse all the possible ways
|
||||||
|
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("invalid member privacy target".into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parameter for MemberPrivacyTarget {
|
||||||
|
fn default_name(&self) -> ParamName {
|
||||||
|
"member_privacy_target"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn format(&self, f: &mut std::fmt::Formatter, _: &str) -> std::fmt::Result {
|
||||||
|
write!(f, "<privacy target>")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn match_value(&self, input: &str) -> Result<FfiParam, SmolStr> {
|
||||||
|
MemberPrivacyTargetKind::from_str(input).map(|target| FfiParam::MemberPrivacyTarget {
|
||||||
|
target: target.as_ref().into(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Eq, Hash, PartialEq)]
|
||||||
|
pub struct PrivacyLevel;
|
||||||
|
|
||||||
|
pub enum PrivacyLevelKind {
|
||||||
|
Public,
|
||||||
|
Private,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsRef<str> for PrivacyLevelKind {
|
||||||
|
fn as_ref(&self) -> &str {
|
||||||
|
match self {
|
||||||
|
Self::Public => "public",
|
||||||
|
Self::Private => "private",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for PrivacyLevelKind {
|
||||||
|
type Err = SmolStr; // todo
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
match s {
|
||||||
|
"public" => Ok(PrivacyLevelKind::Public),
|
||||||
|
"private" => Ok(PrivacyLevelKind::Private),
|
||||||
|
_ => Err("invalid privacy level".into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parameter for PrivacyLevel {
|
||||||
|
fn default_name(&self) -> ParamName {
|
||||||
|
"privacy_level"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn format(&self, f: &mut std::fmt::Formatter, _: &str) -> std::fmt::Result {
|
||||||
|
write!(f, "[privacy level]")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn match_value(&self, input: &str) -> Result<FfiParam, SmolStr> {
|
||||||
|
PrivacyLevelKind::from_str(input).map(|level| FfiParam::PrivacyLevel {
|
||||||
|
level: level.as_ref().into(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Eq, Hash, PartialEq)]
|
||||||
|
pub struct Reset;
|
||||||
|
|
||||||
|
impl AsRef<str> for Reset {
|
||||||
|
fn as_ref(&self) -> &str {
|
||||||
|
"reset"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for Reset {
|
||||||
|
type Err = SmolStr;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
match s {
|
||||||
|
"reset" | "clear" | "default" => Ok(Self),
|
||||||
|
_ => Err("not reset".into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parameter for Reset {
|
||||||
|
fn default_name(&self) -> ParamName {
|
||||||
|
"reset"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn format(&self, f: &mut std::fmt::Formatter, _: &str) -> std::fmt::Result {
|
||||||
|
write!(f, "reset")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn match_value(&self, input: &str) -> Result<FfiParam, SmolStr> {
|
||||||
|
Self::from_str(input).map(|_| FfiParam::Toggle { toggle: true })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Eq, Hash, PartialEq)]
|
||||||
|
pub struct Toggle;
|
||||||
|
|
||||||
|
impl Parameter for Toggle {
|
||||||
|
fn default_name(&self) -> ParamName {
|
||||||
|
"toggle"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn format(&self, f: &mut std::fmt::Formatter, _: &str) -> std::fmt::Result {
|
||||||
|
write!(f, "on/off")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn match_value(&self, input: &str) -> Result<FfiParam, SmolStr> {
|
||||||
|
Enable::from_str(input)
|
||||||
|
.map(Into::<bool>::into)
|
||||||
|
.or_else(|_| Disable::from_str(input).map(Into::<bool>::into))
|
||||||
|
.map(|toggle| FfiParam::Toggle { toggle })
|
||||||
|
.map_err(|_| "invalid toggle".into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Eq, Hash, PartialEq)]
|
||||||
|
pub struct Enable;
|
||||||
|
|
||||||
|
impl FromStr for Enable {
|
||||||
|
type Err = SmolStr;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
match s {
|
||||||
|
"on" | "yes" | "true" | "enable" | "enabled" => Ok(Self),
|
||||||
|
_ => Err("invalid enable".into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parameter for Enable {
|
||||||
|
fn default_name(&self) -> ParamName {
|
||||||
|
"enable"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn format(&self, f: &mut std::fmt::Formatter, _: &str) -> std::fmt::Result {
|
||||||
|
write!(f, "on")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn match_value(&self, input: &str) -> Result<FfiParam, SmolStr> {
|
||||||
|
Self::from_str(input).map(|e| FfiParam::Toggle { toggle: e.into() })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<bool> for Enable {
|
||||||
|
fn into(self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Eq, Hash, PartialEq)]
|
||||||
|
pub struct Disable;
|
||||||
|
|
||||||
|
impl FromStr for Disable {
|
||||||
|
type Err = SmolStr;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
match s {
|
||||||
|
"off" | "no" | "false" | "disable" | "disabled" => Ok(Self),
|
||||||
|
_ => Err("invalid disable".into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<bool> for Disable {
|
||||||
|
fn into(self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parameter for Disable {
|
||||||
|
fn default_name(&self) -> ParamName {
|
||||||
|
"disable"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn format(&self, f: &mut std::fmt::Formatter, _: &str) -> std::fmt::Result {
|
||||||
|
write!(f, "off")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn match_value(&self, input: &str) -> Result<FfiParam, SmolStr> {
|
||||||
|
Self::from_str(input).map(|e| FfiParam::Toggle { toggle: e.into() })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,12 +1,17 @@
|
||||||
use std::{fmt::Display, ops::Not, str::FromStr};
|
use std::{
|
||||||
|
fmt::{Debug, Display},
|
||||||
|
hash::Hash,
|
||||||
|
ops::Not,
|
||||||
|
sync::Arc,
|
||||||
|
};
|
||||||
|
|
||||||
use smol_str::{SmolStr, ToSmolStr};
|
use smol_str::{SmolStr, ToSmolStr};
|
||||||
|
|
||||||
use crate::Parameter;
|
use crate::{parameter::Parameter, Parameter as FfiParam};
|
||||||
|
|
||||||
type ParamName = &'static str;
|
pub type ParamName = &'static str;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Eq, Hash, PartialEq)]
|
#[derive(Debug, Clone)]
|
||||||
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)
|
||||||
// todo: this is likely not the right way to represent this
|
// todo: this is likely not the right way to represent this
|
||||||
|
|
@ -19,33 +24,45 @@ pub enum Token {
|
||||||
/// A bot-defined command / subcommand (usually) (eg. "member" in `pk;member MyName`)
|
/// A bot-defined command / subcommand (usually) (eg. "member" in `pk;member MyName`)
|
||||||
Value(Vec<SmolStr>),
|
Value(Vec<SmolStr>),
|
||||||
|
|
||||||
/// Opaque string (eg. "name" in `pk;member new name`)
|
/// A parameter that must be provided a value
|
||||||
OpaqueString(ParamName),
|
Parameter(ParamName, Arc<dyn Parameter>),
|
||||||
/// Remainder of a command (eg. "desc" in `pk;member <target> description [desc...]`)
|
}
|
||||||
OpaqueRemainder(ParamName),
|
|
||||||
|
|
||||||
/// Member reference (hid or member name)
|
#[macro_export]
|
||||||
MemberRef(ParamName),
|
macro_rules! any {
|
||||||
/// todo: doc
|
($($t:expr),+) => {
|
||||||
MemberPrivacyTarget(ParamName),
|
Token::Any(vec![$(Token::from($t)),+])
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/// System reference
|
impl PartialEq for Token {
|
||||||
SystemRef(ParamName),
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
match (self, other) {
|
||||||
|
(Self::Any(l0), Self::Any(r0)) => l0 == r0,
|
||||||
|
(Self::Value(l0), Self::Value(r0)) => l0 == r0,
|
||||||
|
(Self::Parameter(l0, _), Self::Parameter(r0, _)) => l0 == r0,
|
||||||
|
(Self::Empty, Self::Empty) => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Eq for Token {}
|
||||||
|
|
||||||
/// todo: doc
|
impl Hash for Token {
|
||||||
PrivacyLevel(ParamName),
|
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||||
|
core::mem::discriminant(self).hash(state);
|
||||||
/// on, off; yes, no; true, false
|
match self {
|
||||||
Enable(ParamName),
|
Token::Empty => {}
|
||||||
Disable(ParamName),
|
Token::Any(vec) => vec.hash(state),
|
||||||
Toggle(ParamName),
|
Token::Value(vec) => vec.hash(state),
|
||||||
|
Token::Parameter(name, _) => name.hash(state),
|
||||||
/// reset, clear, default
|
}
|
||||||
Reset(ParamName),
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum TokenMatchError {
|
pub enum TokenMatchError {
|
||||||
|
ParameterMatchError { input: SmolStr, msg: SmolStr },
|
||||||
MissingParameter { name: ParamName },
|
MissingParameter { name: ParamName },
|
||||||
MissingAny { tokens: Vec<Token> },
|
MissingAny { tokens: Vec<Token> },
|
||||||
}
|
}
|
||||||
|
|
@ -53,7 +70,7 @@ pub enum TokenMatchError {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct TokenMatchValue {
|
pub struct TokenMatchValue {
|
||||||
pub raw: SmolStr,
|
pub raw: SmolStr,
|
||||||
pub param: Option<(ParamName, Parameter)>,
|
pub param: Option<(ParamName, FfiParam)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TokenMatchValue {
|
impl TokenMatchValue {
|
||||||
|
|
@ -67,7 +84,7 @@ impl TokenMatchValue {
|
||||||
fn new_match_param(
|
fn new_match_param(
|
||||||
raw: impl Into<SmolStr>,
|
raw: impl Into<SmolStr>,
|
||||||
param_name: ParamName,
|
param_name: ParamName,
|
||||||
param: Parameter,
|
param: FfiParam,
|
||||||
) -> TryMatchResult {
|
) -> TryMatchResult {
|
||||||
Some(Ok(Some(Self {
|
Some(Ok(Some(Self {
|
||||||
raw: raw.into(),
|
raw: raw.into(),
|
||||||
|
|
@ -96,17 +113,8 @@ impl Token {
|
||||||
// empty token
|
// empty token
|
||||||
Self::Empty => Some(Ok(None)),
|
Self::Empty => Some(Ok(None)),
|
||||||
// missing paramaters
|
// missing paramaters
|
||||||
Self::OpaqueRemainder(param_name)
|
Self::Parameter(name, _) => {
|
||||||
| Self::OpaqueString(param_name)
|
Some(Err(TokenMatchError::MissingParameter { name }))
|
||||||
| Self::MemberRef(param_name)
|
|
||||||
| Self::MemberPrivacyTarget(param_name)
|
|
||||||
| Self::SystemRef(param_name)
|
|
||||||
| Self::PrivacyLevel(param_name)
|
|
||||||
| Self::Toggle(param_name)
|
|
||||||
| Self::Enable(param_name)
|
|
||||||
| Self::Disable(param_name)
|
|
||||||
| Self::Reset(param_name) => {
|
|
||||||
Some(Err(TokenMatchError::MissingParameter { name: param_name }))
|
|
||||||
}
|
}
|
||||||
Self::Any(tokens) => tokens.is_empty().then_some(None).unwrap_or_else(|| {
|
Self::Any(tokens) => tokens.is_empty().then_some(None).unwrap_or_else(|| {
|
||||||
Some(Err(TokenMatchError::MissingAny {
|
Some(Err(TokenMatchError::MissingAny {
|
||||||
|
|
@ -134,83 +142,13 @@ impl Token {
|
||||||
.any(|v| v.eq(input))
|
.any(|v| v.eq(input))
|
||||||
.then(|| TokenMatchValue::new_match(input))
|
.then(|| TokenMatchValue::new_match(input))
|
||||||
.unwrap_or(None),
|
.unwrap_or(None),
|
||||||
Self::OpaqueRemainder(param_name) | Self::OpaqueString(param_name) => {
|
Self::Parameter(name, param) => match param.match_value(input) {
|
||||||
TokenMatchValue::new_match_param(
|
Ok(matched) => TokenMatchValue::new_match_param(input, name, matched),
|
||||||
input,
|
Err(err) => Some(Err(TokenMatchError::ParameterMatchError {
|
||||||
param_name,
|
input: input.into(),
|
||||||
Parameter::OpaqueString { raw: input.into() },
|
msg: err,
|
||||||
)
|
})),
|
||||||
}
|
}, // don't add a _ match here!
|
||||||
Self::SystemRef(param_name) => TokenMatchValue::new_match_param(
|
|
||||||
input,
|
|
||||||
param_name,
|
|
||||||
Parameter::SystemRef {
|
|
||||||
system: input.into(),
|
|
||||||
},
|
|
||||||
),
|
|
||||||
Self::MemberRef(param_name) => TokenMatchValue::new_match_param(
|
|
||||||
input,
|
|
||||||
param_name,
|
|
||||||
Parameter::MemberRef {
|
|
||||||
member: input.into(),
|
|
||||||
},
|
|
||||||
),
|
|
||||||
Self::MemberPrivacyTarget(param_name) => match MemberPrivacyTarget::from_str(input) {
|
|
||||||
Ok(target) => TokenMatchValue::new_match_param(
|
|
||||||
input,
|
|
||||||
param_name,
|
|
||||||
Parameter::MemberPrivacyTarget {
|
|
||||||
target: target.as_ref().into(),
|
|
||||||
},
|
|
||||||
),
|
|
||||||
Err(_) => None,
|
|
||||||
},
|
|
||||||
Self::PrivacyLevel(param_name) => match PrivacyLevel::from_str(input) {
|
|
||||||
Ok(level) => TokenMatchValue::new_match_param(
|
|
||||||
input,
|
|
||||||
param_name,
|
|
||||||
Parameter::PrivacyLevel {
|
|
||||||
level: level.as_ref().into(),
|
|
||||||
},
|
|
||||||
),
|
|
||||||
Err(_) => None,
|
|
||||||
},
|
|
||||||
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) => TokenMatchValue::new_match_param(
|
|
||||||
input,
|
|
||||||
param_name,
|
|
||||||
Parameter::Toggle { toggle },
|
|
||||||
),
|
|
||||||
Err(_) => None,
|
|
||||||
},
|
|
||||||
Self::Enable(param_name) => match Enable::from_str(input) {
|
|
||||||
Ok(t) => TokenMatchValue::new_match_param(
|
|
||||||
input,
|
|
||||||
param_name,
|
|
||||||
Parameter::Toggle { toggle: t.into() },
|
|
||||||
),
|
|
||||||
Err(_) => None,
|
|
||||||
},
|
|
||||||
Self::Disable(param_name) => match Disable::from_str(input) {
|
|
||||||
Ok(t) => TokenMatchValue::new_match_param(
|
|
||||||
input,
|
|
||||||
param_name,
|
|
||||||
Parameter::Toggle { toggle: t.into() },
|
|
||||||
),
|
|
||||||
Err(_) => None,
|
|
||||||
},
|
|
||||||
Self::Reset(param_name) => match Reset::from_str(input) {
|
|
||||||
Ok(_) => TokenMatchValue::new_match_param(
|
|
||||||
input,
|
|
||||||
param_name,
|
|
||||||
Parameter::Toggle { toggle: true },
|
|
||||||
),
|
|
||||||
Err(_) => None,
|
|
||||||
},
|
|
||||||
// don't add a _ match here!
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -231,17 +169,7 @@ impl Display for Token {
|
||||||
}
|
}
|
||||||
Token::Value(vec) if vec.is_empty().not() => write!(f, "{}", vec.first().unwrap()),
|
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
|
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::Parameter(name, param) => param.format(f, name),
|
||||||
Token::OpaqueRemainder(param_name) => write!(f, "[{}...]", param_name),
|
|
||||||
Token::OpaqueString(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"),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -252,163 +180,31 @@ impl From<&str> for Token {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<const L: usize> From<[&str; L]> for Token {
|
impl<P: Parameter + 'static> From<P> for Token {
|
||||||
fn from(value: [&str; L]) -> Self {
|
fn from(value: P) -> Self {
|
||||||
Token::Value(value.into_iter().map(|s| s.to_smolstr()).collect())
|
Token::Parameter(value.default_name(), Arc::new(value))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<const L: usize> From<[Token; L]> for Token {
|
impl<P: Parameter + 'static> From<(ParamName, P)> for Token {
|
||||||
fn from(value: [Token; L]) -> Self {
|
fn from(value: (ParamName, P)) -> Self {
|
||||||
Token::Any(value.into_iter().map(|s| s.clone()).collect())
|
Token::Parameter(value.0, Arc::new(value.1))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Eq, Hash, PartialEq)]
|
impl<const L: usize, T: Into<Token>> From<[T; L]> for Token {
|
||||||
pub enum MemberPrivacyTarget {
|
fn from(value: [T; L]) -> Self {
|
||||||
Visibility,
|
let tokens = value.into_iter().map(|s| s.into()).collect::<Vec<_>>();
|
||||||
Name,
|
if tokens.iter().all(|t| matches!(t, Token::Value(_))) {
|
||||||
Description,
|
let values = tokens
|
||||||
Banner,
|
.into_iter()
|
||||||
Avatar,
|
.flat_map(|t| match t {
|
||||||
Birthday,
|
Token::Value(v) => v,
|
||||||
Pronouns,
|
_ => unreachable!(),
|
||||||
Proxy,
|
})
|
||||||
Metadata,
|
.collect::<Vec<_>>();
|
||||||
}
|
return Token::Value(values);
|
||||||
|
|
||||||
impl AsRef<str> for MemberPrivacyTarget {
|
|
||||||
fn as_ref(&self) -> &str {
|
|
||||||
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",
|
|
||||||
}
|
}
|
||||||
}
|
Token::Any(tokens)
|
||||||
}
|
|
||||||
|
|
||||||
impl FromStr for MemberPrivacyTarget {
|
|
||||||
// todo: figure out how to represent these errors best
|
|
||||||
type Err = ();
|
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
||||||
// todo: this doesnt parse all the possible ways
|
|
||||||
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(()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Eq, Hash, PartialEq)]
|
|
||||||
pub enum PrivacyLevel {
|
|
||||||
Public,
|
|
||||||
Private,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsRef<str> for PrivacyLevel {
|
|
||||||
fn as_ref(&self) -> &str {
|
|
||||||
match self {
|
|
||||||
Self::Public => "public",
|
|
||||||
Self::Private => "private",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromStr for PrivacyLevel {
|
|
||||||
type Err = (); // todo
|
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
||||||
match s {
|
|
||||||
"public" => Ok(Self::Public),
|
|
||||||
"private" => Ok(Self::Private),
|
|
||||||
_ => Err(()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Eq, Hash, PartialEq)]
|
|
||||||
pub struct Reset;
|
|
||||||
|
|
||||||
impl AsRef<str> for Reset {
|
|
||||||
fn as_ref(&self) -> &str {
|
|
||||||
"reset"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromStr for Reset {
|
|
||||||
type Err = ();
|
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
||||||
match s {
|
|
||||||
"reset" | "clear" | "default" => Ok(Self),
|
|
||||||
_ => Err(()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue