pub struct ViewArgs(/* private fields */);Expand description
What a view was asked for.
It holds the address’s parameters, with a declared parameter the address
left out filled in from its default. Everything the address carried is
kept, including keys no parameter names, so a view may read more than it
declares; ViewArgs::iter walks all of it.
A parameter the address gave a value its options no longer offer falls back to the default. That is what happens when one parameter’s options depend on another’s value and the other has just changed: a subgenre that belonged to the genre before this one is not an answer to the question being asked now. An empty value is a value: a parameter cleared on purpose stays cleared rather than filling itself in again.
Implementations§
Source§impl ViewArgs
impl ViewArgs
pub fn get(&self, key: &str) -> Option<&str>
Sourcepub fn get_or<'a>(&'a self, key: &str, fallback: &'a str) -> &'a str
pub fn get_or<'a>(&'a self, key: &str, fallback: &'a str) -> &'a str
The value of key, or fallback where the address and the parameter’s
own default both said nothing.
Examples found in repository?
534 fn params(&self, ctx: &Context, asked: &ViewArgs) -> Result<Vec<Param>, ApiError> {
535 let branches = Self::branches(ctx)?;
536 let options: Vec<SelectOption> = branches
537 .iter()
538 .map(|b| SelectOption::labelled(&b.code, &b.name))
539 .collect();
540 let first = branches.first().map(|b| b.code.clone()).unwrap_or_default();
541
542 // A genre and one of its subgenres. The second list follows the first
543 // one's answer, which is why `params` is told what was asked before
544 // anything is settled. A subgenre belonging to the genre chosen last
545 // time is no answer to the question being asked now, and falls back to
546 // every subgenre of the genre chosen this time.
547 let genres: Vec<Genre> = ctx.optional_rows(GENRES_FILE)?;
548 let mut named: Vec<&str> = genres.iter().map(|g| g.genre.as_str()).collect();
549 named.sort_unstable();
550 named.dedup();
551
552 let chosen = asked.get_or("genre", "");
553 let mut subgenres: Vec<&str> = genres
554 .iter()
555 .filter(|g| g.genre == chosen)
556 .map(|g| g.subgenre.as_str())
557 .collect();
558 subgenres.sort_unstable();
559
560 Ok(vec![
561 Param::select("branch", "Branch", options).default(first),
562 Param::select("genre", "Genre", Self::any("Every genre", named)).default(""),
563 Param::select(
564 "subgenre",
565 "Subgenre",
566 Self::any("Every subgenre", subgenres),
567 )
568 .default(""),
569 Param::string("author", "Author"),
570 ])
571 }
572
573 fn render(&self, args: &ViewArgs, ctx: &Context) -> Result<ViewData, ApiError> {
574 let books: Vec<Book> = ctx.optional_rows(BOOKS_FILE)?;
575 let branches = Self::branches(ctx)?;
576 let code = args.get_or("branch", "");
577 let branch = branches.iter().find(|b| b.code == code);
578 let named = branch.map(|b| b.name.as_str()).unwrap_or(code);
579
580 let genre = args.get_or("genre", "");
581 let subgenre = args.get_or("subgenre", "");
582 let author = args.get_or("author", "").trim().to_lowercase();
583
584 // The books this branch holds that answer the rest of the question,
585 // whether they are out or not. A parameter left empty asks nothing.
586 let held: Vec<&Book> = books
587 .iter()
588 .filter(|b| b.shelved.contains_key(code))
589 .filter(|b| genre.is_empty() || b.genre == genre)
590 .filter(|b| subgenre.is_empty() || b.subgenre == subgenre)
591 .filter(|b| {
592 author.is_empty()
593 || format!("{} {}", b.author_first, b.author_last)
594 .to_lowercase()
595 .contains(&author)
596 })
597 .collect();
598
599 let now = today();
600 let mut out = Vec::new();
601 let mut overdue = Vec::new();
602 for book in held.iter().filter(|b| b.lent.unwrap_or(false)) {
603 let days = days_from_civil(&book.due).map(|due| now - due).unwrap_or(0);
604 if days > 0 {
605 overdue.push(OnLoan::row(book, days));
606 } else {
607 out.push(OnLoan::row(book, days));
608 }
609 }
610
611 let lent = out.len() + overdue.len();
612 Ok(ViewData::new()
613 .note(format!(
614 "{lent} of {} book(s) at {named} are out on loan. A title links to its catalogue entry.",
615 held.len()
616 ))
617 .section(
618 Section::new(OnLoan::columns())
619 .heading("Out")
620 .note("Days left before they are due.")
621 .rows(out)?,
622 )
623 .section(
624 Section::new(OnLoan::columns())
625 .heading("Overdue")
626 .note("Days past due. Chase these.")
627 .rows(overdue)?,
628 ))
629 }Sourcepub fn iter(&self) -> impl Iterator<Item = (&str, &str)>
pub fn iter(&self) -> impl Iterator<Item = (&str, &str)>
Every key and value, in order, including those no parameter declares.