///|
/// The limits Discord documents for a modal: its title, its top-level
/// component count, and every component it carries.
pub fn modal_limit_violations(
title~ : String,
components~ : Array[Component],
) -> Array[LimitViolation] {
let checker = LimitChecker::new()
checker.text("title", Some(title), max=45)
checker.count("components", Some(components.length()), min=1, max=5)
for index, component in components {
checker.modal_component("components[\{index}]", component)
}
checker.violations
}
///|
/// Discord's upper bound for a text input's `min_length`, `max_length`, and
/// `value`, in UTF-16 units.
pub let text_input_max_length : Int = 4000
///|
fn LimitChecker::modal_component(
self : LimitChecker,
path : String,
component : Component,
) -> Unit {
match component {
ActionRow(row) =>
for index, child in row.components {
self.modal_component("\{path}.components[\{index}]", child)
}
Label(label) => {
let custom_id = component_custom_id(label.component)
self.text("\{path}.label", label.label, max=45, custom_id?)
self.text("\{path}.description", label.description, max=100, custom_id?)
self.modal_component("\{path}.component", label.component)
}
TextInput(input) => {
let custom_id = input.custom_id
self.text(
"\{path}.custom_id",
Some(custom_id),
min=1,
max=100,
custom_id~,
)
self.count(
"\{path}.min_length",
input.min_length,
min=0,
max=text_input_max_length,
custom_id~,
)
self.count(
"\{path}.max_length",
input.max_length,
min=1,
max=text_input_max_length,
custom_id~,
)
self.ordered(
"\{path}.min_length",
input.min_length,
input.max_length,
custom_id~,
)
self.text(
"\{path}.value",
input.value,
max=text_input_max_length,
custom_id~,
)
self.text("\{path}.placeholder", input.placeholder, max=100, custom_id~)
}
StringSelect(select) =>
self.select_component(path, select, string_select=true)
UserSelect(select)
| RoleSelect(select)
| MentionableSelect(select)
| ChannelSelect(select) =>
self.select_component(path, select, string_select=false)
FileUpload(upload) => {
let custom_id = upload.custom_id
self.text(
"\{path}.custom_id",
Some(custom_id),
min=1,
max=100,
custom_id~,
)
self.count(
"\{path}.min_values",
upload.min_values,
min=0,
max=10,
custom_id~,
)
self.count(
"\{path}.max_values",
upload.max_values,
min=0,
max=10,
custom_id~,
)
self.ordered(
"\{path}.min_values",
upload.min_values,
upload.max_values,
custom_id~,
)
self.count(
"\{path}.file_types",
upload.file_types.map(types => types.length()),
min=0,
max=10,
custom_id~,
)
}
RadioGroup(group) => {
let custom_id = group.custom_id
self.text(
"\{path}.custom_id",
Some(custom_id),
min=1,
max=100,
custom_id~,
)
self.count(
"\{path}.options",
Some(group.options.length()),
min=2,
max=10,
custom_id~,
)
self.radio_options(path, group.options, custom_id~)
}
CheckboxGroup(group) => {
let custom_id = group.custom_id
self.text(
"\{path}.custom_id",
Some(custom_id),
min=1,
max=100,
custom_id~,
)
self.count(
"\{path}.options",
Some(group.options.length()),
min=1,
max=10,
custom_id~,
)
self.count(
"\{path}.min_values",
group.min_values,
min=if group.required == Some(false) { 0 } else { 1 },
max=10,
custom_id~,
)
self.count(
"\{path}.max_values",
group.max_values,
min=1,
max=10,
custom_id~,
)
self.ordered(
"\{path}.min_values",
group.min_values,
group.max_values,
custom_id~,
)
self.radio_options(path, group.options, custom_id~)
}
Checkbox(checkbox) =>
self.text(
"\{path}.custom_id",
Some(checkbox.custom_id),
min=1,
max=100,
custom_id=checkbox.custom_id,
)
// The reference gives Text Display no content length limit.
TextDisplay(_) => ()
_ => ()
}
}
///|
fn LimitChecker::radio_options(
self : LimitChecker,
path : String,
options : Array[RadioOption],
custom_id~ : String,
) -> Unit {
for index, option in options {
let path = "\{path}.options[\{index}]"
self.text("\{path}.label", Some(option.label), max=100, custom_id~)
self.text("\{path}.value", Some(option.value), max=100, custom_id~)
self.text("\{path}.description", option.description, max=100, custom_id~)
}
}
///|
/// The custom id of an interactive component, if it has one.
fn component_custom_id(component : Component) -> String? {
match component {
Button({ custom_id, .. }) => custom_id
StringSelect({ custom_id, .. })
| UserSelect({ custom_id, .. })
| RoleSelect({ custom_id, .. })
| MentionableSelect({ custom_id, .. })
| ChannelSelect({ custom_id, .. }) => Some(custom_id)
TextInput({ custom_id, .. }) => Some(custom_id)
FileUpload({ custom_id, .. }) => Some(custom_id)
RadioGroup({ custom_id, .. }) => Some(custom_id)
CheckboxGroup({ custom_id, .. }) => Some(custom_id)
Checkbox({ custom_id, .. }) => Some(custom_id)
_ => None
}
}