twilight_validate/component/
component_v2.rs1use super::{
4 action_row, button, select_menu, text_input, ComponentValidationError,
5 ComponentValidationErrorType,
6};
7use twilight_model::channel::message::component::{
8 Container, MediaGallery, MediaGalleryItem, Section, TextDisplay, Thumbnail,
9};
10use twilight_model::channel::message::Component;
11
12pub const TEXT_DISPLAY_CONTENT_LENGTH_MAX: usize = 2000;
14
15pub const MEDIA_GALLERY_ITEMS_MIN: usize = 1;
17
18pub const MEDIA_GALLERY_ITEMS_MAX: usize = 10;
20
21pub const MEDIA_GALLERY_ITEM_DESCRIPTION_LENGTH_MAX: usize = 1024;
23
24pub const SECTION_COMPONENTS_MIN: usize = 1;
26
27pub const SECTION_COMPONENTS_MAX: usize = 3;
29
30pub const THUMBNAIL_DESCRIPTION_LENGTH_MAX: usize = 1024;
32
33pub fn component_v2(component: &Component) -> Result<(), ComponentValidationError> {
54 match component {
55 Component::ActionRow(ar) => action_row(ar, true)?,
56 Component::Button(b) => button(b)?,
57 Component::Container(c) => container(c)?,
58 Component::MediaGallery(mg) => media_gallery(mg)?,
59 Component::Section(s) => section(s)?,
60 Component::SelectMenu(sm) => select_menu(sm)?,
61 Component::TextDisplay(td) => text_display(td)?,
62 Component::TextInput(ti) => text_input(ti)?,
63 Component::Thumbnail(t) => thumbnail(t)?,
64 Component::Separator(_) | Component::File(_) | Component::Unknown(_) => (),
65 }
66
67 Ok(())
68}
69
70pub fn text_display(text_display: &TextDisplay) -> Result<(), ComponentValidationError> {
79 let content_len = text_display.content.len();
80 if content_len > TEXT_DISPLAY_CONTENT_LENGTH_MAX {
81 return Err(ComponentValidationError {
82 kind: ComponentValidationErrorType::TextDisplayContentTooLong { len: content_len },
83 });
84 }
85
86 Ok(())
87}
88
89pub fn media_gallery(media_gallery: &MediaGallery) -> Result<(), ComponentValidationError> {
101 let items = media_gallery.items.len();
102 if !(MEDIA_GALLERY_ITEMS_MIN..=MEDIA_GALLERY_ITEMS_MAX).contains(&items) {
103 return Err(ComponentValidationError {
104 kind: ComponentValidationErrorType::MediaGalleryItemCountOutOfRange { count: items },
105 });
106 }
107
108 for item in &media_gallery.items {
109 media_gallery_item(item)?;
110 }
111
112 Ok(())
113}
114
115pub fn section(section: &Section) -> Result<(), ComponentValidationError> {
130 let components = section.components.len();
131 if !(SECTION_COMPONENTS_MIN..=SECTION_COMPONENTS_MAX).contains(&components) {
132 return Err(ComponentValidationError {
133 kind: ComponentValidationErrorType::SectionComponentCountOutOfRange {
134 count: components,
135 },
136 });
137 }
138
139 for component in §ion.components {
140 match component {
141 Component::TextDisplay(td) => text_display(td)?,
142 _ => {
143 return Err(ComponentValidationError {
144 kind: ComponentValidationErrorType::DisallowedChildren,
145 })
146 }
147 }
148 }
149
150 match section.accessory.as_ref() {
151 Component::Button(b) => button(b)?,
152 Component::Thumbnail(t) => thumbnail(t)?,
153 _ => {
154 return Err(ComponentValidationError {
155 kind: ComponentValidationErrorType::DisallowedChildren,
156 })
157 }
158 }
159
160 Ok(())
161}
162
163pub fn container(container: &Container) -> Result<(), ComponentValidationError> {
180 for component in &container.components {
181 match component {
182 Component::ActionRow(ar) => action_row(ar, true)?,
183 Component::TextDisplay(td) => text_display(td)?,
184 Component::Section(s) => section(s)?,
185 Component::MediaGallery(mg) => media_gallery(mg)?,
186 Component::Separator(_) | Component::File(_) => (),
187 _ => {
188 return Err(ComponentValidationError {
189 kind: ComponentValidationErrorType::DisallowedChildren,
190 })
191 }
192 }
193 }
194
195 Ok(())
196}
197
198pub fn thumbnail(thumbnail: &Thumbnail) -> Result<(), ComponentValidationError> {
207 let Some(Some(desc)) = thumbnail.description.as_ref() else {
208 return Ok(());
209 };
210
211 let len = desc.len();
212 if len > THUMBNAIL_DESCRIPTION_LENGTH_MAX {
213 return Err(ComponentValidationError {
214 kind: ComponentValidationErrorType::ThumbnailDescriptionTooLong { len },
215 });
216 }
217
218 Ok(())
219}
220
221fn media_gallery_item(item: &MediaGalleryItem) -> Result<(), ComponentValidationError> {
230 let Some(desc) = item.description.as_ref() else {
231 return Ok(());
232 };
233
234 let len = desc.len();
235 if len > MEDIA_GALLERY_ITEM_DESCRIPTION_LENGTH_MAX {
236 return Err(ComponentValidationError {
237 kind: ComponentValidationErrorType::MediaGalleryItemDescriptionTooLong { len },
238 });
239 }
240
241 Ok(())
242}