1use crate::money::rounded;
2use crate::render::{filled, Named, Piece};
3use patterns_macros::because;
4
5pub fn widened_usize(value: usize) -> u64 {
6 u64::try_from(value).unwrap_or_default()
7}
8because!(widened_usize, "a length or an index as the number a page answers, the widening a binding may not write as a cast");
9
10pub fn named_from<T: Named + Copy>(items: &[T], text: &str) -> Option<T> {
11 items.iter().copied().find(|item| item.text() == text)
12}
13because!(named_from, "the value of a vocabulary whose text is the one given, or nothing, which is how a request parameter becomes a value without a match in a binding");
14
15pub fn label_of_keyed<K: PartialEq, T: Copy>(items: &[T], key_of: fn(&T) -> K, key: K, label: fn(&T) -> String, fallback: &str) -> String {
16 items
17 .iter()
18 .find(|item| key_of(item) == key)
19 .map(label)
20 .unwrap_or_else(|| String::from(fallback))
21}
22because!(label_of_keyed, "the label of the vocabulary entry whose key matches, or a fallback word, which is how a pool address becomes the venue name the feed shows");
23
24pub fn sum_seconds<A>(pairs: &[(A, u64)]) -> u64 {
25 pairs.iter().map(|(_, n)| *n).fold(0u64, |a, b| a.saturating_add(b))
26}
27because!(sum_seconds, "the sum of the counts in a list of pairs, the total a feed shows beside its all entry");
28
29pub fn sum_thirds<A, B>(triples: &[(A, B, u64)]) -> u64 {
30 triples.iter().map(|(_, _, n)| *n).fold(0u64, |a, b| a.saturating_add(b))
31}
32because!(sum_thirds, "the sum of the counts in a list of triples, the same total read from a filter menu that already carries labels");
33
34pub fn options_with_counts<K: Copy + PartialEq>(kinds: &[K], totals: &[(K, u64)], label: fn(&K) -> String) -> Vec<(K, String, u64)> {
35 kinds
36 .iter()
37 .map(|kind| {
38 let count = totals.iter().find(|(k, _)| k == kind).map(|(_, n)| *n).unwrap_or(0);
39 (*kind, label(kind), count)
40 })
41 .filter(|(_, _, count)| *count > 0)
42 .collect()
43}
44because!(options_with_counts, "a filter menu: every kind the menu offers with its label and how many rows it has, with the empty kinds left out, in the order the spec lists them");
45
46pub fn faced<A, P: Named>(items: &[A], pieces_of: fn(&A) -> &'static [Piece<P>], first: &str, second: &str, third: &str, fourth: &str) -> Vec<String> {
47 items
48 .iter()
49 .map(|item| filled(pieces_of(item), first, second, third, fourth))
50 .collect()
51}
52because!(faced, "the text of every item filled from the pieces a spec table gives it, so a binding renders a whole menu of labels with one call");
53
54pub fn picked_names(flags: &[bool], yes: &str, no: &str) -> Vec<String> {
55 flags
56 .iter()
57 .map(|flag| match flag {
58 true => String::from(yes),
59 false => String::from(no),
60 })
61 .collect()
62}
63because!(picked_names, "one of two names for each flag, the token name each feed row is denominated in");
64
65pub fn converted_maybe<A, B: From<A>>(items: Option<Vec<A>>) -> Option<Vec<B>> {
66 items.map(|list| list.into_iter().map(B::from).collect())
67}
68because!(converted_maybe, "an optional list converted when present, the optional twin of converted");
69
70pub fn facet_of<F, T: crate::seq::Facet<F>>(item: &T) -> T::Value {
71 item.facet()
72}
73because!(facet_of, "one projection of one item, the single form of a facet, so a binding hands a projection to a list pattern as a plain function");
74
75pub fn counted_rounded(value: f64, places: u32) -> f64 {
76 rounded(value, places)
77}
78because!(counted_rounded, "a figure rounded to places, kept here beside the view helpers so the views module reads whole; the same rounding as everywhere else");
79
80pub fn listed_maybe<T: Clone>(value: &Option<T>) -> Option<T> {
81 value.clone()
82}
83because!(listed_maybe, "a borrowed maybe as an owned one, so a binding hands an optional row on without a method call");
84
85pub fn far_future() -> u64 {
86 !0u64
87}
88because!(far_future, "the largest moment, every bit set, the upper edge of a date range a reader left open");
89
90pub fn keep_in<A: Clone, B: Copy + PartialEq>(items: &[A], read: fn(&A) -> B, allowed: &[B]) -> Vec<A> {
91 items
92 .iter()
93 .filter(|item| allowed.contains(&read(item)))
94 .cloned()
95 .collect()
96}
97because!(keep_in, "the items whose projection is one of the allowed values, for a table that shows some columns only on some deployments");
98
99pub fn looked_up<K: Copy + PartialEq, V: Copy>(keys: &[K], table: &[(K, V)], fallback: V) -> Vec<V> {
100 keys.iter()
101 .map(|key| table.iter().find(|(k, _)| k == key).map(|(_, v)| *v).unwrap_or(fallback))
102 .collect()
103}
104because!(looked_up, "one value per key out of a small table of pairs, or a fallback where the table has no entry, the counts a menu shows beside its entries");
105
106pub fn converted_each<A, B: From<A>>(lists: Vec<Vec<A>>) -> Vec<Vec<B>> {
107 lists
108 .into_iter()
109 .map(|list| list.into_iter().map(B::from).collect())
110 .collect()
111}
112because!(converted_each, "a list of lists converted through the conversion a spec declares, the nested twin of converted");
113
114pub fn text_of(text: &str) -> String {
115 String::from(text)
116}
117because!(text_of, "a borrowed text as an owned one, the copy a binding may not make with a method");
118
119pub fn described<A, B, P: Named + 'static>(items: &[A], pieces_of: fn(&A) -> &'static [Piece<P>], word_of: fn(&A) -> fn(&B) -> &'static [Piece<P>], arg: &B, first: &str, second: &str, third: &str, blank: &str) -> Vec<String> {
120 items
121 .iter()
122 .map(|item| {
123 let fourth = filled(word_of(item)(arg), first, second, third, blank);
124 filled(pieces_of(item), first, second, third, &fourth)
125 })
126 .collect()
127}
128because!(described, "the text of every item where the fourth hole is itself a phrase chosen per item from a table keyed by something else, a badge's description naming the venue its deployment trades on");