Skip to content

Commit debdd36

Browse files
mkrasnitskiGnomedDev
authored andcommitted
Add EditCommand builder (#3028)
This separates the the builders for creating versus editing a command, since it's not possible to change the `type` of a command (in serenity this is the `kind` field). Also, the `name` field is not required when editing a command.
1 parent 9cc8253 commit debdd36

File tree

5 files changed

+217
-74
lines changed

5 files changed

+217
-74
lines changed

src/builder/create_command.rs

Lines changed: 28 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
use std::borrow::Cow;
22
use std::collections::HashMap;
33

4+
use crate::builder::EditCommand;
45
#[cfg(feature = "http")]
56
use crate::http::Http;
6-
use crate::internal::prelude::*;
77
use crate::model::prelude::*;
88

99
/// A builder for creating a new [`CommandOption`].
@@ -320,59 +320,32 @@ impl<'a> CreateCommandOption<'a> {
320320

321321
/// A builder for creating a new [`Command`].
322322
///
323-
/// [`Self::name`] and [`Self::description`] are required fields.
324-
///
325323
/// [`Command`]: crate::model::application::Command
326324
///
327325
/// Discord docs:
328-
/// - [global command](https://discord.com/developers/docs/interactions/application-commands#create-global-application-command-json-params)
329-
/// - [guild command](https://discord.com/developers/docs/interactions/application-commands#create-guild-application-command-json-params)
326+
/// - [global command](https://discord.com/developers/docs/interactions/application-commands#create-global-application-command)
327+
/// - [guild command](https://discord.com/developers/docs/interactions/application-commands#create-guild-application-command)
330328
#[derive(Clone, Debug, Serialize)]
331329
#[must_use]
332330
pub struct CreateCommand<'a> {
333-
name: Cow<'a, str>,
334-
name_localizations: HashMap<Cow<'a, str>, Cow<'a, str>>,
335-
#[serde(skip_serializing_if = "Option::is_none")]
336-
description: Option<Cow<'a, str>>,
337-
description_localizations: HashMap<Cow<'a, str>, Cow<'a, str>>,
338-
options: Cow<'a, [CreateCommandOption<'a>]>,
339-
#[serde(skip_serializing_if = "Option::is_none")]
340-
default_member_permissions: Option<Permissions>,
341-
#[serde(skip_serializing_if = "Option::is_none")]
342-
#[cfg(not(feature = "unstable"))]
343-
dm_permission: Option<bool>,
344331
#[serde(skip_serializing_if = "Option::is_none")]
345332
#[serde(rename = "type")]
346333
kind: Option<CommandType>,
347334
#[serde(skip_serializing_if = "Option::is_none")]
348-
integration_types: Option<Vec<InstallationContext>>,
349-
#[serde(skip_serializing_if = "Option::is_none")]
350-
contexts: Option<Vec<InteractionContext>>,
351-
nsfw: bool,
352-
#[serde(skip_serializing_if = "Option::is_none")]
353335
handler: Option<EntryPointHandlerType>,
336+
337+
#[serde(flatten)]
338+
fields: EditCommand<'a>,
354339
}
355340

356341
impl<'a> CreateCommand<'a> {
357342
/// Creates a new builder with the given name, leaving all other fields empty.
358343
pub fn new(name: impl Into<Cow<'a, str>>) -> Self {
359344
Self {
360345
kind: None,
361-
362-
name: name.into(),
363-
name_localizations: HashMap::new(),
364-
description: None,
365-
description_localizations: HashMap::new(),
366-
default_member_permissions: None,
367-
#[cfg(not(feature = "unstable"))]
368-
dm_permission: None,
369-
370-
integration_types: None,
371-
contexts: None,
372-
373-
options: Cow::default(),
374-
nsfw: false,
375346
handler: None,
347+
348+
fields: EditCommand::new().name(name),
376349
}
377350
}
378351

@@ -383,15 +356,14 @@ impl<'a> CreateCommand<'a> {
383356
/// global commands of the same app cannot have the same name. Two guild-specific commands of
384357
/// the same app cannot have the same name.
385358
pub fn name(mut self, name: impl Into<Cow<'a, str>>) -> Self {
386-
self.name = name.into();
359+
self.fields = self.fields.name(name);
387360
self
388361
}
389362

390363
/// Specifies a localized name of the application command.
391364
///
392365
/// ```rust
393-
/// # serenity::builder::CreateCommand::new("")
394-
/// .name("birthday")
366+
/// # serenity::builder::CreateCommand::new("birthday")
395367
/// .name_localized("zh-CN", "生日")
396368
/// .name_localized("el", "γενέθλια")
397369
/// # ;
@@ -401,7 +373,7 @@ impl<'a> CreateCommand<'a> {
401373
locale: impl Into<Cow<'a, str>>,
402374
name: impl Into<Cow<'a, str>>,
403375
) -> Self {
404-
self.name_localizations.insert(locale.into(), name.into());
376+
self.fields = self.fields.name_localized(locale, name);
405377
self
406378
}
407379

@@ -413,29 +385,29 @@ impl<'a> CreateCommand<'a> {
413385

414386
/// Specifies the default permissions required to execute the command.
415387
pub fn default_member_permissions(mut self, permissions: Permissions) -> Self {
416-
self.default_member_permissions = Some(permissions);
388+
self.fields = self.fields.default_member_permissions(permissions);
417389
self
418390
}
419391

420392
/// Specifies if the command is available in DMs.
421393
#[cfg(not(feature = "unstable"))]
422394
pub fn dm_permission(mut self, enabled: bool) -> Self {
423-
self.dm_permission = Some(enabled);
395+
self.fields = self.fields.dm_permission(enabled);
424396
self
425397
}
426398

427399
/// Specifies the description of the application command.
428400
///
429401
/// **Note**: Must be between 1 and 100 characters long.
430402
pub fn description(mut self, description: impl Into<Cow<'a, str>>) -> Self {
431-
self.description = Some(description.into());
403+
self.fields = self.fields.description(description);
432404
self
433405
}
434406

435407
/// Specifies a localized description of the application command.
436408
///
437409
/// ```rust
438-
/// # serenity::builder::CreateCommand::new("")
410+
/// # serenity::builder::CreateCommand::new("birthday")
439411
/// .description("Wish a friend a happy birthday")
440412
/// .description_localized("zh-CN", "祝你朋友生日快乐")
441413
/// # ;
@@ -445,53 +417,53 @@ impl<'a> CreateCommand<'a> {
445417
locale: impl Into<Cow<'a, str>>,
446418
description: impl Into<Cow<'a, str>>,
447419
) -> Self {
448-
self.description_localizations.insert(locale.into(), description.into());
420+
self.fields = self.fields.description_localized(locale, description);
449421
self
450422
}
451423

452424
/// Adds an application command option for the application command.
453425
///
454426
/// **Note**: Application commands can have up to 25 options.
455427
pub fn add_option(mut self, option: CreateCommandOption<'a>) -> Self {
456-
self.options.to_mut().push(option);
428+
self.fields = self.fields.add_option(option);
457429
self
458430
}
459431

460432
/// Sets all the application command options for the application command.
461433
///
462434
/// **Note**: Application commands can have up to 25 options.
463435
pub fn set_options(mut self, options: impl Into<Cow<'a, [CreateCommandOption<'a>]>>) -> Self {
464-
self.options = options.into();
436+
self.fields = self.fields.set_options(options);
465437
self
466438
}
467439

468440
/// Adds an installation context that this application command can be used in.
469441
pub fn add_integration_type(mut self, integration_type: InstallationContext) -> Self {
470-
self.integration_types.get_or_insert_with(Vec::default).push(integration_type);
442+
self.fields = self.fields.add_integration_type(integration_type);
471443
self
472444
}
473445

474446
/// Sets the installation contexts that this application command can be used in.
475447
pub fn integration_types(mut self, integration_types: Vec<InstallationContext>) -> Self {
476-
self.integration_types = Some(integration_types);
448+
self.fields = self.fields.integration_types(integration_types);
477449
self
478450
}
479451

480452
/// Adds an interaction context that this application command can be used in.
481453
pub fn add_context(mut self, context: InteractionContext) -> Self {
482-
self.contexts.get_or_insert_with(Vec::default).push(context);
454+
self.fields = self.fields.add_context(context);
483455
self
484456
}
485457

486458
/// Sets the interaction contexts that this application command can be used in.
487459
pub fn contexts(mut self, contexts: Vec<InteractionContext>) -> Self {
488-
self.contexts = Some(contexts);
460+
self.fields = self.fields.contexts(contexts);
489461
self
490462
}
491463

492464
/// Whether this command is marked NSFW (age-restricted)
493465
pub fn nsfw(mut self, nsfw: bool) -> Self {
494-
self.nsfw = nsfw;
466+
self.fields = self.fields.nsfw(nsfw);
495467
self
496468
}
497469

@@ -504,13 +476,11 @@ impl<'a> CreateCommand<'a> {
504476
self
505477
}
506478

507-
/// Create a [`Command`], overriding an existing one with the same name if it exists.
479+
/// Create a [`Command`], overwriting an existing one with the same name if it exists.
508480
///
509481
/// Providing a [`GuildId`] will create a command in the corresponding [`Guild`]. Otherwise, a
510482
/// global command will be created.
511483
///
512-
/// Providing a [`CommandId`] will edit the corresponding command.
513-
///
514484
/// # Errors
515485
///
516486
/// Returns [`Error::Http`] if invalid data is given. See [Discord's docs] for more details.
@@ -519,19 +489,10 @@ impl<'a> CreateCommand<'a> {
519489
///
520490
/// [Discord's docs]: https://discord.com/developers/docs/interactions/slash-commands
521491
#[cfg(feature = "http")]
522-
pub async fn execute(
523-
self,
524-
http: &Http,
525-
guild_id: Option<GuildId>,
526-
command_id: Option<CommandId>,
527-
) -> Result<Command> {
528-
match (guild_id, command_id) {
529-
(Some(guild_id), Some(cmd_id)) => {
530-
http.edit_guild_command(guild_id, cmd_id, &self).await
531-
},
532-
(Some(guild_id), None) => http.create_guild_command(guild_id, &self).await,
533-
(None, Some(cmd_id)) => http.edit_global_command(cmd_id, &self).await,
534-
(None, None) => http.create_global_command(&self).await,
492+
pub async fn execute(self, http: &Http, guild_id: Option<GuildId>) -> Result<Command> {
493+
match guild_id {
494+
Some(guild_id) => http.create_guild_command(guild_id, &self).await,
495+
None => http.create_global_command(&self).await,
535496
}
536497
}
537498
}

0 commit comments

Comments
 (0)