1个不稳定版本
0.1.0 | 2021年9月16日 |
---|
#2208 in 解析器实现
在 2 crates中使用
25KB
556 行
discord-typed-interactions
我在编写一个discord机器人,所有的动态命令数据检查都非常痛苦,所以我受到了启发,决定再也不这样做。因此,这就是这个。
以下几点需要注意
- 输入路径相对于Cargo.toml;include_str!是编译器内置的,我们没有简单的方法来复制该行为。
- 我们不重新导出serde,因此您需要依赖于serde和serde_json来编译生成的代码。
过程宏
use discord_typed_interactions::typify;
use serde_json::json;
typify!("./schema/ctf.json");
fn main() {
let play = json!({
"id":"868983602015252520",
"name":"ctf",
"options":[
{
"name":"play",
"options":[
{
"name":"name",
"value":"howdy"
}
]
}
]
});
serde_json::from_value::<ctf::Ctf>(play).unwrap();
println!("{:#?}", play)
}
Ctf {
id: "868983602015252520",
name: "ctf",
options: Play(
Options {
name: "howdy",
},
),
resolved: None,
}
build.rs
use discord_typed_interactions::Configuration;
fn main() {
Configuration::new("schema/ctf.json")
// .src("schema/other.json") // should you have more commands you can use Configuration::src multiple times
.dest("src/command.rs")
.generate();
}
生成的代码
模式
{
"name": "ctf",
"description": "placeholder",
"options": [
{
"type": 1,
"name": "add",
"description": "placeholder",
"options": [
{
"type": 3,
"name": "name",
"description": "placeholder",
"required": true
}
]
},
{
"type": 1,
"name": "archive",
"description": "placeholder",
"options": [
{
"type": 7,
"name": "channel",
"description": "placeholder"
}
]
},
{
"type": 2,
"name": "players",
"description": "placeholder",
"options": [
{
"type": 1,
"name": "add",
"description": "placeholder",
"options": [
{
"type": 9,
"name": "name",
"description": "placeholder",
"required": true
}
]
},
{
"type": 1,
"name": "remove",
"description": "placeholder",
"options": [
{
"type": 9,
"name": "name",
"description": "placeholder",
"required": true
}
]
}
]
}
]
}
```
生成的代码
pub mod ctf {
pub mod add {
use serde::{
de::{SeqAccess, Visitor},
Deserializer,
};
use std::fmt;
#[derive(serde :: Serialize, Debug, Default)]
pub struct Options {
pub name: String,
}
impl<'de> serde::Deserialize<'de> for Options {
fn deserialize>(deserializer: D) -> Result {
struct PropertyParser;
impl<'de> Visitor<'de> for PropertyParser {
type Value = Options;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("aaa")
}
fn visit_seq>(
self,
mut seq: A,
) -> Result {
#[allow(non_camel_case_types)]
#[derive(serde :: Deserialize, Debug)]
#[serde(tag = "name", content = "value")]
enum Property {
name(String),
}
let mut prop = Options::default();
while let Some(tmp) = seq.next_element::()? {
match tmp {
Property::name(v) => prop.name = v,
}
}
Ok(prop)
}
}
deserializer.deserialize_seq(PropertyParser)
}
}
}
pub mod archive {
use serde::{
de::{SeqAccess, Visitor},
Deserializer,
};
use std::fmt;
#[derive(serde :: Serialize, Debug, Default)]
pub struct Options {
pub channel: String,
}
impl<'de> serde::Deserialize<'de> for Options {
fn deserialize>(deserializer: D) -> Result {
struct PropertyParser;
impl<'de> Visitor<'de> for PropertyParser {
type Value = Options;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("aaa")
}
fn visit_seq>(
self,
mut seq: A,
) -> Result {
#[allow(non_camel_case_types)]
#[derive(serde :: Deserialize, Debug)]
#[serde(tag = "name", content = "value")]
enum Property {
channel(String),
}
let mut prop = Options::default();
while let Some(tmp) = seq.next_element::()? {
match tmp {
Property::channel(v) => prop.channel = v,
}
}
Ok(prop)
}
}
deserializer.deserialize_seq(PropertyParser)
}
}
}
#[derive(serde :: Serialize, serde :: Deserialize, Debug)]
#[serde(tag = "name", rename_all = "snake_case")]
pub struct Ctf {
pub id: String,
#[serde(deserialize_with = "parse_single")]
pub options: Options,
pub resolved: Option,
}
#[derive(serde :: Serialize, serde :: Deserialize, Debug)]
#[serde(tag = "name", content = "options", rename_all = "snake_case")]
pub enum Options {
Add(add::Options),
Archive(archive::Options),
#[serde(deserialize_with = "parse_single")]
Players(players::Players),
}
use serde::{
de::{Error, SeqAccess, Visitor},
Deserializer,
};
use std::fmt;
use std::marker::PhantomData;
fn parse_single<'de, D: Deserializer<'de>, T: serde::Deserialize<'de>>(
deserializer: D,
) -> Result {
struct PropertyParser(PhantomData);
impl<'de, T: serde::Deserialize<'de>> Visitor<'de> for PropertyParser {
type Value = T;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
write!(
formatter,
"a nonempty list of {}",
std::any::type_name::()
)
}
fn visit_seq>(self, mut seq: A) -> Result {
seq.next_element::()?
.ok_or_else(|| A::Error::custom("empty array"))
}
}
deserializer.deserialize_seq(PropertyParser(PhantomData))
}
pub mod players {
pub mod add {
use serde::{
de::{SeqAccess, Visitor},
Deserializer,
};
use std::fmt;
#[derive(serde :: Serialize, Debug, Default)]
pub struct Options {
pub name: String,
}
impl<'de> serde::Deserialize<'de> for Options {
fn deserialize>(deserializer: D) -> Result {
struct PropertyParser;
impl<'de> Visitor<'de> for PropertyParser {
type Value = Options;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("aaa")
}
fn visit_seq>(
self,
mut seq: A,
) -> Result {
#[allow(non_camel_case_types)]
#[derive(serde :: Deserialize, Debug)]
#[serde(tag = "name", content = "value")]
enum Property {
name(String),
}
let mut prop = Options::default();
while let Some(tmp) = seq.next_element::()? {
match tmp {
Property::name(v) => prop.name = v,
}
}
Ok(prop)
}
}
deserializer.deserialize_seq(PropertyParser)
}
}
}
pub mod remove {
use serde::{
de::{SeqAccess, Visitor},
Deserializer,
};
use std::fmt;
#[derive(serde :: Serialize, Debug, Default)]
pub struct Options {
pub name: String,
}
impl<'de> serde::Deserialize<'de> for Options {
fn deserialize>(deserializer: D) -> Result {
struct PropertyParser;
impl<'de> Visitor<'de> for PropertyParser {
type Value = Options;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("aaa")
}
fn visit_seq>(
self,
mut seq: A,
) -> Result {
#[allow(non_camel_case_types)]
#[derive(serde :: Deserialize, Debug)]
#[serde(tag = "name", content = "value")]
enum Property {
name(String),
}
let mut prop = Options::default();
while let Some(tmp) = seq.next_element::()? {
match tmp {
Property::name(v) => prop.name = v,
}
}
Ok(prop)
}
}
deserializer.deserialize_seq(PropertyParser)
}
}
}
#[derive(serde :: Serialize, serde :: Deserialize, Debug)]
#[serde(tag = "name", content = "options")]
#[serde(rename_all = "snake_case")]
pub enum Players {
Add(add::Options),
Remove(remove::Options),
}
}
}
#[derive(serde :: Serialize, Debug)]
#[serde(tag = "type")]
#[non_exhaustive]
pub enum Interaction {
Ping(Ping),
ApplicationCommand(ApplicationCommand),
}
use serde::de::Error;
impl<'de> serde::Deserialize<'de> for Interaction {
fn deserialize>(deserializer: D) -> Result {
let value = serde_json::Value::deserialize(deserializer)?;
Ok(
match value
.get("type")
.and_then(serde_json::Value::as_u64)
.ok_or_else(|| D::Error::custom("type field is either missing or not u64"))?
{
1 => Interaction::Ping(
Ping::deserialize(value).map_err(|x| D::Error::custom(x.to_string()))?,
),
2 => Interaction::ApplicationCommand(
ApplicationCommand::deserialize(value)
.map_err(|x| D::Error::custom(x.to_string()))?,
),
_ => panic!("type isn't valid"),
},
)
}
}
#[derive(serde :: Serialize, serde :: Deserialize, Debug)]
pub struct Ping {
pub application_id: String,
pub id: String,
pub r#type: u64,
pub token: String,
}
#[derive(serde :: Serialize, serde :: Deserialize, Debug)]
pub struct ApplicationCommand {
pub application_id: String,
pub channel_id: String,
pub data: Command,
pub guild_id: Option,
pub id: String,
pub member: Option,
pub user: Option,
pub token: String,
pub r#type: u64,
pub version: u64,
}
#[derive(serde :: Serialize, serde :: Deserialize, Debug)]
#[serde(untagged)]
pub enum Command {
Ctf(ctf::Ctf),
Other { id: String, name: String },
}
#[derive(serde :: Serialize, serde :: Deserialize, Debug)]
pub struct User {
pub id: String,
pub username: String,
pub discriminator: String,
pub avatar: String,
pub bot: Option,
pub system: Option,
pub mfa_enabled: Option,
pub locale: Option,
pub verified: Option,
pub email: Option,
pub flags: Option,
pub premium_type: Option,
pub public_flags: Option,
}
#[derive(serde :: Serialize, serde :: Deserialize, Debug)]
pub struct PartialMember {
pub user: Option,
pub nick: Option,
pub roles: Vec,
pub joined_at: String,
pub premium_since: Option,
pub deaf: Option,
pub mute: Option,
pub pending: Option,
pub permissions: Option,
}
use std::collections::HashMap;
#[derive(serde :: Serialize, serde :: Deserialize, Debug)]
pub struct Resolved {
#[serde(default)]
pub users: HashMap,
#[serde(default)]
pub members: HashMap,
#[serde(default)]
pub roles: HashMap,
#[serde(default)]
pub channels: HashMap,
}
#[derive(serde :: Serialize, serde :: Deserialize, Debug)]
pub struct Role {
pub id: String,
pub name: String,
pub color: u64,
pub hoist: bool,
pub position: u64,
pub permissions: String,
pub managed: bool,
pub mentionable: bool,
pub tags: Option,
}
#[derive(serde :: Serialize, serde :: Deserialize, Debug)]
pub struct RoleTags {
pub bot_id: Option,
pub integration_id: Option,
pub premium_subscriber: Option,
}
#[derive(serde :: Serialize, serde :: Deserialize, Debug)]
pub struct PartialChannel {
pub id: String,
pub r#type: u64,
pub name: String,
pub permissions: String,
}
依赖项
~0.7–1.6MB
~36K SLoC