Skip to main content

tablo_core/declaration/
field_rule.rs

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