The official command framework for the Serenity Discord API wrapper.
serenity-rs / framework Goto Github PK
View Code? Open in Web Editor NEWThe official command framework for bots written in Serenity
License: ISC License
The official command framework for bots written in Serenity
License: ISC License
The official command framework for the Serenity Discord API wrapper.
The command arguments will be part of the #[command]
macro to serve as a shorthand for parsing arguments manually. They will be provided in the signature of the function after the message parameter, appearing as additional parameters specific to the command. The type of the arguments will have to implement the Argument
trait defined in arguments::Argument
. If the type implements FromStr
, an implementation is automatically generated.
What needs to be done:
#[command]
macro by accepting an arbitrary amount of arguments after the message parameterThe current argument parsing system for commands enforces a strict order of the types of arguments, which proceeds as:
#[rest]
argumentEach type of argument may be absent, but violating this order will result in a macro error. What this issue proposes, however, is to break the strict order for required and optional arguments. This would allow for backtracking when parsing arguments for a command.
For example, for the following command:
#[command]
async fn boogie(ctx: FrameworkContext, msg: &Message, user2: Option<UserId>, user: UserId) -> CommandResult {
// ....
}
no error would originate from the macro. Instead, if just one argument is given, user2
will be None
, while user
will be populated with the argument. And in the case of two arguments, user2
will be the first argument, and user
the second argument.
Order between required/optional arguments and a variadic/#[rest]
argument will stay the same and will have to be uphold.
By virtue of relying on the std trait FromStr for parsing arguments, the framework cannot implement FromStr on other crates' types, most notably from serenity itself. For that reason, Member, User, Role, Emoji etc. currently cannot be used in the argument parsing system.
I can think of two ways to achieve this:
impl<T: FromStr> ArgumentParse for T { ... }
2 is probably cleaner, though a new serenity version would have to be released before it can happen, which might take some time. 1 would open the door to potentially support third party types without FromStr as command arguments (chrono, perhaps?)
Currently, when a user gives too many arguments to a command, or too few, or their input was malformed, serenity bubbles up the error as a Error::User
. However, the error doesn't happen in user code at all; it happens in serenity's dispatch glue code. I think it would be more intuitive and more useful to bubble up argument parsing errors as Error::Dispatch
Edit: we decided that a new designated Error enum variant Error::Argument(ArgumentError)
for argument parsing errors is better
Currently, the framework wraps the user data in an Arc<RwLock<_>>. It may be worth considering giving the choice to users which exact type they need, by using &D (or Arc if required) as the data type passed in Context instead of Arc<RwLock>.
Advantages:
Disadvantages:
Having a OneOf
(OneOfTwo
more specifically) enum to allow a command argument to be one of two types would be very convenient with the new framework.
enum OneOf<T: Parse, U: Parse> {
VariantOne(T),
VariantTwo(U),
}
The OneOf
enum would implement the Parse
trait and try to parse the string as type T
first. If that fails, it'd try type U
. If both fail, it'd return an error that contains both T
's and U
's parse errors. Inside a command's function body, a simple match
could be used to handle the OneOf
argument.
Currently, without such a type, a developer would have to create newtypes and implement the Parse
trait for all combinations they wish to support (or create something like this type themselves). I've often had to parse user input as one of two possible types, so having the framework support something like this would be very nice.
I wanted to create this issue before working on a PR for the same to see if such a type would be appropriate for the new framework.
Add something like a localisation
module for users to easily integrate localised messages with the framework.
The FrameworkContext docs state: this context type contains every data that's relevant to the command.
Would it be possible to move the &Message parameter, that every command function currently had, into FrameworkContext as well?
FrameworkContext
and &Message
to a certain utility function, which seems redundant)Is there an issue with the proposal that I hadn't considered? Otherwise I'd gladly create a PR for this
Code like this can be successfully processed by the #[command]
proc-macro:
#[command]
pub async fn some_command(ctx: FrameworkContext<Data>, msg: &Message) {}
However, code like this fails:
type Context = FrameworkContext<Data>;
#[command]
pub async fn some_command(ctx: Context, msg: &Message) {}
Error:
error[E0308]: mismatched types
--> src/crates.rs:42:1
|
42 | #[command]
| ^^^^^^^^^^
| |
| expected `()`, found struct `Data`
| expected `serenity_framework::command::Command` because of return type
|
= note: expected struct `serenity_framework::command::Command<()>`
found struct `serenity_framework::command::Command<Data>`
I think the reason for this failure is that #[command]
attempts to deduce the user data type by parsing generic parameters of the ctx
function argument. Because of the type alias, it can't find any generic parameters and falsely thinks this function uses the default user data (()
).
If this suspicion is correct, the issue can be worked around something like this:
// Inside framework crate
pub struct Context<D = DefaultData, E = DefaultError> {
// ...
}
#[doc(hidden)]
pub trait _ContextTypes {
type D;
type E;
}
impl<D, E> _ContextTypes for Context<D, E> {
type D = D;
type E = E;
}
The command_attr
crate then uses <ARGUMENTTYPE as _ContextTypes>::D
and <ARGUMENTTYPE as _ContextTypes>::E
instead of trying to deduce the generic parameters from ARGUMENTTYPE
.
This is a basic roadmap to get the initial release of the new framework up and running.
What needs to be done:
FromStr
implementation (relevant issues: #24)What does not have to be, but would be very nice:
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.