titanium_model/
builder.rs

1//! Builders for creating Discord entities.
2
3use crate::{
4    Component, CreateMessage, Embed, EmbedAuthor, EmbedField, EmbedFooter, EmbedMedia, TitanString,
5};
6
7/// Builder for creating an Embed.
8#[derive(Debug, Clone, Default)]
9pub struct EmbedBuilder<'a> {
10    embed: Embed<'a>,
11}
12
13impl<'a> EmbedBuilder<'a> {
14    /// Create a new EmbedBuilder.
15    #[inline]
16    pub fn new() -> Self {
17        Self::default()
18    }
19
20    /// Set the title of the embed.
21    #[inline]
22    pub fn title(mut self, title: impl Into<TitanString<'a>>) -> Self {
23        self.embed.title = Some(title.into());
24        self
25    }
26
27    /// Set the description of the embed.
28    pub fn description(mut self, description: impl Into<TitanString<'a>>) -> Self {
29        self.embed.description = Some(description.into());
30        self
31    }
32
33    /// Set the URL of the embed.
34    pub fn url(mut self, url: impl Into<TitanString<'a>>) -> Self {
35        self.embed.url = Some(url.into());
36        self
37    }
38
39    /// Set the timestamp of the embed.
40    pub fn timestamp(mut self, timestamp: impl Into<TitanString<'a>>) -> Self {
41        self.embed.timestamp = Some(timestamp.into());
42        self
43    }
44
45    /// Set the color of the embed.
46    pub fn color(mut self, color: u32) -> Self {
47        self.embed.color = Some(color);
48        self
49    }
50
51    /// Set the color of the embed from RGB values.
52    pub fn color_rgb(mut self, r: u8, g: u8, b: u8) -> Self {
53        self.embed.color = Some(((r as u32) << 16) | ((g as u32) << 8) | (b as u32));
54        self
55    }
56
57    /// Set the footer of the embed.
58    pub fn footer(
59        mut self,
60        text: impl Into<TitanString<'a>>,
61        icon_url: Option<impl Into<TitanString<'a>>>,
62    ) -> Self {
63        self.embed.footer = Some(EmbedFooter {
64            text: text.into(),
65            icon_url: icon_url.map(Into::into),
66            proxy_icon_url: None,
67        });
68        self
69    }
70
71    /// Set the image of the embed.
72    pub fn image(mut self, url: impl Into<TitanString<'a>>) -> Self {
73        self.embed.image = Some(EmbedMedia {
74            url: Some(url.into()),
75            proxy_url: None,
76            height: None,
77            width: None,
78        });
79        self
80    }
81
82    /// Set the thumbnail of the embed.
83    pub fn thumbnail(mut self, url: impl Into<TitanString<'a>>) -> Self {
84        self.embed.thumbnail = Some(EmbedMedia {
85            url: Some(url.into()),
86            proxy_url: None,
87            height: None,
88            width: None,
89        });
90        self
91    }
92
93    /// Set the author of the embed.
94    pub fn author(
95        mut self,
96        name: impl Into<TitanString<'a>>,
97        url: Option<impl Into<TitanString<'a>>>,
98        icon_url: Option<impl Into<TitanString<'a>>>,
99    ) -> Self {
100        self.embed.author = Some(EmbedAuthor {
101            name: name.into(),
102            url: url.map(Into::into),
103            icon_url: icon_url.map(Into::into),
104            proxy_icon_url: None,
105        });
106        self
107    }
108
109    /// Add a field to the embed.
110    pub fn field(
111        mut self,
112        name: impl Into<TitanString<'a>>,
113        value: impl Into<TitanString<'a>>,
114        inline: bool,
115    ) -> Self {
116        self.embed.fields.push(EmbedField {
117            name: name.into(),
118            value: value.into(),
119            inline,
120        });
121        self
122    }
123
124    /// Add an inline field.
125    pub fn field_inline(
126        self,
127        name: impl Into<TitanString<'a>>,
128        value: impl Into<TitanString<'a>>,
129    ) -> Self {
130        self.field(name, value, true)
131    }
132
133    /// Add a block field (not inline).
134    pub fn field_block(
135        self,
136        name: impl Into<TitanString<'a>>,
137        value: impl Into<TitanString<'a>>,
138    ) -> Self {
139        self.field(name, value, false)
140    }
141
142    /// Build the Embed.
143    pub fn build(self) -> Embed<'a> {
144        self.embed
145    }
146}
147
148/// Builder for creating a Message.
149#[derive(Debug, Clone, Default)]
150pub struct MessageBuilder<'a> {
151    message: CreateMessage<'a>,
152}
153
154impl<'a> MessageBuilder<'a> {
155    /// Create a new MessageBuilder.
156    #[inline]
157    pub fn new() -> Self {
158        Self::default()
159    }
160
161    /// Set the content of the message.
162    #[inline]
163    pub fn content(mut self, content: impl Into<TitanString<'a>>) -> Self {
164        self.message.content = Some(content.into());
165        self
166    }
167
168    /// Enable/disable TTS.
169    pub fn tts(mut self, tts: bool) -> Self {
170        self.message.tts = Some(tts);
171        self
172    }
173
174    /// Reply to a message (sets message_reference).
175    pub fn reply(mut self, message_id: impl Into<crate::Snowflake>) -> Self {
176        self.message.message_reference = Some(crate::MessageReference {
177            message_id: Some(message_id.into()),
178            channel_id: None,
179            guild_id: None,
180            fail_if_not_exists: Some(true),
181        });
182        self
183    }
184
185    /// Add an embed to the message.
186    pub fn embed(mut self, embed: impl Into<Embed<'a>>) -> Self {
187        if let Some(embeds) = &mut self.message.embeds {
188            embeds.push(embed.into());
189        } else {
190            self.message.embeds = Some(vec![embed.into()]);
191        }
192        self
193    }
194
195    /// Add multiple embeds.
196    pub fn embeds(mut self, embeds: Vec<Embed<'a>>) -> Self {
197        if let Some(existing) = &mut self.message.embeds {
198            existing.extend(embeds);
199        } else {
200            self.message.embeds = Some(embeds);
201        }
202        self
203    }
204
205    /// Add a component (ActionRow, etc.) to the message.
206    pub fn component(mut self, component: impl Into<Component<'a>>) -> Self {
207        if let Some(components) = &mut self.message.components {
208            components.push(component.into());
209        } else {
210            self.message.components = Some(vec![component.into()]);
211        }
212        self
213    }
214
215    /// Add a file to upload.
216    pub fn add_file(
217        mut self,
218        filename: impl Into<TitanString<'a>>,
219        data: impl Into<Vec<u8>>,
220    ) -> Self {
221        self.message.files.push(crate::file::FileUpload::new(
222            filename.into().into_owned(),
223            data,
224        ));
225        self
226    }
227
228    /// Build the CreateMessage payload.
229    pub fn build(self) -> CreateMessage<'a> {
230        self.message
231    }
232}
233
234// ============================================================================
235// Modify Guild Builder
236// ============================================================================
237
238/// Payload for modifying a guild.
239#[derive(Debug, Clone, serde::Serialize, Default)]
240pub struct ModifyGuild<'a> {
241    #[serde(skip_serializing_if = "Option::is_none")]
242    pub name: Option<TitanString<'a>>,
243    #[serde(skip_serializing_if = "Option::is_none")]
244    pub region: Option<TitanString<'a>>,
245    #[serde(skip_serializing_if = "Option::is_none")]
246    pub verification_level: Option<u8>,
247    #[serde(skip_serializing_if = "Option::is_none")]
248    pub default_message_notifications: Option<u8>,
249    #[serde(skip_serializing_if = "Option::is_none")]
250    pub explicit_content_filter: Option<u8>,
251    #[serde(skip_serializing_if = "Option::is_none")]
252    pub afk_channel_id: Option<crate::Snowflake>,
253    #[serde(skip_serializing_if = "Option::is_none")]
254    pub afk_timeout: Option<u32>,
255    #[serde(skip_serializing_if = "Option::is_none")]
256    pub icon: Option<TitanString<'a>>,
257    #[serde(skip_serializing_if = "Option::is_none")]
258    pub owner_id: Option<crate::Snowflake>,
259    #[serde(skip_serializing_if = "Option::is_none")]
260    pub splash: Option<TitanString<'a>>,
261    #[serde(skip_serializing_if = "Option::is_none")]
262    pub discovery_splash: Option<TitanString<'a>>,
263    #[serde(skip_serializing_if = "Option::is_none")]
264    pub banner: Option<TitanString<'a>>,
265    #[serde(skip_serializing_if = "Option::is_none")]
266    pub system_channel_id: Option<crate::Snowflake>,
267    #[serde(skip_serializing_if = "Option::is_none")]
268    pub system_channel_flags: Option<u64>,
269    #[serde(skip_serializing_if = "Option::is_none")]
270    pub rules_channel_id: Option<crate::Snowflake>,
271    #[serde(skip_serializing_if = "Option::is_none")]
272    pub public_updates_channel_id: Option<crate::Snowflake>,
273    #[serde(skip_serializing_if = "Option::is_none")]
274    pub preferred_locale: Option<TitanString<'a>>,
275    #[serde(skip_serializing_if = "Option::is_none")]
276    pub features: Option<Vec<TitanString<'a>>>,
277    #[serde(skip_serializing_if = "Option::is_none")]
278    pub description: Option<TitanString<'a>>,
279    #[serde(skip_serializing_if = "Option::is_none")]
280    pub premium_progress_bar_enabled: Option<bool>,
281    #[serde(skip_serializing_if = "Option::is_none")]
282    pub safety_alerts_channel_id: Option<crate::Snowflake>,
283}
284
285/// Builder for modifying a Guild.
286#[derive(Debug, Clone, Default)]
287pub struct ModifyGuildBuilder<'a> {
288    params: ModifyGuild<'a>,
289}
290
291impl<'a> ModifyGuildBuilder<'a> {
292    /// Create a new ModifyGuildBuilder.
293    pub fn new() -> Self {
294        Self::default()
295    }
296
297    /// Set name.
298    pub fn name(mut self, name: impl Into<TitanString<'a>>) -> Self {
299        self.params.name = Some(name.into());
300        self
301    }
302
303    /// Set region (deprecated).
304    pub fn region(mut self, region: impl Into<TitanString<'a>>) -> Self {
305        self.params.region = Some(region.into());
306        self
307    }
308
309    /// Set verification level.
310    pub fn verification_level(mut self, level: u8) -> Self {
311        self.params.verification_level = Some(level);
312        self
313    }
314
315    /// Set default message notifications.
316    pub fn default_message_notifications(mut self, level: u8) -> Self {
317        self.params.default_message_notifications = Some(level);
318        self
319    }
320
321    /// Set explicit content filter.
322    pub fn explicit_content_filter(mut self, level: u8) -> Self {
323        self.params.explicit_content_filter = Some(level);
324        self
325    }
326
327    /// Set AFK channel ID.
328    pub fn afk_channel_id(mut self, id: impl Into<crate::Snowflake>) -> Self {
329        self.params.afk_channel_id = Some(id.into());
330        self
331    }
332
333    /// Set AFK timeout.
334    pub fn afk_timeout(mut self, timeout: u32) -> Self {
335        self.params.afk_timeout = Some(timeout);
336        self
337    }
338
339    /// Set icon (base64).
340    pub fn icon(mut self, icon: impl Into<TitanString<'a>>) -> Self {
341        self.params.icon = Some(icon.into());
342        self
343    }
344
345    /// Set system channel ID.
346    pub fn system_channel_id(mut self, id: impl Into<crate::Snowflake>) -> Self {
347        self.params.system_channel_id = Some(id.into());
348        self
349    }
350
351    /// Build the ModifyGuild payload.
352    pub fn build(self) -> ModifyGuild<'a> {
353        self.params
354    }
355}
356
357// ============================================================================
358// Modify Member Builder
359// ============================================================================
360
361/// Payload for modifying a guild member.
362#[derive(Debug, Clone, serde::Serialize, Default)]
363pub struct ModifyMember<'a> {
364    #[serde(skip_serializing_if = "Option::is_none")]
365    pub nick: Option<TitanString<'a>>,
366    #[serde(skip_serializing_if = "Option::is_none")]
367    pub roles: Option<Vec<crate::Snowflake>>,
368    #[serde(skip_serializing_if = "Option::is_none")]
369    pub mute: Option<bool>,
370    #[serde(skip_serializing_if = "Option::is_none")]
371    pub deaf: Option<bool>,
372    #[serde(skip_serializing_if = "Option::is_none")]
373    pub channel_id: Option<crate::Snowflake>, // Move to voice channel
374    #[serde(skip_serializing_if = "Option::is_none")]
375    pub communication_disabled_until: Option<TitanString<'a>>, // Timeout
376    #[serde(skip_serializing_if = "Option::is_none")]
377    pub flags: Option<u64>,
378}
379
380/// Builder for modifying a GuildMember.
381#[derive(Debug, Clone, Default)]
382pub struct ModifyMemberBuilder<'a> {
383    params: ModifyMember<'a>,
384}
385
386impl<'a> ModifyMemberBuilder<'a> {
387    /// Create a new ModifyMemberBuilder.
388    pub fn new() -> Self {
389        Self::default()
390    }
391
392    /// Set nickname.
393    pub fn nick(mut self, nick: impl Into<TitanString<'a>>) -> Self {
394        self.params.nick = Some(nick.into());
395        self
396    }
397
398    /// Set roles (replaces all roles).
399    pub fn roles(mut self, roles: Vec<crate::Snowflake>) -> Self {
400        self.params.roles = Some(roles);
401        self
402    }
403
404    /// Mute or unmute.
405    pub fn mute(mut self, mute: bool) -> Self {
406        self.params.mute = Some(mute);
407        self
408    }
409
410    /// Deafen or undeafen.
411    pub fn deaf(mut self, deaf: bool) -> Self {
412        self.params.deaf = Some(deaf);
413        self
414    }
415
416    /// Move to voice channel (or disconnect if null, but we use strict type here).
417    pub fn move_to_channel(mut self, channel_id: impl Into<crate::Snowflake>) -> Self {
418        self.params.channel_id = Some(channel_id.into());
419        self
420    }
421
422    /// Timeout user until timestamp (ISO8601).
423    pub fn timeout_until(mut self, timestamp: impl Into<TitanString<'a>>) -> Self {
424        self.params.communication_disabled_until = Some(timestamp.into());
425        self
426    }
427
428    /// Build the ModifyMember payload.
429    pub fn build(self) -> ModifyMember<'a> {
430        self.params
431    }
432}
433
434// ============================================================================
435// Start Thread Builder
436// ============================================================================
437
438/// Payload for starting a thread.
439#[derive(Debug, Clone, serde::Serialize, Default)]
440pub struct StartThread<'a> {
441    pub name: TitanString<'a>,
442    #[serde(skip_serializing_if = "Option::is_none")]
443    pub auto_archive_duration: Option<u32>,
444    #[serde(skip_serializing_if = "Option::is_none")]
445    pub type_: Option<u8>, // For Start Thread without Message
446    #[serde(skip_serializing_if = "Option::is_none")]
447    pub invitable: Option<bool>,
448    #[serde(skip_serializing_if = "Option::is_none")]
449    pub rate_limit_per_user: Option<u32>,
450}
451
452/// Builder for starting a Thread.
453#[derive(Debug, Clone)]
454pub struct StartThreadBuilder<'a> {
455    params: StartThread<'a>,
456}
457
458impl<'a> StartThreadBuilder<'a> {
459    /// Create a new StartThreadBuilder.
460    pub fn new(name: impl Into<TitanString<'a>>) -> Self {
461        Self {
462            params: StartThread {
463                name: name.into(),
464                ..Default::default()
465            },
466        }
467    }
468
469    /// Set auto archive duration (60, 1440, 4320, 10080).
470    pub fn auto_archive_duration(mut self, duration: u32) -> Self {
471        self.params.auto_archive_duration = Some(duration);
472        self
473    }
474
475    /// Set thread type (for standalone threads).
476    pub fn kind(mut self, kind: u8) -> Self {
477        self.params.type_ = Some(kind);
478        self
479    }
480
481    /// Set invitable (private threads).
482    pub fn invitable(mut self, invitable: bool) -> Self {
483        self.params.invitable = Some(invitable);
484        self
485    }
486
487    /// Set rate limit per user.
488    pub fn rate_limit_per_user(mut self, limit: u32) -> Self {
489        self.params.rate_limit_per_user = Some(limit);
490        self
491    }
492
493    /// Build the StartThread payload.
494    pub fn build(self) -> StartThread<'a> {
495        self.params
496    }
497}
498
499// ============================================================================
500// Component Builders
501// ============================================================================
502
503/// Builder for creating a Button.
504#[derive(Debug, Clone)]
505pub struct ButtonBuilder<'a> {
506    component: crate::Component<'a>,
507}
508
509impl<'a> ButtonBuilder<'a> {
510    /// Create a new ButtonBuilder.
511    #[inline]
512    pub fn new() -> Self {
513        Self {
514            component: crate::Component::Button(crate::component::Button {
515                style: crate::component::ButtonStyle::Primary, // Default
516                label: None,
517                emoji: None,
518                custom_id: None,
519                url: None,
520                disabled: false,
521                component_type: crate::component::ComponentType::Button,
522            }),
523        }
524    }
525
526    /// Set style.
527    pub fn style(mut self, style: crate::component::ButtonStyle) -> Self {
528        if let crate::Component::Button(b) = &mut self.component {
529            b.style = style;
530        }
531        self
532    }
533
534    /// Set label.
535    pub fn label(mut self, label: impl Into<TitanString<'a>>) -> Self {
536        if let crate::Component::Button(b) = &mut self.component {
537            b.label = Some(label.into());
538        }
539        self
540    }
541
542    /// Set emoji.
543    pub fn emoji(mut self, emoji: impl Into<crate::reaction::ReactionEmoji<'a>>) -> Self {
544        if let crate::Component::Button(b) = &mut self.component {
545            b.emoji = Some(emoji.into());
546        }
547        self
548    }
549
550    /// Set custom ID.
551    pub fn custom_id(mut self, id: impl Into<TitanString<'a>>) -> Self {
552        if let crate::Component::Button(b) = &mut self.component {
553            b.custom_id = Some(id.into());
554        }
555        self
556    }
557
558    /// Set URL.
559    pub fn url(mut self, url: impl Into<TitanString<'a>>) -> Self {
560        if let crate::Component::Button(b) = &mut self.component {
561            b.url = Some(url.into());
562        }
563        self
564    }
565
566    /// Set disabled.
567    pub fn disabled(mut self, disabled: bool) -> Self {
568        if let crate::Component::Button(b) = &mut self.component {
569            b.disabled = disabled;
570        }
571        self
572    }
573
574    /// Build the Component.
575    pub fn build(self) -> crate::Component<'a> {
576        self.component
577    }
578}
579
580/// Builder for creating a Select Menu.
581#[derive(Debug, Clone)]
582pub struct SelectMenuBuilder<'a> {
583    component: crate::Component<'a>,
584}
585
586impl<'a> SelectMenuBuilder<'a> {
587    /// Create a new SelectMenuBuilder.
588    #[inline]
589    pub fn new(custom_id: impl Into<TitanString<'a>>) -> Self {
590        Self {
591            component: crate::Component::SelectMenu(crate::component::SelectMenu {
592                custom_id: custom_id.into(),
593                options: Vec::with_capacity(25), // Discord max options
594                placeholder: None,
595                min_values: None,
596                max_values: None,
597                disabled: false,
598                component_type: crate::component::ComponentType::StringSelect, // Default
599            }),
600        }
601    }
602
603    /// Add an option.
604    pub fn option(
605        mut self,
606        label: impl Into<TitanString<'a>>,
607        value: impl Into<TitanString<'a>>,
608    ) -> Self {
609        if let crate::Component::SelectMenu(s) = &mut self.component {
610            s.options.push(crate::component::SelectOption {
611                label: label.into(),
612                value: value.into(),
613                description: None,
614                emoji: None,
615                default: false,
616            });
617        }
618        self
619    }
620
621    /// Set placeholder.
622    pub fn placeholder(mut self, placeholder: impl Into<TitanString<'a>>) -> Self {
623        if let crate::Component::SelectMenu(s) = &mut self.component {
624            s.placeholder = Some(placeholder.into());
625        }
626        self
627    }
628
629    /// Set min values.
630    pub fn min_values(mut self, min: u8) -> Self {
631        if let crate::Component::SelectMenu(s) = &mut self.component {
632            s.min_values = Some(min);
633        }
634        self
635    }
636
637    /// Set max values.
638    pub fn max_values(mut self, max: u8) -> Self {
639        if let crate::Component::SelectMenu(s) = &mut self.component {
640            s.max_values = Some(max);
641        }
642        self
643    }
644
645    /// Set disabled.
646    pub fn disabled(mut self, disabled: bool) -> Self {
647        if let crate::Component::SelectMenu(s) = &mut self.component {
648            s.disabled = disabled;
649        }
650        self
651    }
652
653    /// Build the Component.
654    pub fn build(self) -> crate::Component<'a> {
655        self.component
656    }
657}
658
659/// Builder for creating an Action Row.
660#[derive(Debug, Clone)]
661pub struct ActionRowBuilder<'a> {
662    component: crate::Component<'a>,
663}
664
665impl<'a> ActionRowBuilder<'a> {
666    pub fn new() -> Self {
667        Self {
668            component: crate::Component::ActionRow(crate::component::ActionRow {
669                components: Vec::with_capacity(5), // Discord max components per row
670                component_type: crate::component::ComponentType::ActionRow,
671            }),
672        }
673    }
674
675    pub fn add_button(mut self, button: ButtonBuilder<'a>) -> Self {
676        if let crate::Component::ActionRow(r) = &mut self.component {
677            r.components.push(button.build());
678        }
679        self
680    }
681
682    pub fn add_select_menu(mut self, menu: SelectMenuBuilder<'a>) -> Self {
683        if let crate::Component::ActionRow(r) = &mut self.component {
684            r.components.push(menu.build());
685        }
686        self
687    }
688
689    pub fn build(self) -> crate::Component<'a> {
690        self.component
691    }
692}
693
694/// Builder for Interaction Response.
695#[derive(Debug, Clone)]
696pub struct InteractionResponseBuilder<'a> {
697    response: crate::interaction::InteractionResponse<'a>,
698}
699
700impl<'a> Default for InteractionResponseBuilder<'a> {
701    fn default() -> Self {
702        Self::new()
703    }
704}
705
706impl<'a> InteractionResponseBuilder<'a> {
707    /// Create a new builder.
708    pub fn new() -> Self {
709        Self {
710            response: crate::interaction::InteractionResponse {
711                response_type:
712                    crate::interaction::InteractionCallbackType::ChannelMessageWithSource,
713                data: Some(Default::default()),
714            },
715        }
716    }
717
718    pub fn kind(mut self, kind: crate::interaction::InteractionCallbackType) -> Self {
719        self.response.response_type = kind;
720        self
721    }
722
723    pub fn content(mut self, content: impl Into<TitanString<'a>>) -> Self {
724        if let Some(data) = &mut self.response.data {
725            data.content = Some(content.into());
726        }
727        self
728    }
729
730    pub fn embed(mut self, embed: impl Into<crate::Embed<'a>>) -> Self {
731        if self.response.data.is_none() {
732            self.response.data = Some(Default::default());
733        }
734        if let Some(data) = &mut self.response.data {
735            data.embeds.push(embed.into());
736        }
737        self
738    }
739
740    pub fn build(self) -> crate::interaction::InteractionResponse<'a> {
741        self.response
742    }
743}
744
745// ============================================================================
746// Create Channel Builder
747// ============================================================================
748
749/// Payload for creating a channel.
750/// Payload for creating a channel.
751#[derive(Debug, Clone, serde::Serialize, Default)]
752pub struct CreateChannel<'a> {
753    pub name: TitanString<'a>,
754    #[serde(skip_serializing_if = "Option::is_none", rename = "type")]
755    pub kind: Option<u8>,
756    #[serde(skip_serializing_if = "Option::is_none")]
757    pub topic: Option<TitanString<'a>>,
758    #[serde(skip_serializing_if = "Option::is_none")]
759    pub bitrate: Option<u32>,
760    #[serde(skip_serializing_if = "Option::is_none")]
761    pub user_limit: Option<u32>,
762    #[serde(skip_serializing_if = "Option::is_none")]
763    pub rate_limit_per_user: Option<u32>,
764    #[serde(skip_serializing_if = "Option::is_none")]
765    pub position: Option<u32>,
766    #[serde(skip_serializing_if = "Option::is_none")]
767    pub permission_overwrites: Option<Vec<crate::json::Value>>,
768    #[serde(skip_serializing_if = "Option::is_none")]
769    pub parent_id: Option<crate::Snowflake>,
770    #[serde(skip_serializing_if = "Option::is_none")]
771    pub nsfw: Option<bool>,
772}
773
774/// Builder for creating a Channel.
775#[derive(Debug, Clone, Default)]
776pub struct CreateChannelBuilder<'a> {
777    params: CreateChannel<'a>,
778}
779
780impl<'a> CreateChannelBuilder<'a> {
781    pub fn new(name: impl Into<TitanString<'a>>) -> Self {
782        let mut builder = Self::default();
783        builder.params.name = name.into();
784        builder
785    }
786
787    pub fn kind(mut self, kind: u8) -> Self {
788        self.params.kind = Some(kind);
789        self
790    }
791
792    pub fn topic(mut self, topic: impl Into<TitanString<'a>>) -> Self {
793        self.params.topic = Some(topic.into());
794        self
795    }
796
797    pub fn build(self) -> CreateChannel<'a> {
798        self.params
799    }
800}
801
802// ============================================================================
803// Create Role Builder
804// ============================================================================
805
806/// Payload for creating a role.
807/// Payload for creating a role.
808#[derive(Debug, Clone, serde::Serialize, Default)]
809pub struct CreateRole<'a> {
810    #[serde(skip_serializing_if = "Option::is_none")]
811    pub name: Option<TitanString<'a>>,
812    #[serde(skip_serializing_if = "Option::is_none")]
813    pub permissions: Option<String>,
814    #[serde(skip_serializing_if = "Option::is_none")]
815    pub color: Option<u32>,
816    #[serde(skip_serializing_if = "Option::is_none")]
817    pub hoist: Option<bool>,
818    #[serde(skip_serializing_if = "Option::is_none")]
819    pub icon: Option<TitanString<'a>>,
820    #[serde(skip_serializing_if = "Option::is_none")]
821    pub unicode_emoji: Option<TitanString<'a>>,
822    #[serde(skip_serializing_if = "Option::is_none")]
823    pub mentionable: Option<bool>,
824}
825
826/// Builder for creating a Role.
827#[derive(Debug, Clone, Default)]
828pub struct CreateRoleBuilder<'a> {
829    params: CreateRole<'a>,
830}
831
832impl<'a> CreateRoleBuilder<'a> {
833    pub fn new() -> Self {
834        Self::default()
835    }
836
837    pub fn name(mut self, name: impl Into<TitanString<'a>>) -> Self {
838        self.params.name = Some(name.into());
839        self
840    }
841
842    pub fn color(mut self, color: u32) -> Self {
843        self.params.color = Some(color);
844        self
845    }
846
847    pub fn hoist(mut self, hoist: bool) -> Self {
848        self.params.hoist = Some(hoist);
849        self
850    }
851
852    pub fn icon(mut self, icon: impl Into<TitanString<'a>>) -> Self {
853        self.params.icon = Some(icon.into());
854        self
855    }
856
857    pub fn unicode_emoji(mut self, emoji: impl Into<TitanString<'a>>) -> Self {
858        self.params.unicode_emoji = Some(emoji.into());
859        self
860    }
861
862    pub fn mentionable(mut self, mentionable: bool) -> Self {
863        self.params.mentionable = Some(mentionable);
864        self
865    }
866
867    pub fn build(self) -> CreateRole<'a> {
868        self.params
869    }
870}
871
872// ============================================================================
873// Command Builder (Slash Commands)
874// ============================================================================
875
876/// Builder for creating an Application Command (Slash Command).
877#[derive(Debug, Clone, serde::Serialize)]
878pub struct CommandBuilder<'a> {
879    pub name: TitanString<'a>,
880    #[serde(skip_serializing_if = "Option::is_none")]
881    pub name_localizations: Option<std::collections::HashMap<String, String>>,
882    #[serde(skip_serializing_if = "Option::is_none")]
883    pub description: Option<TitanString<'a>>,
884    #[serde(skip_serializing_if = "Option::is_none")]
885    pub description_localizations: Option<std::collections::HashMap<String, String>>,
886    #[serde(skip_serializing_if = "Option::is_none")]
887    pub default_member_permissions: Option<TitanString<'a>>,
888    #[serde(skip_serializing_if = "Option::is_none")]
889    pub dm_permission: Option<bool>,
890    #[serde(default)]
891    #[serde(rename = "type")]
892    pub kind: Option<crate::command::CommandType>,
893    #[serde(skip_serializing_if = "Option::is_none")]
894    pub nsfw: Option<bool>,
895}
896
897impl<'a> CommandBuilder<'a> {
898    pub fn new(name: impl Into<TitanString<'a>>, description: impl Into<TitanString<'a>>) -> Self {
899        Self {
900            name: name.into(),
901            description: Some(description.into()),
902            name_localizations: None,
903            description_localizations: None,
904            default_member_permissions: None,
905            dm_permission: None,
906            kind: Some(crate::command::CommandType::ChatInput),
907            nsfw: None,
908        }
909    }
910
911    pub fn build(self) -> Self {
912        self
913    }
914}
915
916// ============================================================================
917// AutoMod Rule Builder
918// ============================================================================
919
920#[derive(Debug, Clone, serde::Serialize, Default)]
921pub struct AutoModRuleBuilder {
922    pub name: String,
923}
924
925// Duplicates removed
926
927// ============================================================================
928// Missing Variants Placeholders
929// ============================================================================
930
931/// Payload for creating a scheduled event.
932#[derive(Debug, Clone, serde::Serialize, Default)]
933pub struct CreateScheduledEvent<'a> {
934    pub name: TitanString<'a>,
935    pub privacy_level: crate::scheduled::ScheduledEventPrivacyLevel,
936    pub scheduled_start_time: TitanString<'a>,
937    #[serde(skip_serializing_if = "Option::is_none")]
938    pub scheduled_end_time: Option<TitanString<'a>>,
939    #[serde(skip_serializing_if = "Option::is_none")]
940    pub description: Option<TitanString<'a>>,
941    pub entity_type: crate::scheduled::ScheduledEventEntityType,
942    #[serde(skip_serializing_if = "Option::is_none")]
943    pub channel_id: Option<crate::Snowflake>,
944    #[serde(skip_serializing_if = "Option::is_none")]
945    pub entity_metadata: Option<crate::scheduled::ScheduledEventEntityMetadata<'a>>,
946    #[serde(skip_serializing_if = "Option::is_none")]
947    pub image: Option<TitanString<'a>>, // Base64
948}
949
950/// Builder for creating a Scheduled Event.
951#[derive(Debug, Clone)]
952pub struct ScheduledEventBuilder<'a> {
953    params: CreateScheduledEvent<'a>,
954}
955
956impl<'a> ScheduledEventBuilder<'a> {
957    /// Create a new ScheduledEventBuilder.
958    pub fn new(
959        name: impl Into<TitanString<'a>>,
960        start_time: impl Into<TitanString<'a>>,
961        entity_type: crate::scheduled::ScheduledEventEntityType,
962    ) -> Self {
963        Self {
964            params: CreateScheduledEvent {
965                name: name.into(),
966                scheduled_start_time: start_time.into(),
967                entity_type,
968                privacy_level: crate::scheduled::ScheduledEventPrivacyLevel::GuildOnly,
969                ..Default::default()
970            },
971        }
972    }
973
974    /// Set description.
975    #[inline]
976    pub fn description(mut self, description: impl Into<TitanString<'a>>) -> Self {
977        self.params.description = Some(description.into());
978        self
979    }
980
981    /// Set end time.
982    #[inline]
983    pub fn end_time(mut self, time: impl Into<TitanString<'a>>) -> Self {
984        self.params.scheduled_end_time = Some(time.into());
985        self
986    }
987
988    /// Set channel ID (required for Stage/Voice events).
989    #[inline]
990    pub fn channel_id(mut self, id: impl Into<crate::Snowflake>) -> Self {
991        self.params.channel_id = Some(id.into());
992        self
993    }
994
995    /// Set location (required for External events).
996    #[inline]
997    pub fn location(mut self, location: impl Into<TitanString<'a>>) -> Self {
998        self.params.entity_metadata = Some(crate::scheduled::ScheduledEventEntityMetadata {
999            location: Some(location.into()),
1000        });
1001        self
1002    }
1003
1004    /// Set cover image (base64).
1005    #[inline]
1006    pub fn image(mut self, image: impl Into<TitanString<'a>>) -> Self {
1007        self.params.image = Some(image.into());
1008        self
1009    }
1010
1011    /// Build the payload.
1012    #[inline]
1013    pub fn build(self) -> CreateScheduledEvent<'a> {
1014        self.params
1015    }
1016}
1017
1018/// Builder for creating a Poll.
1019#[derive(Debug, Clone)]
1020pub struct PollBuilder<'a> {
1021    poll: crate::poll::Poll<'a>,
1022}
1023
1024impl<'a> PollBuilder<'a> {
1025    /// Create a new PollBuilder.
1026    pub fn new(question: impl Into<TitanString<'a>>) -> Self {
1027        Self {
1028            poll: crate::poll::Poll {
1029                question: crate::poll::PollMedia {
1030                    text: Some(question.into()),
1031                    emoji: None,
1032                },
1033                answers: Vec::new(),
1034                expiry: None,
1035                allow_multiselect: false,
1036                layout_type: None,
1037                results: None,
1038            },
1039        }
1040    }
1041
1042    /// Add an answer.
1043    pub fn answer(mut self, answer: impl Into<crate::poll::PollAnswer<'a>>) -> Self {
1044        self.poll.answers.push(answer.into());
1045        self
1046    }
1047
1048    /// Set expiry.
1049    pub fn expiry(mut self, expiry: impl Into<TitanString<'a>>) -> Self {
1050        self.poll.expiry = Some(expiry.into());
1051        self
1052    }
1053
1054    /// Allow multiselect.
1055    pub fn allow_multiselect(mut self, allow: bool) -> Self {
1056        self.poll.allow_multiselect = allow;
1057        self
1058    }
1059
1060    /// Build the Poll.
1061    pub fn build(self) -> crate::poll::Poll<'a> {
1062        self.poll
1063    }
1064}
1065
1066/// Payload for executing a webhook.
1067#[derive(Debug, Clone, serde::Serialize, Default)]
1068pub struct ExecuteWebhook {
1069    #[serde(skip_serializing_if = "Option::is_none")]
1070    pub content: Option<String>,
1071    #[serde(skip_serializing_if = "Option::is_none")]
1072    pub username: Option<String>,
1073    #[serde(skip_serializing_if = "Option::is_none")]
1074    pub avatar_url: Option<String>,
1075    #[serde(skip_serializing_if = "Option::is_none")]
1076    pub tts: Option<bool>,
1077    #[serde(skip_serializing_if = "Vec::is_empty")]
1078    pub embeds: Vec<crate::Embed<'static>>,
1079    // Note: files and components omitted for brevity in this iteration, but can be added
1080}
1081
1082/// Builder for executing a Webhook.
1083#[derive(Debug, Clone, Default)]
1084pub struct WebhookExecuteBuilder<'a> {
1085    params: ExecuteWebhook,
1086    _phantom: std::marker::PhantomData<&'a ()>,
1087}
1088
1089impl<'a> WebhookExecuteBuilder<'a> {
1090    /// Create a new WebhookExecuteBuilder.
1091    pub fn new() -> Self {
1092        Self::default()
1093    }
1094
1095    /// Set content.
1096    #[inline]
1097    pub fn content(mut self, content: impl Into<String>) -> Self {
1098        self.params.content = Some(content.into());
1099        self
1100    }
1101
1102    /// Set username override.
1103    #[inline]
1104    pub fn username(mut self, username: impl Into<String>) -> Self {
1105        self.params.username = Some(username.into());
1106        self
1107    }
1108
1109    /// Set avatar URL override.
1110    #[inline]
1111    pub fn avatar_url(mut self, url: impl Into<String>) -> Self {
1112        self.params.avatar_url = Some(url.into());
1113        self
1114    }
1115
1116    /// Set TTS.
1117    #[inline]
1118    pub fn tts(mut self, tts: bool) -> Self {
1119        self.params.tts = Some(tts);
1120        self
1121    }
1122
1123    /// Add an embed.
1124    #[inline]
1125    pub fn embed(mut self, embed: impl Into<crate::Embed<'static>>) -> Self {
1126        self.params.embeds.push(embed.into());
1127        self
1128    }
1129
1130    /// Add multiple embeds.
1131    #[inline]
1132    pub fn embeds(mut self, embeds: Vec<crate::Embed<'static>>) -> Self {
1133        self.params.embeds.extend(embeds);
1134        self
1135    }
1136
1137    /// Build the payload.
1138    #[inline]
1139    pub fn build(self) -> ExecuteWebhook {
1140        self.params
1141    }
1142}
1143
1144// Add missing Default implementations and inlines to other builders where applicable
1145impl<'a> Default for ButtonBuilder<'a> {
1146    fn default() -> Self {
1147        Self::new()
1148    }
1149}
1150
1151impl<'a> Default for ActionRowBuilder<'a> {
1152    fn default() -> Self {
1153        Self::new()
1154    }
1155}
1156
1157impl<'a> Default for SelectMenuBuilder<'a> {
1158    fn default() -> Self {
1159        Self::new("default_select") // Fallback, though usually ID is required
1160    }
1161}
1162
1163#[cfg(test)]
1164mod modify_tests {
1165    use super::*;
1166    #[test]
1167    fn test_modify_guild_builder() {
1168        let payload = ModifyGuildBuilder::new()
1169            .name("New Guild Name")
1170            .region("us-west")
1171            .verification_level(1)
1172            .build();
1173
1174        assert_eq!(
1175            payload.name,
1176            Some(TitanString::from("New Guild Name".to_string()))
1177        );
1178        assert_eq!(
1179            payload.region,
1180            Some(TitanString::from("us-west".to_string()))
1181        );
1182        assert_eq!(payload.verification_level, Some(1));
1183    }
1184
1185    #[test]
1186    fn test_modify_member_builder() {
1187        let payload = ModifyMemberBuilder::new()
1188            .nick("New Nick")
1189            .mute(true)
1190            .deaf(false)
1191            .build();
1192
1193        assert_eq!(
1194            payload.nick,
1195            Some(TitanString::from("New Nick".to_string()))
1196        );
1197        assert_eq!(payload.mute, Some(true));
1198        assert_eq!(payload.deaf, Some(false));
1199    }
1200
1201    #[test]
1202    fn test_start_thread_builder() {
1203        let payload = StartThreadBuilder::new("Thread Name")
1204            .auto_archive_duration(60)
1205            .kind(11) // Public Thread
1206            .build();
1207
1208        assert_eq!(payload.name, "Thread Name".to_string());
1209        assert_eq!(payload.auto_archive_duration, Some(60));
1210        assert_eq!(payload.type_, Some(11));
1211    }
1212}
1213
1214// ============================================================================
1215// Create Invite Builder
1216// ============================================================================
1217
1218/// Payload for creating an invite.
1219#[derive(Debug, Clone, serde::Serialize, Default)]
1220pub struct CreateInvite {
1221    #[serde(skip_serializing_if = "Option::is_none")]
1222    pub max_age: Option<u32>,
1223    #[serde(skip_serializing_if = "Option::is_none")]
1224    pub max_uses: Option<u32>,
1225    #[serde(skip_serializing_if = "Option::is_none")]
1226    pub temporary: Option<bool>,
1227    #[serde(skip_serializing_if = "Option::is_none")]
1228    pub unique: Option<bool>,
1229    #[serde(skip_serializing_if = "Option::is_none")]
1230    pub target_type: Option<u8>,
1231    #[serde(skip_serializing_if = "Option::is_none")]
1232    pub target_user_id: Option<crate::Snowflake>,
1233    #[serde(skip_serializing_if = "Option::is_none")]
1234    pub target_application_id: Option<crate::Snowflake>,
1235}
1236
1237/// Builder for creating an Invite.
1238#[derive(Debug, Clone, Default)]
1239pub struct CreateInviteBuilder {
1240    params: CreateInvite,
1241}
1242
1243impl CreateInviteBuilder {
1244    /// Create a new CreateInviteBuilder.
1245    pub fn new() -> Self {
1246        Self::default()
1247    }
1248
1249    /// Set max age in seconds (0 = never expire).
1250    pub fn max_age(mut self, seconds: u32) -> Self {
1251        self.params.max_age = Some(seconds);
1252        self
1253    }
1254
1255    /// Set max uses (0 = unlimited).
1256    pub fn max_uses(mut self, uses: u32) -> Self {
1257        self.params.max_uses = Some(uses);
1258        self
1259    }
1260
1261    /// Set temporary (kick after disconnect).
1262    pub fn temporary(mut self, temp: bool) -> Self {
1263        self.params.temporary = Some(temp);
1264        self
1265    }
1266
1267    /// Set unique (don't reuse similar invite).
1268    pub fn unique(mut self, unique: bool) -> Self {
1269        self.params.unique = Some(unique);
1270        self
1271    }
1272
1273    /// Build the CreateInvite payload.
1274    pub fn build(self) -> CreateInvite {
1275        self.params
1276    }
1277}
1278
1279// ============================================================================
1280// Create Emoji Builder
1281// ============================================================================
1282
1283/// Payload for creating an emoji.
1284#[derive(Debug, Clone, serde::Serialize, Default)]
1285pub struct CreateEmoji {
1286    pub name: String,
1287    pub image: String, // Data URI
1288    #[serde(skip_serializing_if = "Vec::is_empty")]
1289    pub roles: Vec<crate::Snowflake>,
1290}
1291
1292/// Builder for creating an Emoji.
1293#[derive(Debug, Clone)]
1294pub struct CreateEmojiBuilder {
1295    params: CreateEmoji,
1296}
1297
1298impl CreateEmojiBuilder {
1299    /// Create a new CreateEmojiBuilder.
1300    /// `image_data` should be a Data URI Scheme string (e.g. "data:image/jpeg;base64,...").
1301    pub fn new(name: impl Into<String>, image_data: impl Into<String>) -> Self {
1302        Self {
1303            params: CreateEmoji {
1304                name: name.into(),
1305                image: image_data.into(),
1306                roles: Vec::new(),
1307            },
1308        }
1309    }
1310
1311    /// Add a role that can use this emoji.
1312    pub fn role(mut self, role_id: impl Into<crate::Snowflake>) -> Self {
1313        self.params.roles.push(role_id.into());
1314        self
1315    }
1316
1317    /// Build the CreateEmoji payload.
1318    pub fn build(self) -> CreateEmoji {
1319        self.params
1320    }
1321}
1322
1323#[cfg(test)]
1324mod final_tests {
1325    use super::*;
1326    use crate::Mention;
1327    use crate::Snowflake; // Ensure Mention trait is in scope
1328
1329    #[test]
1330    fn test_create_invite_builder() {
1331        let payload = CreateInviteBuilder::new()
1332            .max_age(86400)
1333            .max_uses(10)
1334            .unique(true)
1335            .build();
1336
1337        assert_eq!(payload.max_age, Some(86400));
1338        assert_eq!(payload.max_uses, Some(10));
1339        assert_eq!(payload.unique, Some(true));
1340    }
1341
1342    #[test]
1343    fn test_create_emoji_builder() {
1344        let payload = CreateEmojiBuilder::new("test_emoji", "data:image/png;base64,...")
1345            .role(Snowflake(12345))
1346            .build();
1347
1348        assert_eq!(payload.name, "test_emoji");
1349        assert_eq!(payload.roles.len(), 1);
1350    }
1351
1352    #[test]
1353    fn test_mention_trait() {
1354        let user = crate::User {
1355            id: Snowflake(123),
1356            username: "test".to_string().into(),
1357            discriminator: "0000".to_string().into(),
1358            global_name: None,
1359            avatar: None,
1360            bot: false,
1361            system: false,
1362            mfa_enabled: None,
1363            banner: None,
1364            accent_color: None,
1365            locale: None,
1366            verified: None,
1367            email: None,
1368            flags: None,
1369            premium_type: None,
1370            public_flags: None,
1371            avatar_decoration_data: None,
1372        };
1373
1374        // This test relies on Mention being implemented for User
1375        // Since Mention is in lib.rs, we might need to import it properly.
1376        // But here we are in builder.rs, so crate::Mention works if public.
1377        // Wait, builder.rs is a module, lib.rs creates the crate. crate::Mention is correct.
1378
1379        let mention = user.mention();
1380        assert_eq!(mention, "<@123>");
1381    }
1382
1383    #[test]
1384    fn test_add_file_builder() {
1385        let msg = MessageBuilder::new()
1386            .content("With file")
1387            .add_file("test.txt", vec![1, 2, 3])
1388            .build();
1389
1390        assert_eq!(msg.files.len(), 1);
1391        assert_eq!(msg.files[0].filename, "test.txt");
1392        assert_eq!(msg.files[0].data, vec![1, 2, 3]);
1393    }
1394}
1395#[cfg(test)]
1396mod optimization_tests {
1397    use super::*;
1398    use TitanString;
1399
1400    #[test]
1401    fn test_embed_builder_zero_allocation() {
1402        let title = "Static Title";
1403        // Should accept &str directly
1404        let embed = EmbedBuilder::new().title(title).build();
1405        match embed.title {
1406            Some(TitanString::Borrowed(t)) => assert_eq!(t, "Static Title"),
1407            _ => panic!("Expected Borrowed Cow for static string"),
1408        }
1409    }
1410
1411    #[test]
1412    fn test_embed_builder_owned() {
1413        let title = String::from("Owned Title");
1414        let embed = EmbedBuilder::new().title(title).build();
1415        match embed.title {
1416            Some(TitanString::Owned(t)) => assert_eq!(t, "Owned Title"),
1417            _ => panic!("Expected Owned Cow for String"),
1418        }
1419    }
1420
1421    #[test]
1422    fn test_component_builder_zero_allocation() {
1423        let id = "custom_id";
1424        let btn = ButtonBuilder::new().custom_id(id).build();
1425        if let crate::Component::Button(b) = btn {
1426            match b.custom_id {
1427                Some(TitanString::Borrowed(s)) => assert_eq!(s, "custom_id"),
1428                _ => panic!("Expected Borrowed Cow for button custom_id"),
1429            }
1430        }
1431    }
1432}
1433
1434// ============================================================================
1435// Create Sticker Builder
1436// ============================================================================
1437
1438/// Payload for creating a sticker.
1439#[derive(Debug, Clone, serde::Serialize, Default)]
1440pub struct CreateSticker {
1441    pub name: String,
1442    pub description: String,
1443    pub tags: String,
1444}
1445
1446/// Builder for creating a Sticker.
1447#[derive(Debug, Clone)]
1448pub struct CreateStickerBuilder {
1449    params: CreateSticker,
1450}
1451
1452impl CreateStickerBuilder {
1453    /// Create a new CreateStickerBuilder.
1454    pub fn new(
1455        name: impl Into<String>,
1456        description: impl Into<String>,
1457        tags: impl Into<String>,
1458    ) -> Self {
1459        Self {
1460            params: CreateSticker {
1461                name: name.into(),
1462                description: description.into(),
1463                tags: tags.into(),
1464            },
1465        }
1466    }
1467
1468    /// Build the CreateSticker payload.
1469    pub fn build(self) -> CreateSticker {
1470        self.params
1471    }
1472}
1473
1474// ============================================================================
1475// Modify Emoji Builder
1476// ============================================================================
1477
1478/// Payload for modifying an emoji.
1479#[derive(Debug, Clone, serde::Serialize, Default)]
1480pub struct ModifyEmoji {
1481    #[serde(skip_serializing_if = "Option::is_none")]
1482    pub name: Option<String>,
1483    #[serde(skip_serializing_if = "Option::is_none")]
1484    pub roles: Option<Vec<crate::Snowflake>>,
1485}
1486
1487/// Builder for modifying an Emoji.
1488#[derive(Debug, Clone, Default)]
1489pub struct ModifyEmojiBuilder {
1490    params: ModifyEmoji,
1491}
1492
1493impl ModifyEmojiBuilder {
1494    /// Create a new ModifyEmojiBuilder.
1495    pub fn new() -> Self {
1496        Self::default()
1497    }
1498
1499    /// Set name.
1500    pub fn name(mut self, name: impl Into<String>) -> Self {
1501        self.params.name = Some(name.into());
1502        self
1503    }
1504
1505    /// Set roles.
1506    pub fn roles(mut self, roles: Vec<crate::Snowflake>) -> Self {
1507        self.params.roles = Some(roles);
1508        self
1509    }
1510
1511    /// Build the ModifyEmoji payload.
1512    pub fn build(self) -> ModifyEmoji {
1513        self.params
1514    }
1515}
1516
1517// ============================================================================
1518// Stage Instance Builder
1519// ============================================================================
1520
1521/// Payload for creating a stage instance.
1522#[derive(Debug, Clone, serde::Serialize, Default)]
1523pub struct CreateStageInstance {
1524    pub channel_id: crate::Snowflake,
1525    pub topic: String,
1526    #[serde(skip_serializing_if = "Option::is_none")]
1527    pub privacy_level: Option<crate::stage::StagePrivacyLevel>,
1528    #[serde(skip_serializing_if = "Option::is_none")]
1529    pub send_start_notification: Option<bool>,
1530}
1531
1532/// Builder for creating a Stage Instance.
1533#[derive(Debug, Clone)]
1534pub struct StageInstanceBuilder {
1535    params: CreateStageInstance,
1536}
1537
1538impl StageInstanceBuilder {
1539    /// Create a new StageInstanceBuilder.
1540    pub fn new(channel_id: impl Into<crate::Snowflake>, topic: impl Into<String>) -> Self {
1541        Self {
1542            params: CreateStageInstance {
1543                channel_id: channel_id.into(),
1544                topic: topic.into(),
1545                privacy_level: None,
1546                send_start_notification: None,
1547            },
1548        }
1549    }
1550
1551    /// Set privacy level.
1552    #[inline]
1553    pub fn privacy_level(mut self, level: crate::stage::StagePrivacyLevel) -> Self {
1554        self.params.privacy_level = Some(level);
1555        self
1556    }
1557
1558    /// Set send start notification.
1559    #[inline]
1560    pub fn send_start_notification(mut self, send: bool) -> Self {
1561        self.params.send_start_notification = Some(send);
1562        self
1563    }
1564
1565    /// Build the CreateStageInstance payload.
1566    #[inline]
1567    pub fn build(self) -> CreateStageInstance {
1568        self.params
1569    }
1570}
1571
1572// ============================================================================
1573// Guild Builders (Basic)
1574// ============================================================================
1575
1576/// Payload for creating a Guild.
1577#[derive(Debug, Clone, serde::Serialize, Default)]
1578pub struct CreateGuild {
1579    pub name: String,
1580    #[serde(skip_serializing_if = "Option::is_none")]
1581    pub icon: Option<String>,
1582    #[serde(skip_serializing_if = "Option::is_none")]
1583    pub verification_level: Option<u8>,
1584    #[serde(skip_serializing_if = "Option::is_none")]
1585    pub default_message_notifications: Option<u8>,
1586    #[serde(skip_serializing_if = "Option::is_none")]
1587    pub explicit_content_filter: Option<u8>,
1588    #[serde(skip_serializing_if = "Vec::is_empty")]
1589    pub roles: Vec<crate::json::Value>,
1590    #[serde(skip_serializing_if = "Vec::is_empty")]
1591    pub channels: Vec<crate::json::Value>,
1592    #[serde(skip_serializing_if = "Option::is_none")]
1593    pub afk_channel_id: Option<crate::Snowflake>,
1594    #[serde(skip_serializing_if = "Option::is_none")]
1595    pub afk_timeout: Option<u32>,
1596    #[serde(skip_serializing_if = "Option::is_none")]
1597    pub system_channel_id: Option<crate::Snowflake>,
1598    #[serde(skip_serializing_if = "Option::is_none")]
1599    pub system_channel_flags: Option<u64>,
1600}
1601
1602/// Builder for creating a Guild.
1603#[derive(Debug, Clone)]
1604pub struct CreateGuildBuilder {
1605    params: CreateGuild,
1606}
1607
1608impl CreateGuildBuilder {
1609    pub fn new(name: impl Into<String>) -> Self {
1610        Self {
1611            params: CreateGuild {
1612                name: name.into(),
1613                ..Default::default()
1614            },
1615        }
1616    }
1617
1618    #[inline]
1619    pub fn icon(mut self, icon: impl Into<String>) -> Self {
1620        self.params.icon = Some(icon.into());
1621        self
1622    }
1623
1624    pub fn verification_level(mut self, level: u8) -> Self {
1625        self.params.verification_level = Some(level);
1626        self
1627    }
1628
1629    pub fn build(self) -> CreateGuild {
1630        self.params
1631    }
1632}