mirror of
https://github.com/PluralKit/PluralKit.git
synced 2026-02-04 04:56:49 +00:00
better parameters handling, implement import export
This commit is contained in:
parent
e4f38c76a9
commit
5198f7d83b
19 changed files with 250 additions and 174 deletions
|
|
@ -245,6 +245,8 @@ public partial class CommandTree
|
||||||
Commands.MessageDelete(var param, var flags) => ctx.Execute<ProxiedMessage>(Message, m => m.GetMessage(ctx, param.target.MessageId, flags.GetReplyFormat(), true, false)),
|
Commands.MessageDelete(var param, var flags) => ctx.Execute<ProxiedMessage>(Message, m => m.GetMessage(ctx, param.target.MessageId, flags.GetReplyFormat(), true, false)),
|
||||||
Commands.MessageEdit(var param, var flags) => ctx.Execute<ProxiedMessage>(MessageEdit, m => m.EditMessage(ctx, param.target.MessageId, param.new_content, flags.regex, flags.mutate_space, flags.append, flags.prepend, flags.clear_embeds, flags.clear_attachments)),
|
Commands.MessageEdit(var param, var flags) => ctx.Execute<ProxiedMessage>(MessageEdit, m => m.EditMessage(ctx, param.target.MessageId, param.new_content, flags.regex, flags.mutate_space, flags.append, flags.prepend, flags.clear_embeds, flags.clear_attachments)),
|
||||||
Commands.MessageReproxy(var param, _) => ctx.Execute<ProxiedMessage>(MessageReproxy, m => m.ReproxyMessage(ctx, param.target.MessageId)),
|
Commands.MessageReproxy(var param, _) => ctx.Execute<ProxiedMessage>(MessageReproxy, m => m.ReproxyMessage(ctx, param.target.MessageId)),
|
||||||
|
Commands.Import(var param, _) => ctx.Execute<ImportExport>(Import, m => m.Import(ctx, param.url)),
|
||||||
|
Commands.Export(_, _) => ctx.Execute<ImportExport>(Export, m => m.Export(ctx)),
|
||||||
_ =>
|
_ =>
|
||||||
// this should only ever occur when deving if commands are not implemented...
|
// this should only ever occur when deving if commands are not implemented...
|
||||||
ctx.Reply(
|
ctx.Reply(
|
||||||
|
|
@ -256,10 +258,6 @@ public partial class CommandTree
|
||||||
return HandleConfigCommand(ctx);
|
return HandleConfigCommand(ctx);
|
||||||
if (ctx.Match("serverconfig", "guildconfig", "scfg"))
|
if (ctx.Match("serverconfig", "guildconfig", "scfg"))
|
||||||
return HandleServerConfigCommand(ctx);
|
return HandleServerConfigCommand(ctx);
|
||||||
if (ctx.Match("import"))
|
|
||||||
return ctx.Execute<ImportExport>(Import, m => m.Import(ctx));
|
|
||||||
if (ctx.Match("export"))
|
|
||||||
return ctx.Execute<ImportExport>(Export, m => m.Export(ctx));
|
|
||||||
if (ctx.Match("log"))
|
if (ctx.Match("log"))
|
||||||
if (ctx.Match("channel"))
|
if (ctx.Match("channel"))
|
||||||
return ctx.Execute<ServerConfig>(LogChannel, m => m.SetLogChannel(ctx), true);
|
return ctx.Execute<ServerConfig>(LogChannel, m => m.SetLogChannel(ctx), true);
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ public static class ContextParametersExt
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<List<PKMember>> ParamResolveMembers(this Context ctx, string param_name)
|
public static async Task<List<PKMember>?> ParamResolveMembers(this Context ctx, string param_name)
|
||||||
{
|
{
|
||||||
return await ctx.Parameters.ResolveParameter(
|
return await ctx.Parameters.ResolveParameter(
|
||||||
ctx, param_name,
|
ctx, param_name,
|
||||||
|
|
@ -36,7 +36,7 @@ public static class ContextParametersExt
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<List<PKGroup>> ParamResolveGroups(this Context ctx, string param_name)
|
public static async Task<List<PKGroup>?> ParamResolveGroups(this Context ctx, string param_name)
|
||||||
{
|
{
|
||||||
return await ctx.Parameters.ResolveParameter(
|
return await ctx.Parameters.ResolveParameter(
|
||||||
ctx, param_name,
|
ctx, param_name,
|
||||||
|
|
|
||||||
|
|
@ -126,6 +126,8 @@ public class Parameters
|
||||||
return new Parameter.ChannelRef(await ctx.Rest.GetChannelOrNull(channelId) ?? throw new PKError($"Channel {channelId} not found"));
|
return new Parameter.ChannelRef(await ctx.Rest.GetChannelOrNull(channelId) ?? throw new PKError($"Channel {channelId} not found"));
|
||||||
case uniffi.commands.Parameter.GuildRef(var guildId):
|
case uniffi.commands.Parameter.GuildRef(var guildId):
|
||||||
return new Parameter.GuildRef(await ctx.Rest.GetGuildOrNull(guildId) ?? throw new PKError($"Guild {guildId} not found"));
|
return new Parameter.GuildRef(await ctx.Rest.GetGuildOrNull(guildId) ?? throw new PKError($"Guild {guildId} not found"));
|
||||||
|
case uniffi.commands.Parameter.Null:
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,9 +31,9 @@ public class ImportExport
|
||||||
_dmCache = dmCache;
|
_dmCache = dmCache;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Import(Context ctx)
|
public async Task Import(Context ctx, string? inputUrl)
|
||||||
{
|
{
|
||||||
var inputUrl = ctx.RemainderOrNull() ?? ctx.Message.Attachments.FirstOrDefault()?.Url;
|
inputUrl = inputUrl ?? ctx.Message.Attachments.FirstOrDefault()?.Url;
|
||||||
if (inputUrl == null) throw Errors.NoImportFilePassed;
|
if (inputUrl == null) throw Errors.NoImportFilePassed;
|
||||||
|
|
||||||
if (!Core.MiscUtils.TryMatchUri(inputUrl, out var url))
|
if (!Core.MiscUtils.TryMatchUri(inputUrl, out var url))
|
||||||
|
|
|
||||||
|
|
@ -23,8 +23,9 @@ public class Switch
|
||||||
await DoSwitchCommand(ctx, []);
|
await DoSwitchCommand(ctx, []);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task DoSwitchCommand(Context ctx, ICollection<PKMember> members)
|
private async Task DoSwitchCommand(Context ctx, ICollection<PKMember>? members)
|
||||||
{
|
{
|
||||||
|
if (members == null) members = new List<PKMember>();
|
||||||
// Make sure there are no dupes in the list
|
// Make sure there are no dupes in the list
|
||||||
// We do this by checking if removing duplicate member IDs results in a list of different length
|
// We do this by checking if removing duplicate member IDs results in a list of different length
|
||||||
if (members.Select(m => m.Id).Distinct().Count() != members.Count) throw Errors.DuplicateSwitchMembers;
|
if (members.Select(m => m.Id).Distinct().Count() != members.Count) throw Errors.DuplicateSwitchMembers;
|
||||||
|
|
@ -101,10 +102,12 @@ public class Switch
|
||||||
await ctx.Reply($"{Emojis.Success} Switch moved to <t:{newSwitchTime}> ({newSwitchDeltaStr} ago).");
|
await ctx.Reply($"{Emojis.Success} Switch moved to <t:{newSwitchTime}> ({newSwitchDeltaStr} ago).");
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task SwitchEdit(Context ctx, List<PKMember> newMembers, bool newSwitch = false, bool first = false, bool remove = false, bool append = false, bool prepend = false)
|
public async Task SwitchEdit(Context ctx, List<PKMember>? newMembers, bool newSwitch = false, bool first = false, bool remove = false, bool append = false, bool prepend = false)
|
||||||
{
|
{
|
||||||
ctx.CheckSystem();
|
ctx.CheckSystem();
|
||||||
|
|
||||||
|
if (newMembers == null) newMembers = new List<PKMember>();
|
||||||
|
|
||||||
await using var conn = await ctx.Database.Obtain();
|
await using var conn = await ctx.Database.Obtain();
|
||||||
var currentSwitch = await ctx.Repository.GetLatestSwitch(ctx.System.Id);
|
var currentSwitch = await ctx.Repository.GetLatestSwitch(ctx.System.Id);
|
||||||
if (currentSwitch == null)
|
if (currentSwitch == null)
|
||||||
|
|
@ -170,8 +173,10 @@ public class Switch
|
||||||
await DoEditCommand(ctx, []);
|
await DoEditCommand(ctx, []);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task DoEditCommand(Context ctx, ICollection<PKMember> members)
|
public async Task DoEditCommand(Context ctx, ICollection<PKMember>? members)
|
||||||
{
|
{
|
||||||
|
if (members == null) members = new List<PKMember>();
|
||||||
|
|
||||||
// Make sure there are no dupes in the list
|
// Make sure there are no dupes in the list
|
||||||
// We do this by checking if removing duplicate member IDs results in a list of different length
|
// We do this by checking if removing duplicate member IDs results in a list of different length
|
||||||
if (members.Select(m => m.Id).Distinct().Count() != members.Count) throw Errors.DuplicateSwitchMembers;
|
if (members.Select(m => m.Id).Distinct().Count() != members.Count) throw Errors.DuplicateSwitchMembers;
|
||||||
|
|
|
||||||
|
|
@ -158,9 +158,9 @@ pub fn cmds() -> impl Iterator<Item = Command> {
|
||||||
.map(apply_list_opts);
|
.map(apply_list_opts);
|
||||||
|
|
||||||
let group_modify_members_cmd = [
|
let group_modify_members_cmd = [
|
||||||
command!(group_target, "add", MemberRefs => "group_add_member")
|
command!(group_target, "add", Optional(MemberRefs) => "group_add_member")
|
||||||
.flag(("all", ["a"])),
|
.flag(("all", ["a"])),
|
||||||
command!(group_target, ("remove", ["delete", "del", "rem"]), MemberRefs => "group_remove_member")
|
command!(group_target, ("remove", ["delete", "del", "rem"]), Optional(MemberRefs) => "group_remove_member")
|
||||||
.flag(("all", ["a"])),
|
.flag(("all", ["a"])),
|
||||||
]
|
]
|
||||||
.into_iter();
|
.into_iter();
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ 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!(("dashboard", ["dash"]) => "dashboard"),
|
||||||
command!("explain" => "explain"),
|
command!("explain" => "explain"),
|
||||||
command!(help => "help")
|
command!(help => "help")
|
||||||
.flag(("foo", OpaqueString)) // todo: just for testing
|
.flag(("foo", OpaqueString)) // todo: just for testing
|
||||||
|
|
|
||||||
|
|
@ -1 +1,9 @@
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
pub fn cmds() -> impl Iterator<Item = Command> {
|
||||||
|
[
|
||||||
|
command!("import", Optional(("url", OpaqueStringRemainder)) => "import"),
|
||||||
|
command!("export" => "export"),
|
||||||
|
]
|
||||||
|
.into_iter()
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,12 @@ pub mod system;
|
||||||
|
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
|
|
||||||
use command_parser::{command, command::Command, parameter::ParameterKind::*, tokens};
|
use command_parser::{
|
||||||
|
command,
|
||||||
|
command::Command,
|
||||||
|
parameter::{Optional, Parameter, ParameterKind::*, Remainder, Skip},
|
||||||
|
tokens,
|
||||||
|
};
|
||||||
|
|
||||||
pub fn all() -> impl Iterator<Item = Command> {
|
pub fn all() -> impl Iterator<Item = Command> {
|
||||||
(help::cmds())
|
(help::cmds())
|
||||||
|
|
@ -34,6 +39,7 @@ pub fn all() -> impl Iterator<Item = Command> {
|
||||||
.chain(autoproxy::cmds())
|
.chain(autoproxy::cmds())
|
||||||
.chain(debug::cmds())
|
.chain(debug::cmds())
|
||||||
.chain(message::cmds())
|
.chain(message::cmds())
|
||||||
|
.chain(import_export::cmds())
|
||||||
.map(|cmd| {
|
.map(|cmd| {
|
||||||
cmd.hidden_flag(("plaintext", ["pt"]))
|
cmd.hidden_flag(("plaintext", ["pt"]))
|
||||||
.hidden_flag(("raw", ["r"]))
|
.hidden_flag(("raw", ["r"]))
|
||||||
|
|
|
||||||
|
|
@ -182,11 +182,11 @@ pub fn cmds() -> impl Iterator<Item = Command> {
|
||||||
[
|
[
|
||||||
command!(member_keep_proxy => "member_keepproxy_show")
|
command!(member_keep_proxy => "member_keepproxy_show")
|
||||||
.help("Shows a member's keep-proxy setting"),
|
.help("Shows a member's keep-proxy setting"),
|
||||||
command!(member_keep_proxy, ("value", Toggle) => "member_keepproxy_update")
|
command!(member_keep_proxy, Skip(("value", Toggle)) => "member_keepproxy_update")
|
||||||
.help("Changes a member's keep-proxy setting"),
|
.help("Changes a member's keep-proxy setting"),
|
||||||
command!(member_server_keep_proxy => "member_server_keepproxy_show")
|
command!(member_server_keep_proxy => "member_server_keepproxy_show")
|
||||||
.help("Shows a member's server-specific keep-proxy setting"),
|
.help("Shows a member's server-specific keep-proxy setting"),
|
||||||
command!(member_server_keep_proxy, ("value", Toggle) => "member_server_keepproxy_update")
|
command!(member_server_keep_proxy, Skip(("value", Toggle)) => "member_server_keepproxy_update")
|
||||||
.help("Changes a member's server-specific keep-proxy setting"),
|
.help("Changes a member's server-specific keep-proxy setting"),
|
||||||
command!(member_server_keep_proxy, ("clear", ["c"]) => "member_server_keepproxy_clear")
|
command!(member_server_keep_proxy, ("clear", ["c"]) => "member_server_keepproxy_clear")
|
||||||
.flag(("yes", ["y"]))
|
.flag(("yes", ["y"]))
|
||||||
|
|
@ -303,9 +303,9 @@ pub fn cmds() -> impl Iterator<Item = Command> {
|
||||||
.map(|cmd| cmd.flags(get_list_flags()));
|
.map(|cmd| cmd.flags(get_list_flags()));
|
||||||
|
|
||||||
let member_add_remove_group_cmds = [
|
let member_add_remove_group_cmds = [
|
||||||
command!(member_group, "add", ("groups", GroupRefs) => "member_group_add")
|
command!(member_group, "add", Optional(("groups", GroupRefs)) => "member_group_add")
|
||||||
.help("Adds a member to one or more groups"),
|
.help("Adds a member to one or more groups"),
|
||||||
command!(member_group, ("remove", ["rem"]), ("groups", GroupRefs) => "member_group_remove")
|
command!(member_group, ("remove", ["rem"]), Optional(("groups", GroupRefs)) => "member_group_remove")
|
||||||
.help("Removes a member from one or more groups"),
|
.help("Removes a member from one or more groups"),
|
||||||
]
|
]
|
||||||
.into_iter();
|
.into_iter();
|
||||||
|
|
|
||||||
|
|
@ -9,23 +9,22 @@ pub fn cmds() -> impl Iterator<Item = Command> {
|
||||||
let copy = ("copy", ["add", "duplicate", "dupe"]);
|
let copy = ("copy", ["add", "duplicate", "dupe"]);
|
||||||
let out = "out";
|
let out = "out";
|
||||||
|
|
||||||
|
let edit_flags = [
|
||||||
|
("first", ["f"]),
|
||||||
|
("remove", ["r"]),
|
||||||
|
("append", ["a"]),
|
||||||
|
("prepend", ["p"]),
|
||||||
|
];
|
||||||
|
|
||||||
[
|
[
|
||||||
command!(switch, out => "switch_out"),
|
command!(switch, out => "switch_out"),
|
||||||
command!(switch, r#move, OpaqueString => "switch_move"), // TODO: datetime parsing
|
command!(switch, r#move, OpaqueString => "switch_move"), // TODO: datetime parsing
|
||||||
command!(switch, delete => "switch_delete").flag(("all", ["clear", "c"])),
|
command!(switch, delete => "switch_delete").flag(("all", ["clear", "c"])),
|
||||||
command!(switch, edit, out => "switch_edit_out"),
|
command!(switch, edit, out => "switch_edit_out"),
|
||||||
command!(switch, edit, MemberRefs => "switch_edit")
|
command!(switch, edit, Optional(MemberRefs) => "switch_edit").flags(edit_flags),
|
||||||
.flag(("first", ["f"]))
|
command!(switch, copy, Optional(MemberRefs) => "switch_copy").flags(edit_flags),
|
||||||
.flag(("remove", ["r"]))
|
|
||||||
.flag(("append", ["a"]))
|
|
||||||
.flag(("prepend", ["p"])),
|
|
||||||
command!(switch, copy, MemberRefs => "switch_copy")
|
|
||||||
.flag(("first", ["f"]))
|
|
||||||
.flag(("remove", ["r"]))
|
|
||||||
.flag(("append", ["a"]))
|
|
||||||
.flag(("prepend", ["p"])),
|
|
||||||
command!(switch, ("commands", ["help"]) => "switch_commands"),
|
command!(switch, ("commands", ["help"]) => "switch_commands"),
|
||||||
command!(switch, MemberRefs => "switch_do"),
|
command!(switch, Optional(MemberRefs) => "switch_do"),
|
||||||
]
|
]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -220,7 +220,7 @@ pub fn edit() -> impl Iterator<Item = Command> {
|
||||||
let system_proxy_cmd = [
|
let system_proxy_cmd = [
|
||||||
command!(system_proxy => "system_show_proxy_current")
|
command!(system_proxy => "system_show_proxy_current")
|
||||||
.help("Shows your system's proxy setting for the guild you are in"),
|
.help("Shows your system's proxy setting for the guild you are in"),
|
||||||
command!(system_proxy, Toggle => "system_toggle_proxy_current")
|
command!(system_proxy, Skip(Toggle) => "system_toggle_proxy_current")
|
||||||
.help("Toggle your system's proxy for the guild you are in"),
|
.help("Toggle your system's proxy for the guild you are in"),
|
||||||
command!(system_proxy, GuildRef => "system_show_proxy")
|
command!(system_proxy, GuildRef => "system_show_proxy")
|
||||||
.help("Shows your system's proxy setting for a guild"),
|
.help("Shows your system's proxy setting for a guild"),
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ pub enum FlagValueMatchError {
|
||||||
pub struct Flag {
|
pub struct Flag {
|
||||||
name: SmolStr,
|
name: SmolStr,
|
||||||
aliases: Vec<SmolStr>,
|
aliases: Vec<SmolStr>,
|
||||||
value: Option<ParameterKind>,
|
value: Option<Parameter>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for Flag {
|
impl Display for Flag {
|
||||||
|
|
@ -22,7 +22,7 @@ impl Display for Flag {
|
||||||
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, "=")?;
|
write!(f, "=")?;
|
||||||
Parameter::from(*value).fmt(f)?;
|
value.fmt(f)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
@ -58,8 +58,8 @@ impl Flag {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn value(mut self, param: ParameterKind) -> Self {
|
pub fn value(mut self, param: impl Into<Parameter>) -> Self {
|
||||||
self.value = Some(param);
|
self.value = Some(param.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -72,8 +72,8 @@ impl Flag {
|
||||||
&self.name
|
&self.name
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_value(&self) -> Option<ParameterKind> {
|
pub fn get_value(&self) -> Option<&Parameter> {
|
||||||
self.value
|
self.value.as_ref()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_aliases(&self) -> impl Iterator<Item = &str> {
|
pub fn get_aliases(&self) -> impl Iterator<Item = &str> {
|
||||||
|
|
|
||||||
|
|
@ -242,7 +242,7 @@ 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 =
|
let is_match_remaining_token =
|
||||||
|token: &Token| matches!(token, Token::Parameter(param) if param.kind().remainder());
|
|token: &Token| matches!(token, Token::Parameter(param) if param.is_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);
|
||||||
// either use matched param or rest of the input if matching remaining
|
// either use matched param or rest of the input if matching remaining
|
||||||
|
|
|
||||||
|
|
@ -24,12 +24,23 @@ pub enum ParameterValue {
|
||||||
PrivacyLevel(String),
|
PrivacyLevel(String),
|
||||||
Toggle(bool),
|
Toggle(bool),
|
||||||
Avatar(String),
|
Avatar(String),
|
||||||
|
Null,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_remainder(kind: ParameterKind) -> bool {
|
||||||
|
matches!(
|
||||||
|
kind,
|
||||||
|
ParameterKind::OpaqueStringRemainder | ParameterKind::MemberRefs | ParameterKind::GroupRefs
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
pub struct Parameter {
|
pub struct Parameter {
|
||||||
name: SmolStr,
|
name: SmolStr,
|
||||||
kind: ParameterKind,
|
kind: ParameterKind,
|
||||||
|
remainder: bool,
|
||||||
|
optional: bool,
|
||||||
|
skip: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Parameter {
|
impl Parameter {
|
||||||
|
|
@ -40,106 +51,36 @@ impl Parameter {
|
||||||
pub fn kind(&self) -> ParameterKind {
|
pub fn kind(&self) -> ParameterKind {
|
||||||
self.kind
|
self.kind
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for Parameter {
|
pub fn remainder(mut self) -> Self {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
self.remainder = true;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn optional(mut self) -> Self {
|
||||||
|
self.optional = true;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn skip(mut self) -> Self {
|
||||||
|
self.skip = true;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_remainder(&self) -> bool {
|
||||||
|
self.remainder
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_optional(&self) -> bool {
|
||||||
|
self.optional
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_skip(&self) -> bool {
|
||||||
|
self.skip
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn match_value(&self, input: &str) -> Result<ParameterValue, SmolStr> {
|
||||||
match self.kind {
|
match self.kind {
|
||||||
ParameterKind::OpaqueString => {
|
|
||||||
write!(f, "[{}]", self.name)
|
|
||||||
}
|
|
||||||
ParameterKind::OpaqueStringRemainder => {
|
|
||||||
write!(f, "[{}]...", self.name)
|
|
||||||
}
|
|
||||||
ParameterKind::MemberRef => write!(f, "<target member>"),
|
|
||||||
ParameterKind::MemberRefs => write!(f, "<member 1> <member 2> <member 3>..."),
|
|
||||||
ParameterKind::GroupRef => write!(f, "<target group>"),
|
|
||||||
ParameterKind::GroupRefs => write!(f, "<group 1> <group 2> <group 3>..."),
|
|
||||||
ParameterKind::SystemRef => write!(f, "<target system>"),
|
|
||||||
ParameterKind::MessageRef => write!(f, "<target message>"),
|
|
||||||
ParameterKind::ChannelRef => write!(f, "<target channel>"),
|
|
||||||
ParameterKind::GuildRef => write!(f, "<target guild>"),
|
|
||||||
ParameterKind::MemberPrivacyTarget => write!(f, "<privacy target>"),
|
|
||||||
ParameterKind::GroupPrivacyTarget => write!(f, "<privacy target>"),
|
|
||||||
ParameterKind::SystemPrivacyTarget => write!(f, "<privacy target>"),
|
|
||||||
ParameterKind::PrivacyLevel => write!(f, "[privacy level]"),
|
|
||||||
ParameterKind::Toggle => write!(f, "on/off"),
|
|
||||||
ParameterKind::Avatar => write!(f, "<url|@mention>"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<ParameterKind> for Parameter {
|
|
||||||
fn from(value: ParameterKind) -> Self {
|
|
||||||
Parameter {
|
|
||||||
name: value.default_name().into(),
|
|
||||||
kind: value,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<(&str, ParameterKind)> for Parameter {
|
|
||||||
fn from((name, kind): (&str, ParameterKind)) -> Self {
|
|
||||||
Parameter {
|
|
||||||
name: name.into(),
|
|
||||||
kind,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
|
||||||
pub enum ParameterKind {
|
|
||||||
OpaqueString,
|
|
||||||
OpaqueStringRemainder,
|
|
||||||
MemberRef,
|
|
||||||
MemberRefs,
|
|
||||||
GroupRef,
|
|
||||||
GroupRefs,
|
|
||||||
SystemRef,
|
|
||||||
MessageRef,
|
|
||||||
ChannelRef,
|
|
||||||
GuildRef,
|
|
||||||
MemberPrivacyTarget,
|
|
||||||
GroupPrivacyTarget,
|
|
||||||
SystemPrivacyTarget,
|
|
||||||
PrivacyLevel,
|
|
||||||
Toggle,
|
|
||||||
Avatar,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ParameterKind {
|
|
||||||
pub(crate) fn default_name(&self) -> &str {
|
|
||||||
match self {
|
|
||||||
ParameterKind::OpaqueString => "string",
|
|
||||||
ParameterKind::OpaqueStringRemainder => "string",
|
|
||||||
ParameterKind::MemberRef => "target",
|
|
||||||
ParameterKind::MemberRefs => "targets",
|
|
||||||
ParameterKind::GroupRef => "target",
|
|
||||||
ParameterKind::GroupRefs => "targets",
|
|
||||||
ParameterKind::SystemRef => "target",
|
|
||||||
ParameterKind::MessageRef => "target",
|
|
||||||
ParameterKind::ChannelRef => "target",
|
|
||||||
ParameterKind::GuildRef => "target",
|
|
||||||
ParameterKind::MemberPrivacyTarget => "member_privacy_target",
|
|
||||||
ParameterKind::GroupPrivacyTarget => "group_privacy_target",
|
|
||||||
ParameterKind::SystemPrivacyTarget => "system_privacy_target",
|
|
||||||
ParameterKind::PrivacyLevel => "privacy_level",
|
|
||||||
ParameterKind::Toggle => "toggle",
|
|
||||||
ParameterKind::Avatar => "avatar",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn remainder(&self) -> bool {
|
|
||||||
matches!(
|
|
||||||
self,
|
|
||||||
ParameterKind::OpaqueStringRemainder
|
|
||||||
| ParameterKind::MemberRefs
|
|
||||||
| ParameterKind::GroupRefs
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn match_value(&self, input: &str) -> Result<ParameterValue, SmolStr> {
|
|
||||||
match self {
|
|
||||||
// TODO: actually parse image url
|
// TODO: actually parse image url
|
||||||
ParameterKind::OpaqueString | ParameterKind::OpaqueStringRemainder => {
|
ParameterKind::OpaqueString | ParameterKind::OpaqueStringRemainder => {
|
||||||
Ok(ParameterValue::OpaqueString(input.into()))
|
Ok(ParameterValue::OpaqueString(input.into()))
|
||||||
|
|
@ -217,13 +158,130 @@ impl ParameterKind {
|
||||||
.map_err(|_| SmolStr::new("invalid guild ID")),
|
.map_err(|_| SmolStr::new("invalid guild ID")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn skip_if_cant_match(&self) -> Option<Option<ParameterValue>> {
|
impl Display for Parameter {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self.kind {
|
||||||
|
ParameterKind::OpaqueString => {
|
||||||
|
write!(f, "[{}]", self.name)
|
||||||
|
}
|
||||||
|
ParameterKind::OpaqueStringRemainder => {
|
||||||
|
write!(f, "[{}]...", self.name)
|
||||||
|
}
|
||||||
|
ParameterKind::MemberRef => write!(f, "<target member>"),
|
||||||
|
ParameterKind::MemberRefs => write!(f, "<member 1> <member 2> <member 3>..."),
|
||||||
|
ParameterKind::GroupRef => write!(f, "<target group>"),
|
||||||
|
ParameterKind::GroupRefs => write!(f, "<group 1> <group 2> <group 3>..."),
|
||||||
|
ParameterKind::SystemRef => write!(f, "<target system>"),
|
||||||
|
ParameterKind::MessageRef => write!(f, "<target message>"),
|
||||||
|
ParameterKind::ChannelRef => write!(f, "<target channel>"),
|
||||||
|
ParameterKind::GuildRef => write!(f, "<target guild>"),
|
||||||
|
ParameterKind::MemberPrivacyTarget => write!(f, "<privacy target>"),
|
||||||
|
ParameterKind::GroupPrivacyTarget => write!(f, "<privacy target>"),
|
||||||
|
ParameterKind::SystemPrivacyTarget => write!(f, "<privacy target>"),
|
||||||
|
ParameterKind::PrivacyLevel => write!(f, "[privacy level]"),
|
||||||
|
ParameterKind::Toggle => write!(f, "on/off"),
|
||||||
|
ParameterKind::Avatar => write!(f, "<url|@mention>"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ParameterKind> for Parameter {
|
||||||
|
fn from(value: ParameterKind) -> Self {
|
||||||
|
Parameter {
|
||||||
|
name: value.default_name().into(),
|
||||||
|
kind: value,
|
||||||
|
remainder: is_remainder(value),
|
||||||
|
optional: false,
|
||||||
|
skip: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<(&str, ParameterKind)> for Parameter {
|
||||||
|
fn from((name, kind): (&str, ParameterKind)) -> Self {
|
||||||
|
Parameter {
|
||||||
|
name: name.into(),
|
||||||
|
kind,
|
||||||
|
remainder: is_remainder(kind),
|
||||||
|
optional: false,
|
||||||
|
skip: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Optional<P: Into<Parameter>>(pub P);
|
||||||
|
|
||||||
|
impl<P: Into<Parameter>> From<Optional<P>> for Parameter {
|
||||||
|
fn from(value: Optional<P>) -> Self {
|
||||||
|
let p = value.0.into();
|
||||||
|
p.optional()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Remainder<P: Into<Parameter>>(pub P);
|
||||||
|
|
||||||
|
impl<P: Into<Parameter>> From<Remainder<P>> for Parameter {
|
||||||
|
fn from(value: Remainder<P>) -> Self {
|
||||||
|
let p = value.0.into();
|
||||||
|
p.remainder()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo(dusk): this is kind of annoying to use, should probably introduce
|
||||||
|
// a way to match multiple parameters in a single parameter
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Skip<P: Into<Parameter>>(pub P);
|
||||||
|
|
||||||
|
impl<P: Into<Parameter>> From<Skip<P>> for Parameter {
|
||||||
|
fn from(value: Skip<P>) -> Self {
|
||||||
|
let p = value.0.into();
|
||||||
|
p.skip()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
pub enum ParameterKind {
|
||||||
|
OpaqueString,
|
||||||
|
OpaqueStringRemainder,
|
||||||
|
MemberRef,
|
||||||
|
MemberRefs,
|
||||||
|
GroupRef,
|
||||||
|
GroupRefs,
|
||||||
|
SystemRef,
|
||||||
|
MessageRef,
|
||||||
|
ChannelRef,
|
||||||
|
GuildRef,
|
||||||
|
MemberPrivacyTarget,
|
||||||
|
GroupPrivacyTarget,
|
||||||
|
SystemPrivacyTarget,
|
||||||
|
PrivacyLevel,
|
||||||
|
Toggle,
|
||||||
|
Avatar,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ParameterKind {
|
||||||
|
pub(crate) fn default_name(&self) -> &str {
|
||||||
match self {
|
match self {
|
||||||
ParameterKind::Toggle => Some(None),
|
ParameterKind::OpaqueString => "string",
|
||||||
ParameterKind::MemberRefs => Some(Some(ParameterValue::MemberRefs(Vec::new()))),
|
ParameterKind::OpaqueStringRemainder => "string",
|
||||||
ParameterKind::GroupRefs => Some(Some(ParameterValue::GroupRefs(Vec::new()))),
|
ParameterKind::MemberRef => "target",
|
||||||
_ => None,
|
ParameterKind::MemberRefs => "targets",
|
||||||
|
ParameterKind::GroupRef => "target",
|
||||||
|
ParameterKind::GroupRefs => "targets",
|
||||||
|
ParameterKind::SystemRef => "target",
|
||||||
|
ParameterKind::MessageRef => "target",
|
||||||
|
ParameterKind::ChannelRef => "target",
|
||||||
|
ParameterKind::GuildRef => "target",
|
||||||
|
ParameterKind::MemberPrivacyTarget => "member_privacy_target",
|
||||||
|
ParameterKind::GroupPrivacyTarget => "group_privacy_target",
|
||||||
|
ParameterKind::SystemPrivacyTarget => "system_privacy_target",
|
||||||
|
ParameterKind::PrivacyLevel => "privacy_level",
|
||||||
|
ParameterKind::Toggle => "toggle",
|
||||||
|
ParameterKind::Avatar => "avatar",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use std::fmt::{Debug, Display};
|
||||||
|
|
||||||
use smol_str::SmolStr;
|
use smol_str::SmolStr;
|
||||||
|
|
||||||
use crate::parameter::{Parameter, ParameterKind, ParameterValue};
|
use crate::parameter::{Optional, Parameter, ParameterKind, ParameterValue};
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
pub enum Token {
|
pub enum Token {
|
||||||
|
|
@ -46,9 +46,17 @@ impl Token {
|
||||||
// short circuit on:
|
// short circuit on:
|
||||||
return match self {
|
return match self {
|
||||||
// missing paramaters
|
// missing paramaters
|
||||||
Self::Parameter(param) => Some(TokenMatchResult::MissingParameter {
|
Self::Parameter(param) => Some(
|
||||||
name: param.name().into(),
|
param
|
||||||
}),
|
.is_optional()
|
||||||
|
.then(|| TokenMatchResult::MatchedParameter {
|
||||||
|
name: param.name().into(),
|
||||||
|
value: ParameterValue::Null,
|
||||||
|
})
|
||||||
|
.unwrap_or_else(|| TokenMatchResult::MissingParameter {
|
||||||
|
name: param.name().into(),
|
||||||
|
}),
|
||||||
|
),
|
||||||
// everything else doesnt match if no input anyway
|
// everything else doesnt match if no input anyway
|
||||||
Self::Value { .. } => None,
|
Self::Value { .. } => None,
|
||||||
// don't add a _ match here!
|
// don't add a _ match here!
|
||||||
|
|
@ -62,20 +70,14 @@ impl Token {
|
||||||
Self::Value { name, aliases } => (aliases.iter().chain(std::iter::once(name)))
|
Self::Value { name, aliases } => (aliases.iter().chain(std::iter::once(name)))
|
||||||
.any(|v| v.eq(input))
|
.any(|v| v.eq(input))
|
||||||
.then(|| TokenMatchResult::MatchedValue),
|
.then(|| TokenMatchResult::MatchedValue),
|
||||||
Self::Parameter(param) => Some(match param.kind().match_value(input) {
|
Self::Parameter(param) => Some(match param.match_value(input) {
|
||||||
Ok(matched) => TokenMatchResult::MatchedParameter {
|
Ok(matched) => TokenMatchResult::MatchedParameter {
|
||||||
name: param.name().into(),
|
name: param.name().into(),
|
||||||
value: matched,
|
value: matched,
|
||||||
},
|
},
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
if let Some(maybe_empty) = param.kind().skip_if_cant_match() {
|
if param.is_skip() {
|
||||||
match maybe_empty {
|
return None;
|
||||||
Some(matched) => TokenMatchResult::MatchedParameter {
|
|
||||||
name: param.name().into(),
|
|
||||||
value: matched,
|
|
||||||
},
|
|
||||||
None => return None,
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
TokenMatchResult::ParameterMatchError {
|
TokenMatchResult::ParameterMatchError {
|
||||||
input: input.into(),
|
input: input.into(),
|
||||||
|
|
@ -115,21 +117,10 @@ impl From<&str> for Token {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Parameter> for Token {
|
// parameter -> Token::Parameter
|
||||||
fn from(value: Parameter) -> Self {
|
impl<P: Into<Parameter>> From<P> for Token {
|
||||||
Self::Parameter(value)
|
fn from(value: P) -> Self {
|
||||||
}
|
Self::Parameter(value.into())
|
||||||
}
|
|
||||||
|
|
||||||
impl From<ParameterKind> for Token {
|
|
||||||
fn from(value: ParameterKind) -> Self {
|
|
||||||
Self::from(Parameter::from(value))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<(&str, ParameterKind)> for Token {
|
|
||||||
fn from(value: (&str, ParameterKind)) -> Self {
|
|
||||||
Self::from(Parameter::from(value))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -45,19 +45,23 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
for param in &command_params {
|
for param in &command_params {
|
||||||
writeln!(
|
writeln!(
|
||||||
&mut command_params_init,
|
&mut command_params_init,
|
||||||
r#"@{name} = await ctx.ParamResolve{extract_fn_name}("{name}") ?? throw new PKError("this is a bug"),"#,
|
r#"@{name} = await ctx.ParamResolve{extract_fn_name}("{name}"){throw_null},"#,
|
||||||
name = param.name().replace("-", "_"),
|
name = param.name().replace("-", "_"),
|
||||||
extract_fn_name = get_param_param_ty(param.kind()),
|
extract_fn_name = get_param_param_ty(param.kind()),
|
||||||
|
throw_null = param
|
||||||
|
.is_optional()
|
||||||
|
.then_some("")
|
||||||
|
.unwrap_or(" ?? throw new PKError(\"this is a bug\")"),
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
let mut command_flags_init = String::new();
|
let mut command_flags_init = String::new();
|
||||||
for flag in &command.flags {
|
for flag in &command.flags {
|
||||||
if let Some(kind) = flag.get_value() {
|
if let Some(param) = flag.get_value() {
|
||||||
writeln!(
|
writeln!(
|
||||||
&mut command_flags_init,
|
&mut command_flags_init,
|
||||||
r#"@{name} = await ctx.FlagResolve{extract_fn_name}("{name}"),"#,
|
r#"@{name} = await ctx.FlagResolve{extract_fn_name}("{name}"),"#,
|
||||||
name = flag.get_name().replace("-", "_"),
|
name = flag.get_name().replace("-", "_"),
|
||||||
extract_fn_name = get_param_param_ty(kind),
|
extract_fn_name = get_param_param_ty(param.kind()),
|
||||||
)?;
|
)?;
|
||||||
} else {
|
} else {
|
||||||
writeln!(
|
writeln!(
|
||||||
|
|
@ -109,19 +113,20 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
for param in &command_params {
|
for param in &command_params {
|
||||||
writeln!(
|
writeln!(
|
||||||
&mut command_params_fields,
|
&mut command_params_fields,
|
||||||
r#"public required {ty} @{name};"#,
|
r#"public required {ty}{nullable} @{name};"#,
|
||||||
name = param.name().replace("-", "_"),
|
name = param.name().replace("-", "_"),
|
||||||
ty = get_param_ty(param.kind()),
|
ty = get_param_ty(param.kind()),
|
||||||
|
nullable = param.is_optional().then_some("?").unwrap_or(""),
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
let mut command_flags_fields = String::new();
|
let mut command_flags_fields = String::new();
|
||||||
for flag in &command.flags {
|
for flag in &command.flags {
|
||||||
if let Some(kind) = flag.get_value() {
|
if let Some(param) = flag.get_value() {
|
||||||
writeln!(
|
writeln!(
|
||||||
&mut command_flags_fields,
|
&mut command_flags_fields,
|
||||||
r#"public {ty}? @{name};"#,
|
r#"public {ty}? @{name};"#,
|
||||||
name = flag.get_name().replace("-", "_"),
|
name = flag.get_name().replace("-", "_"),
|
||||||
ty = get_param_ty(kind),
|
ty = get_param_ty(param.kind()),
|
||||||
)?;
|
)?;
|
||||||
} else {
|
} else {
|
||||||
writeln!(
|
writeln!(
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ interface Parameter {
|
||||||
OpaqueString(string raw);
|
OpaqueString(string raw);
|
||||||
Toggle(boolean toggle);
|
Toggle(boolean toggle);
|
||||||
Avatar(string avatar);
|
Avatar(string avatar);
|
||||||
|
Null();
|
||||||
};
|
};
|
||||||
dictionary ParsedCommand {
|
dictionary ParsedCommand {
|
||||||
string command_ref;
|
string command_ref;
|
||||||
|
|
|
||||||
|
|
@ -69,6 +69,7 @@ pub enum Parameter {
|
||||||
Avatar {
|
Avatar {
|
||||||
avatar: String,
|
avatar: String,
|
||||||
},
|
},
|
||||||
|
Null,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ParameterValue> for Parameter {
|
impl From<ParameterValue> for Parameter {
|
||||||
|
|
@ -93,6 +94,7 @@ impl From<ParameterValue> for Parameter {
|
||||||
},
|
},
|
||||||
ParameterValue::ChannelRef(channel_id) => Self::ChannelRef { channel_id },
|
ParameterValue::ChannelRef(channel_id) => Self::ChannelRef { channel_id },
|
||||||
ParameterValue::GuildRef(guild_id) => Self::GuildRef { guild_id },
|
ParameterValue::GuildRef(guild_id) => Self::GuildRef { guild_id },
|
||||||
|
ParameterValue::Null => Self::Null,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue