titanium_model/builder/
component.rs

1use crate::TitanString;
2
3/// Builder for creating a Button.
4#[derive(Debug, Clone)]
5#[must_use]
6pub struct ButtonBuilder<'a> {
7    inner: crate::component::Button<'a>,
8}
9
10impl Default for ButtonBuilder<'_> {
11    fn default() -> Self {
12        Self::new()
13    }
14}
15
16impl<'a> ButtonBuilder<'a> {
17    /// Create a new `ButtonBuilder`.
18    #[inline]
19    pub fn new() -> Self {
20        Self {
21            inner: crate::component::Button {
22                style: crate::component::ButtonStyle::Primary,
23                label: None,
24                emoji: None,
25                custom_id: None,
26                url: None,
27                disabled: false,
28                component_type: crate::component::ComponentType::Button,
29            },
30        }
31    }
32
33    /// Create a primary (blurple) button.
34    #[inline]
35    pub fn primary(
36        label: impl Into<TitanString<'a>>,
37        custom_id: impl Into<TitanString<'a>>,
38    ) -> Self {
39        Self::new()
40            .style(crate::component::ButtonStyle::Primary)
41            .label(label)
42            .custom_id(custom_id)
43    }
44
45    /// Create a secondary (grey) button.
46    #[inline]
47    pub fn secondary(
48        label: impl Into<TitanString<'a>>,
49        custom_id: impl Into<TitanString<'a>>,
50    ) -> Self {
51        Self::new()
52            .style(crate::component::ButtonStyle::Secondary)
53            .label(label)
54            .custom_id(custom_id)
55    }
56
57    /// Create a success (green) button.
58    #[inline]
59    pub fn success(
60        label: impl Into<TitanString<'a>>,
61        custom_id: impl Into<TitanString<'a>>,
62    ) -> Self {
63        Self::new()
64            .style(crate::component::ButtonStyle::Success)
65            .label(label)
66            .custom_id(custom_id)
67    }
68
69    /// Create a danger (red) button.
70    #[inline]
71    pub fn danger(
72        label: impl Into<TitanString<'a>>,
73        custom_id: impl Into<TitanString<'a>>,
74    ) -> Self {
75        Self::new()
76            .style(crate::component::ButtonStyle::Danger)
77            .label(label)
78            .custom_id(custom_id)
79    }
80
81    /// Create a link button (opens URL).
82    #[inline]
83    pub fn link(label: impl Into<TitanString<'a>>, url: impl Into<TitanString<'a>>) -> Self {
84        Self::new()
85            .style(crate::component::ButtonStyle::Link)
86            .label(label)
87            .url(url)
88    }
89
90    /// Set style.
91    pub fn style(mut self, style: crate::component::ButtonStyle) -> Self {
92        self.inner.style = style;
93        self
94    }
95
96    /// Set label.
97    pub fn label(mut self, label: impl Into<TitanString<'a>>) -> Self {
98        self.inner.label = Some(label.into());
99        self
100    }
101
102    /// Set emoji.
103    pub fn emoji(mut self, emoji: impl Into<crate::reaction::ReactionEmoji<'a>>) -> Self {
104        self.inner.emoji = Some(emoji.into());
105        self
106    }
107
108    /// Set custom ID.
109    pub fn custom_id(mut self, id: impl Into<TitanString<'a>>) -> Self {
110        self.inner.custom_id = Some(id.into());
111        self
112    }
113
114    /// Set URL.
115    pub fn url(mut self, url: impl Into<TitanString<'a>>) -> Self {
116        self.inner.url = Some(url.into());
117        self
118    }
119
120    /// Set disabled.
121    pub fn disabled(mut self, disabled: bool) -> Self {
122        self.inner.disabled = disabled;
123        self
124    }
125
126    /// Build the Component.
127    #[must_use]
128    pub fn build(self) -> crate::Component<'a> {
129        crate::Component::Button(self.inner)
130    }
131}
132
133/// Builder for creating a Select Menu.
134#[derive(Debug, Clone)]
135#[must_use]
136pub struct SelectMenuBuilder<'a> {
137    inner: crate::component::SelectMenu<'a>,
138}
139
140impl Default for SelectMenuBuilder<'_> {
141    fn default() -> Self {
142        Self::new("default_select")
143    }
144}
145
146impl<'a> SelectMenuBuilder<'a> {
147    /// Create a new `SelectMenuBuilder`.
148    #[inline]
149    pub fn new(custom_id: impl Into<TitanString<'a>>) -> Self {
150        Self {
151            inner: crate::component::SelectMenu {
152                custom_id: custom_id.into(),
153                options: Vec::with_capacity(25), // Discord max options
154                placeholder: None,
155                min_values: None,
156                max_values: None,
157                disabled: false,
158                component_type: crate::component::ComponentType::StringSelect, // Default
159            },
160        }
161    }
162
163    /// Add an option.
164    pub fn option(
165        mut self,
166        label: impl Into<TitanString<'a>>,
167        value: impl Into<TitanString<'a>>,
168    ) -> Self {
169        self.inner.options.push(crate::component::SelectOption {
170            label: label.into(),
171            value: value.into(),
172            description: None,
173            emoji: None,
174            default: false,
175        });
176        self
177    }
178
179    /// Set placeholder.
180    pub fn placeholder(mut self, placeholder: impl Into<TitanString<'a>>) -> Self {
181        self.inner.placeholder = Some(placeholder.into());
182        self
183    }
184
185    /// Set min values.
186    pub fn min_values(mut self, min: u8) -> Self {
187        self.inner.min_values = Some(min);
188        self
189    }
190
191    /// Set max values.
192    pub fn max_values(mut self, max: u8) -> Self {
193        self.inner.max_values = Some(max);
194        self
195    }
196
197    /// Set disabled.
198    pub fn disabled(mut self, disabled: bool) -> Self {
199        self.inner.disabled = disabled;
200        self
201    }
202
203    /// Build the Component.
204    #[must_use]
205    pub fn build(self) -> crate::Component<'a> {
206        crate::Component::SelectMenu(self.inner)
207    }
208}
209
210/// Builder for creating an Action Row.
211#[derive(Debug, Clone)]
212#[must_use]
213pub struct ActionRowBuilder<'a> {
214    inner: crate::component::ActionRow<'a>,
215}
216
217impl Default for ActionRowBuilder<'_> {
218    fn default() -> Self {
219        Self::new()
220    }
221}
222
223impl<'a> ActionRowBuilder<'a> {
224    pub fn new() -> Self {
225        Self {
226            inner: crate::component::ActionRow {
227                components: Vec::with_capacity(5), // Discord max components per row
228                component_type: crate::component::ComponentType::ActionRow,
229            },
230        }
231    }
232
233    pub fn add_button(mut self, button: ButtonBuilder<'a>) -> Self {
234        self.inner.components.push(button.build());
235        self
236    }
237
238    pub fn add_select_menu(mut self, menu: SelectMenuBuilder<'a>) -> Self {
239        self.inner.components.push(menu.build());
240        self
241    }
242
243    #[must_use]
244    pub fn build(self) -> crate::Component<'a> {
245        crate::Component::ActionRow(self.inner)
246    }
247
248    pub fn input(mut self, input: TextInputBuilder<'a>) -> Self {
249        self.inner.components.push(input.build());
250        self
251    }
252}
253
254/// Builder for creating a Text Input.
255#[derive(Debug, Clone)]
256#[must_use]
257pub struct TextInputBuilder<'a> {
258    inner: crate::component::TextInput<'a>,
259}
260
261impl<'a> TextInputBuilder<'a> {
262    pub fn new(
263        custom_id: impl Into<TitanString<'a>>,
264        style: crate::component::TextInputStyle,
265        label: impl Into<TitanString<'a>>,
266    ) -> Self {
267        Self {
268            inner: crate::component::TextInput {
269                component_type: crate::component::ComponentType::TextInput,
270                custom_id: custom_id.into(),
271                style,
272                label: label.into(),
273                min_length: None,
274                max_length: None,
275                required: None,
276                value: None,
277                placeholder: None,
278            },
279        }
280    }
281
282    pub fn min_length(mut self, min: u16) -> Self {
283        self.inner.min_length = Some(min);
284        self
285    }
286
287    pub fn max_length(mut self, max: u16) -> Self {
288        self.inner.max_length = Some(max);
289        self
290    }
291
292    pub fn required(mut self, required: bool) -> Self {
293        self.inner.required = Some(required);
294        self
295    }
296
297    pub fn value(mut self, value: impl Into<TitanString<'a>>) -> Self {
298        self.inner.value = Some(value.into());
299        self
300    }
301
302    pub fn placeholder(mut self, placeholder: impl Into<TitanString<'a>>) -> Self {
303        self.inner.placeholder = Some(placeholder.into());
304        self
305    }
306
307    #[must_use]
308    pub fn build(self) -> crate::Component<'a> {
309        crate::Component::TextInput(self.inner)
310    }
311}
312
313/// `ButtonBuilder` automatically converts to Component
314impl<'a> From<ButtonBuilder<'a>> for crate::Component<'a> {
315    #[inline]
316    fn from(builder: ButtonBuilder<'a>) -> Self {
317        builder.build()
318    }
319}
320
321/// `SelectMenuBuilder` automatically converts to Component
322impl<'a> From<SelectMenuBuilder<'a>> for crate::Component<'a> {
323    #[inline]
324    fn from(builder: SelectMenuBuilder<'a>) -> Self {
325        builder.build()
326    }
327}
328
329/// `ActionRowBuilder` automatically converts to Component
330impl<'a> From<ActionRowBuilder<'a>> for crate::Component<'a> {
331    #[inline]
332    fn from(builder: ActionRowBuilder<'a>) -> Self {
333        builder.build()
334    }
335}