1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
//! The declaration mistakes of a field's rules, and their wording.
use std::fmt;
/// Why a field's declared rules cannot work:
/// [`DeclarationErrorKind::FieldRule`](super::DeclarationErrorKind::FieldRule).
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum FieldRuleFault {
/// A `min` or `max` that is no value the field's type parses, or a type without an order.
///
/// Bound a number, date, time or timestamp field by a value of its type.
UnorderedBound(String),
/// A `pattern` the server does not compile, with the compiler's reason.
///
/// Fix the pattern.
InvalidPattern(String),
/// A `min_length` above the `max_length`, or a `min` above the `max`: no value passes.
///
/// Lower the minimum or raise the maximum.
EmptyRange,
/// A default that breaks a rule, so a create that keeps it is refused.
///
/// Change the default or the rule.
RefusedDefault(String),
/// A default the field's type does not parse, as a timestamp spelled without an offset, so a
/// create that keeps it is refused.
///
/// Spell the default as the type does, or pass a value of the type.
UnparsedDefault(String),
/// A rich text editor over a field whose type is no text, as a number or a timestamp,
/// carrying the input type its text control had.
NotText(&'static str),
/// `accepted` on a control other than the built-in checkbox or switch.
NotCheckbox,
/// `min_items` or `max_items` on a choice that is not `multiple`.
SingleValue,
/// A multiple choice's `min_items` above the count of static options it offers, carried.
FewerOptions(usize),
/// `tags` on a relationship's choice: a typed value names no record.
///
/// Drop `tags`, or offer static options as its suggestions.
TagsOverRecords,
/// A repeater's `max_items` above [`MAX_REPEATER_ROWS`](crate::MAX_REPEATER_ROWS),
/// carried.
AboveRowCap(usize),
/// A field always disabled, with no default, whose blank answer breaks its own rules: an
/// `accepted` checkbox or a `min_items`, so every create is refused.
RefusedBlank,
/// A comparison with a field the schema does not place, carried.
///
/// Place the compared field in the same schema, or in the same repeater row.
UnplacedComparedField(String),
/// A comparison with a field that is no text field of the same type, carried.
///
/// Compare two fields of one type: an `Option<T>` field compares with a `T` one.
IncomparableField(String),
/// An ordering comparison over a type without an order, carrying the compared field.
///
/// Order a number, date, time or timestamp field, or compare with `different`.
UnorderedComparison(String),
/// A comparison with the declaring field itself, so `gt` and `lt` refuse every value and the
/// others none.
///
/// Compare with another field.
SelfComparison,
/// A file field's `extensions` listing none, so it refuses every file.
///
/// List the extensions it takes, or drop `extensions` to take any.
NoExtension,
/// A file field's extension that is not letters and digits, carried: `tar.gz` or `*`, which
/// no file's last extension is, as the rule reads only the last.
///
/// List the last extension alone: `gz`.
InvalidExtension(String),
/// `max_files` on a file field over one path, from `Field::file`.
///
/// Bind a `Vec<String>` with `Field::files`, or drop `max_files`.
SingleFile,
/// A file field's `max_bytes` at or above the panel's body cap,
/// [`Limits::body_bytes`](crate::Limits::body_bytes): the body cap refuses a larger file
/// first, so the rule never refuses one.
///
/// Lower `max_bytes` below the cap, or raise the cap.
AboveFormCap {
/// The field's `max_bytes`.
max: usize,
/// The panel's body cap.
cap: usize,
},
}
/// [`DeclarationErrorKind::FieldRule`](super::DeclarationErrorKind::FieldRule)'s sentence.
pub(super) fn fmt_field_rule(
f: &mut fmt::Formatter<'_>,
field: &str,
fault: &FieldRuleFault,
) -> fmt::Result {
match fault {
FieldRuleFault::UnorderedBound(bound) => write!(
f,
"field '{field}' is bounded by '{bound}', which is no value of a type that orders: \
bound a number, date, time or timestamp field by a value of its type"
),
FieldRuleFault::InvalidPattern(error) => write!(
f,
"field '{field}' has a pattern that does not compile: {error}"
),
FieldRuleFault::EmptyRange => write!(
f,
"field '{field}' declares a minimum above its maximum, so it refuses every value"
),
FieldRuleFault::RefusedDefault(value) => write!(
f,
"field '{field}' defaults to '{value}', which its own rules refuse: change the default \
or the rule"
),
FieldRuleFault::UnparsedDefault(value) => write!(
f,
"field '{field}' defaults to '{value}', which is no value of its type: spell the \
default as the type does, or pass a value of the type"
),
FieldRuleFault::NotText(input) => write!(
f,
"field '{field}' is a rich text editor over a field its form edits as '{input}' \
input, but rich text stores HTML: make the field a `String` or `Option<String>`"
),
FieldRuleFault::NotCheckbox => write!(
f,
"field '{field}' is `accepted` but is not the built-in checkbox or switch: declare \
it with `Field::toggle` or `Field::toggle_input`"
),
FieldRuleFault::SingleValue => write!(
f,
"field '{field}' bounds how many options it takes, but takes one: make it `multiple` \
or drop `min_items` and `max_items`"
),
FieldRuleFault::FewerOptions(options) => write!(
f,
"field '{field}' needs more options chosen than the {options} it offers, so it \
refuses every value"
),
FieldRuleFault::TagsOverRecords => write!(
f,
"field '{field}' takes tags over a relationship's records, whose values a user \
cannot type: drop `tags`, or offer static options as its suggestions"
),
FieldRuleFault::AboveRowCap(max) => write!(
f,
"field '{field}' takes up to {max} rows, more than any repeater takes \
(`MAX_REPEATER_ROWS`): lower `max_items`"
),
FieldRuleFault::RefusedBlank => write!(
f,
"field '{field}' is disabled and has no default, but its own rules refuse its blank \
answer, so every create is refused: give it a default or drop the rule"
),
FieldRuleFault::UnplacedComparedField(other) => write!(
f,
"field '{field}' compares with field '{other}', which the schema does not place: \
place it in the same schema, or in the same repeater row"
),
FieldRuleFault::IncomparableField(other) => write!(
f,
"field '{field}' compares with field '{other}', which is not a text field of the same \
type: compare two fields of one type"
),
FieldRuleFault::UnorderedComparison(other) => write!(
f,
"field '{field}' orders against field '{other}', but its type has no order: compare \
a number, date, time or timestamp field, or use `different`"
),
FieldRuleFault::SelfComparison => write!(
f,
"field '{field}' compares with itself: compare it with another field"
),
FieldRuleFault::NoExtension => write!(
f,
"field '{field}' lists no extension it takes, so it refuses every file: list one, or \
drop `extensions`"
),
FieldRuleFault::InvalidExtension(extension) => write!(
f,
"field '{field}' takes the extension '{extension}', but a file's extension is the \
last one alone, in letters and digits: list that one"
),
FieldRuleFault::SingleFile => write!(
f,
"field '{field}' bounds how many files it takes, but stores one path: bind a \
`Vec<String>` with `Field::files`, or drop `max_files`"
),
FieldRuleFault::AboveFormCap { max, cap } => write!(
f,
"field '{field}' takes files up to {max} bytes, but the panel's form body holds at \
most {cap} bytes: lower `max_bytes` below it, or raise `Limits::body_bytes`"
),
}
}