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 256-260)
248 fn validate(&self, rows: &[Book], ctx: &Context) -> Result<Vec<ValidationError>, ApiError> {
249 let genres = Self::genres(ctx)?;
250 let branches = Self::branches(ctx)?;
251 let mut errors = Vec::new();
252
253 for (idx, row) in rows.iter().enumerate() {
254 let line = idx + 1;
255 if row.title.trim().is_empty() {
256 errors.push(ValidationError::field(
257 line,
258 "title",
259 "a book needs a title",
260 ));
261 }
262
263 // A subgenre stands or falls with the genre it was chosen under.
264 if !genres.is_empty() && !row.subgenre.is_empty() {
265 let known = genres
266 .iter()
267 .any(|g| g.genre == row.genre && g.subgenre == row.subgenre);
268 if !known {
269 errors.push(ValidationError::field(
270 line,
271 "subgenre",
272 format!("not a subgenre of {}", row.genre),
273 ));
274 }
275 }
276
277 if !branches.is_empty() {
278 for branch in row.shelved.keys() {
279 if !branches.iter().any(|b| b.code == *branch) {
280 errors.push(ValidationError::field(
281 line,
282 "shelved",
283 format!("no branch has the code {branch}"),
284 ));
285 }
286 }
287 }
288 }
289
290 Ok(errors)
291 }
292
293 fn derive(&self, rows: &[Book], _ctx: &Context) -> Result<Vec<Value>, ApiError> {
294 Ok(rows
295 .iter()
296 .map(|row| json!({ "shelf": Self::shelf_mark(row) }))
297 .collect())
298 }
299
300 fn siblings(&self, ctx: &Context) -> Result<Value, ApiError> {
301 Ok(json!({ "genres": Self::genres(ctx)? }))
302 }
303}
304
305// ── Genres ──────────────────────────────────────────────────────────────────
306
307struct Genres;
308
309impl TableLogic for Genres {
310 type Row = Genre;
311
312 fn name(&self) -> &'static str {
313 "genres"
314 }
315
316 fn file(&self) -> &'static str {
317 GENRES_FILE
318 }
319
320 fn title(&self) -> &'static str {
321 "Genres"
322 }
323
324 fn schema(&self, _ctx: &Context) -> Result<Schema, ApiError> {
325 Ok(Schema::new([
326 Column::string("genre", "Genre").width_ch(18),
327 Column::string("subgenre", "Subgenre").width_ch(22),
328 ])
329 .sortable()
330 .new_row(
331 NewRow::new()
332 .with("genre", "")
333 .with("subgenre", "")
334 .carry_forward(["genre"]),
335 ))
336 }
337
338 fn validate(&self, rows: &[Genre], _ctx: &Context) -> Result<Vec<ValidationError>, ApiError> {
339 let mut errors = Vec::new();
340 for (idx, row) in rows.iter().enumerate() {
341 if row.genre.trim().is_empty() {
342 errors.push(ValidationError::field(
343 idx + 1,
344 "genre",
345 "a genre is needed",
346 ));
347 }
348 if row.subgenre.trim().is_empty() {
349 errors.push(ValidationError::field(
350 idx + 1,
351 "subgenre",
352 "a subgenre is needed",
353 ));
354 }
355 }
356 Ok(errors)
357 }
358}
359
360// ── Branches ────────────────────────────────────────────────────────────────
361
362struct Branches;
363
364impl TableLogic for Branches {
365 type Row = Branch;
366
367 fn name(&self) -> &'static str {
368 "branches"
369 }
370
371 fn file(&self) -> &'static str {
372 BRANCHES_FILE
373 }
374
375 fn title(&self) -> &'static str {
376 "Branches"
377 }
378
379 fn schema(&self, _ctx: &Context) -> Result<Schema, ApiError> {
380 // Row order here is the order the branches are listed in, which is a
381 // choice the table makes, so this table is not sortable and rows are
382 // dragged into place instead.
383 Ok(Schema::new([
384 Column::string("code", "Code").width_ch(3),
385 Column::string("name", "Name").width_ch(22),
386 Column::string("librarian_first", "Librarian").width_ch(8),
387 Column::string("librarian_last", "Surname")
388 .width_ch(8)
389 .datalist("librarian-names"),
390 Column::number("staff", "Staff").int_only().width_ch(2),
391 Column::boolean("open", "Open"),
392 Column::map(
393 "hours",
394 "Hours",
395 MapSpec::new("Day", "Hours")
396 .key_options(["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"])
397 .value_options([
398 SelectOption::new("09:00-17:00"),
399 SelectOption::new("12:00-20:00"),
400 ])
401 .allow_new_keys()
402 .allow_new_values(),
403 ),
404 ])
405 .datalist(
406 "librarian-names",
407 Datalist::from_rows(["librarian_last"], " "),
408 )
409 .new_row(
410 NewRow::new()
411 .with("code", "")
412 .with("name", "")
413 .with("librarian_first", "")
414 .with("librarian_last", ""),
415 ))
416 }
417
418 fn validate(&self, rows: &[Branch], _ctx: &Context) -> Result<Vec<ValidationError>, ApiError> {
419 let mut errors = Vec::new();
420 for (idx, row) in rows.iter().enumerate() {
421 if row.code.trim().is_empty() {
422 errors.push(ValidationError::field(
423 idx + 1,
424 "code",
425 "a branch needs a code",
426 ));
427 }
428 }
429 Ok(errors)
430 }Trait Implementations§
Source§impl Clone for ValidationError
impl Clone for ValidationError
Source§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