pub struct ValidationError {
pub line: usize,
pub field: Option<String>,
pub message: String,
}Expand description
A problem with one row of a table. field names the column at fault when
the check is specific to one, and is null for a whole-row check.
Fields§
§line: usizeThe row’s one-based position in the set being validated, which is the row the editor highlights. Blank lines in the stored file are skipped on the way in, so this need not be the file line the row was read from.
field: Option<String>§message: StringImplementations§
Source§impl ValidationError
impl ValidationError
Sourcepub fn field(
line: usize,
field: impl Into<String>,
message: impl Into<String>,
) -> Self
pub fn field( line: usize, field: impl Into<String>, message: impl Into<String>, ) -> Self
A problem with the column field of the row on line.
Examples found in repository?
examples/library/main.rs (lines 255-259)
247 fn validate(&self, rows: &[Book], ctx: &Context) -> Result<Vec<ValidationError>, ApiError> {
248 let genres = Self::genres(ctx)?;
249 let branches = Self::branches(ctx)?;
250 let mut errors = Vec::new();
251
252 for (idx, row) in rows.iter().enumerate() {
253 let line = idx + 1;
254 if row.title.trim().is_empty() {
255 errors.push(ValidationError::field(
256 line,
257 "title",
258 "a book needs a title",
259 ));
260 }
261
262 // A subgenre stands or falls with the genre it was chosen under.
263 if !genres.is_empty() && !row.subgenre.is_empty() {
264 let known = genres
265 .iter()
266 .any(|g| g.genre == row.genre && g.subgenre == row.subgenre);
267 if !known {
268 errors.push(ValidationError::field(
269 line,
270 "subgenre",
271 format!("not a subgenre of {}", row.genre),
272 ));
273 }
274 }
275
276 if !branches.is_empty() {
277 for branch in row.shelved.keys() {
278 if !branches.iter().any(|b| b.code == *branch) {
279 errors.push(ValidationError::field(
280 line,
281 "shelved",
282 format!("no branch has the code {branch}"),
283 ));
284 }
285 }
286 }
287 }
288
289 Ok(errors)
290 }
291
292 fn derive(&self, rows: &[Book], _ctx: &Context) -> Result<Vec<Value>, ApiError> {
293 Ok(rows
294 .iter()
295 .map(|row| json!({ "shelf": Self::shelf_mark(row) }))
296 .collect())
297 }
298
299 fn siblings(&self, ctx: &Context) -> Result<Value, ApiError> {
300 Ok(json!({ "genres": Self::genres(ctx)? }))
301 }
302}
303
304// ── Genres ──────────────────────────────────────────────────────────────────
305
306struct Genres;
307
308impl TableLogic for Genres {
309 type Row = Genre;
310
311 fn name(&self) -> &'static str {
312 "genres"
313 }
314
315 fn file(&self) -> &'static str {
316 GENRES_FILE
317 }
318
319 fn title(&self) -> &'static str {
320 "Genres"
321 }
322
323 fn schema(&self, _ctx: &Context) -> Result<Schema, ApiError> {
324 Ok(Schema::new([
325 Column::string("genre", "Genre").width_ch(18),
326 Column::string("subgenre", "Subgenre").width_ch(22),
327 ])
328 .sortable()
329 .new_row(
330 NewRow::new()
331 .with("genre", "")
332 .with("subgenre", "")
333 .carry_forward(["genre"]),
334 ))
335 }
336
337 fn validate(&self, rows: &[Genre], _ctx: &Context) -> Result<Vec<ValidationError>, ApiError> {
338 let mut errors = Vec::new();
339 for (idx, row) in rows.iter().enumerate() {
340 if row.genre.trim().is_empty() {
341 errors.push(ValidationError::field(
342 idx + 1,
343 "genre",
344 "a genre is needed",
345 ));
346 }
347 if row.subgenre.trim().is_empty() {
348 errors.push(ValidationError::field(
349 idx + 1,
350 "subgenre",
351 "a subgenre is needed",
352 ));
353 }
354 }
355 Ok(errors)
356 }
357}
358
359// ── Branches ────────────────────────────────────────────────────────────────
360
361struct Branches;
362
363impl TableLogic for Branches {
364 type Row = Branch;
365
366 fn name(&self) -> &'static str {
367 "branches"
368 }
369
370 fn file(&self) -> &'static str {
371 BRANCHES_FILE
372 }
373
374 fn title(&self) -> &'static str {
375 "Branches"
376 }
377
378 fn schema(&self, _ctx: &Context) -> Result<Schema, ApiError> {
379 // Row order here is the order the branches are listed in, which is a
380 // choice the table makes, so this table is not sortable and rows are
381 // dragged into place instead.
382 Ok(Schema::new([
383 Column::string("code", "Code").width_ch(3),
384 Column::string("name", "Name").width_ch(22),
385 Column::string("librarian_first", "Librarian").width_ch(8),
386 Column::string("librarian_last", "Surname")
387 .width_ch(8)
388 .datalist("librarian-names"),
389 Column::number("staff", "Staff").int_only().width_ch(2),
390 Column::boolean("open", "Open"),
391 Column::map(
392 "hours",
393 "Hours",
394 MapSpec::new("Day", "Hours")
395 .key_options(["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"])
396 .value_options([
397 SelectOption::new("09:00-17:00"),
398 SelectOption::new("12:00-20:00"),
399 ])
400 .allow_new_keys()
401 .allow_new_values(),
402 ),
403 ])
404 .datalist(
405 "librarian-names",
406 Datalist::from_rows(["librarian_last"], " "),
407 )
408 .new_row(
409 NewRow::new()
410 .with("code", "")
411 .with("name", "")
412 .with("librarian_first", "")
413 .with("librarian_last", ""),
414 ))
415 }
416
417 fn validate(&self, rows: &[Branch], _ctx: &Context) -> Result<Vec<ValidationError>, ApiError> {
418 let mut errors = Vec::new();
419 for (idx, row) in rows.iter().enumerate() {
420 if row.code.trim().is_empty() {
421 errors.push(ValidationError::field(
422 idx + 1,
423 "code",
424 "a branch needs a code",
425 ));
426 }
427 }
428 Ok(errors)
429 }Trait Implementations§
Source§impl Clone for ValidationError
impl Clone for ValidationError
Source§fn clone(&self) -> ValidationError
fn clone(&self) -> ValidationError
Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
Performs copy-assignment from
source. Read moreSource§impl Debug for ValidationError
impl Debug for ValidationError
Source§impl<'de> Deserialize<'de> for ValidationError
impl<'de> Deserialize<'de> for ValidationError
Source§fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
Deserialize this value from the given Serde deserializer. Read more
impl Eq for ValidationError
Source§impl PartialEq for ValidationError
impl PartialEq for ValidationError
Source§impl Serialize for ValidationError
impl Serialize for ValidationError
impl StructuralPartialEq for ValidationError
Auto Trait Implementations§
impl Freeze for ValidationError
impl RefUnwindSafe for ValidationError
impl Send for ValidationError
impl Sync for ValidationError
impl Unpin for ValidationError
impl UnsafeUnpin for ValidationError
impl UnwindSafe for ValidationError
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Mutably borrows from an owned value. Read more