Skip to content

Commit

Permalink
feat!: require fallbacks in desc_localizations implementations for…
Browse files Browse the repository at this point in the history
… localized commands (#31)
  • Loading branch information
circuitsacul authored Jul 9, 2023
1 parent d6c2556 commit 2f17c11
Show file tree
Hide file tree
Showing 9 changed files with 297 additions and 33 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- A basic example bot implementation has been added in the `examples` directory
of the repository.
- `DescriptionLocalizations` and `NameLocalizations` structs

### Changed
- `desc` and `desc_localizations` are now mutually exclusive
- `desc_localizations` and `name_localizations` must return the
`DescriptionLocalizations` and `NameLocalizations` structs,
respectively.

## [0.15.2] - 2023-06-23
### Added
Expand Down
36 changes: 36 additions & 0 deletions twilight-interactions-derive/src/command/description.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
use quote::quote;

use crate::parse::parse_doc;

pub fn get_description(
desc_localizations: &Option<syn::Path>,
desc: &Option<String>,
span: proc_macro2::Span,
attrs: &[syn::Attribute],
) -> syn::Result<proc_macro2::TokenStream> {
if desc.is_some() && desc_localizations.is_some() {
return Err(syn::Error::new(
span,
"You can't specify `desc` and `desc_localizations`.",
));
}

let desc = match desc_localizations {
Some(path) => quote! {
{
let desc = #path();
(desc.fallback, ::std::option::Option::Some(desc.localizations))
}
},
None => {
let desc = match desc {
Some(desc) => desc.clone(),
None => parse_doc(attrs, span)?,
};

quote! { (::std::convert::From::from(#desc), None) }
}
};

Ok(desc)
}
1 change: 1 addition & 0 deletions twilight-interactions-derive/src/command/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

mod impls;

mod description;
mod model;
mod subcommand;

Expand Down
36 changes: 21 additions & 15 deletions twilight-interactions-derive/src/command/model/create_command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use quote::{quote, quote_spanned};
use syn::{spanned::Spanned, DeriveInput, Error, FieldsNamed, Result};

use super::parse::{channel_type, command_option_value, optional, StructField, TypeAttribute};
use crate::parse::{find_attr, parse_doc};
use crate::{command::description::get_description, parse::find_attr};

/// Implementation of `CreateCommand` derive macro
pub fn impl_create_command(input: DeriveInput, fields: Option<FieldsNamed>) -> Result<TokenStream> {
Expand Down Expand Up @@ -36,16 +36,18 @@ pub fn impl_create_command(input: DeriveInput, fields: Option<FieldsNamed>) -> R
));
}

let desc = get_description(
&attributes.desc_localizations,
&attributes.desc,
span,
&input.attrs,
)?;

let name = match &attributes.name {
Some(name) => name,
None => return Err(Error::new(attr_span, "missing required attribute `name`")),
};
let name_localizations = localization_field(&attributes.name_localizations);
let description = match &attributes.desc {
Some(desc) => desc.clone(),
None => parse_doc(&input.attrs, span)?,
};
let description_localizations = localization_field(&attributes.desc_localizations);
let default_permissions = match &attributes.default_permissions {
Some(path) => quote! { ::std::option::Option::Some(#path())},
None => quote! { ::std::option::Option::None },
Expand Down Expand Up @@ -73,11 +75,12 @@ pub fn impl_create_command(input: DeriveInput, fields: Option<FieldsNamed>) -> R

#(#field_options)*

let desc = #desc;
::twilight_interactions::command::ApplicationCommandData {
name: ::std::convert::From::from(#name),
name_localizations: #name_localizations,
description: ::std::convert::From::from(#description),
description_localizations: #description_localizations,
description: desc.0,
description_localizations: desc.1,
options: command_options,
default_member_permissions: #default_permissions,
dm_permission: #dm_permission,
Expand All @@ -94,13 +97,15 @@ fn field_option(field: &StructField) -> Result<TokenStream> {
let ty = &field.ty;
let span = field.span;

let desc = get_description(
&field.attributes.desc_localizations,
&field.attributes.desc,
field.span,
&field.raw_attrs,
)?;

let name = field.attributes.name_default(field.ident.to_string());
let name_localizations = localization_field(&field.attributes.name_localizations);
let description = match &field.attributes.desc {
Some(desc) => desc.clone(),
None => parse_doc(&field.raw_attrs, field.span)?,
};
let description_localizations = localization_field(&field.attributes.desc_localizations);
let required = field.kind.required();
let autocomplete = field.attributes.autocomplete;
let max_value = command_option_value(field.attributes.max_value);
Expand All @@ -116,12 +121,13 @@ fn field_option(field: &StructField) -> Result<TokenStream> {
};

Ok(quote_spanned! {span=>
let desc = #desc;
command_options.push(<#ty as ::twilight_interactions::command::CreateOption>::create_option(
::twilight_interactions::command::internal::CreateOptionData {
name: ::std::convert::From::from(#name),
name_localizations: #name_localizations,
description: ::std::convert::From::from(#description),
description_localizations: #description_localizations,
description: desc.0,
description_localizations: desc.1,
required: ::std::option::Option::Some(#required),
autocomplete: #autocomplete,
data: ::twilight_interactions::command::internal::CommandOptionData {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use quote::{quote, quote_spanned};
use syn::{spanned::Spanned, DeriveInput, Error, Result, Variant};

use super::parse::{ParsedVariant, TypeAttribute};
use crate::parse::{find_attr, parse_doc};
use crate::{command::description::get_description, parse::find_attr};

/// Implementation of `CreateCommand` derive macro
pub fn impl_create_command(
Expand All @@ -26,14 +26,16 @@ pub fn impl_create_command(
}
};

let desc = get_description(
&attribute.desc_localizations,
&attribute.desc,
span,
&input.attrs,
)?;

let capacity = variants.len();
let name = &attribute.name;
let name_localizations = localization_field(&attribute.name_localizations);
let description_localizations = localization_field(&attribute.desc_localizations);
let description = match attribute.desc {
Some(desc) => desc,
None => parse_doc(&input.attrs, span)?,
};
let default_permissions = match &attribute.default_permissions {
Some(path) => quote! { ::std::option::Option::Some(#path())},
None => quote! { ::std::option::Option::None },
Expand All @@ -54,15 +56,16 @@ pub fn impl_create_command(
const NAME: &'static str = #name;

fn create_command() -> ::twilight_interactions::command::ApplicationCommandData {
let desc = #desc;
let mut command_options = ::std::vec::Vec::with_capacity(#capacity);

#(#variant_options)*

::twilight_interactions::command::ApplicationCommandData {
name: ::std::convert::From::from(#name),
name_localizations: #name_localizations,
description: ::std::convert::From::from(#description),
description_localizations: #description_localizations,
description: desc.0,
description_localizations: desc.1,
options: command_options,
default_member_permissions: #default_permissions,
dm_permission: #dm_permission,
Expand Down
41 changes: 41 additions & 0 deletions twilight-interactions/src/command/localizations.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
use std::collections::HashMap;

pub struct DescriptionLocalizations {
pub fallback: String,
pub localizations: HashMap<String, String>,
}

impl DescriptionLocalizations {
pub fn new<I, K, V>(fallback: impl ToString, localizations: I) -> Self
where
I: IntoIterator<Item = (K, V)>,
K: ToString,
V: ToString,
{
Self {
fallback: fallback.to_string(),
localizations: localizations
.into_iter()
.map(|(k, v)| (k.to_string(), v.to_string()))
.collect(),
}
}
}

pub struct NameLocalizations(pub HashMap<String, String>);

impl NameLocalizations {
pub fn new<I, K, V>(localizations: I) -> Self
where
I: IntoIterator<Item = (K, V)>,
K: ToString,
V: ToString,
{
Self(
localizations
.into_iter()
.map(|(k, v)| (k.to_string(), v.to_string()))
.collect(),
)
}
}
10 changes: 6 additions & 4 deletions twilight-interactions/src/command/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,14 @@
//! and the second tuple element is the localized value.
//!
//! ```
//! use twilight_interactions::command::{CommandModel, CreateCommand, ResolvedUser};
//! use twilight_interactions::command::{CommandModel, CreateCommand, ResolvedUser, DescriptionLocalizations};
//!
//! #[derive(CommandModel, CreateCommand)]
//! #[command(name = "hello", desc = "Say hello", desc_localizations = "hello_desc")]
//! #[command(name = "hello", desc_localizations = "hello_desc")]
//! struct HelloCommand;
//!
//! pub fn hello_desc() -> [(&'static str, &'static str); 2] {
//! [("fr", "Dis bonjour"), ("de", "Sag Hallo")]
//! pub fn hello_desc() -> DescriptionLocalizations {
//! DescriptionLocalizations::new("Say hello", [("fr", "Dis bonjour"), ("de", "Sag Hallo")])
//! }
//! ```
//!
Expand Down Expand Up @@ -84,6 +84,7 @@

mod command_model;
mod create_command;
mod localizations;

#[doc(hidden)]
pub mod internal;
Expand All @@ -93,6 +94,7 @@ pub use command_model::{
ResolvedUser,
};
pub use create_command::{ApplicationCommandData, CreateCommand, CreateOption};
pub use localizations::{DescriptionLocalizations, NameLocalizations};
#[cfg(feature = "derive")]
#[cfg_attr(docsrs, doc(cfg(feature = "derive")))]
pub use twilight_interactions_derive::{CommandModel, CommandOption, CreateCommand, CreateOption};
Loading

0 comments on commit 2f17c11

Please sign in to comment.