1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
|
use jellycommon::{NodeKind, NodePublic};
use rocket::{
http::uri::fmt::{Query, UriDisplay},
FromForm, FromFormField, UriDisplayQuery,
};
#[derive(FromForm, UriDisplayQuery)]
pub struct NodeFilterSort {
sort_by: Option<SortProperty>,
filter_kind: Option<Vec<NodeKind>>,
sort_order: Option<SortOrder>,
}
#[rustfmt::skip]
#[derive(FromFormField, UriDisplayQuery, Clone, Copy, PartialEq, Eq)]
enum SortProperty {
#[field(value = "release_date")] ReleaseDate,
#[field(value = "title")] Title,
}
#[rustfmt::skip]
#[derive(FromFormField, UriDisplayQuery, Clone, Copy, PartialEq, Eq)]
enum SortOrder {
#[field(value = "ascending")] Ascending,
#[field(value = "descending")] Descending,
}
pub fn filter_and_sort_nodes(f: &NodeFilterSort, nodes: &mut Vec<(String, NodePublic)>) {
nodes.retain(|(_id, node)| {
let mut o = true;
if let Some(kind) = &f.filter_kind {
o &= kind.contains(&node.kind)
}
o
});
if let Some(sort_prop) = f.sort_by {
match sort_prop {
SortProperty::ReleaseDate => nodes.sort_by_key(|(_, _n)| 0), // TODO
SortProperty::Title => nodes.sort_by(|(_, a), (_, b)| a.title.cmp(&b.title)),
}
}
match f.sort_order.unwrap_or(SortOrder::Ascending) {
SortOrder::Ascending => (),
SortOrder::Descending => nodes.reverse(),
}
}
markup::define! {
NodeFilterSortForm<'a>(f: &'a NodeFilterSort) {
details {
summary { "Filter and Sort" }
form[method="GET", action=""] {
fieldset {
legend { "Filter" }
@use NodeKind::*;
@for (value, label) in [(Movie, "Movie"), (Episode, "Episode"), (Video, "Video"), (Channel, "Channel")] {
label { input[type="checkbox", name="filter_kind", value=A(value), checked=f.filter_kind.as_ref().map(|k|k.contains(&value)).unwrap_or(true)]; @label } br;
}
}
fieldset {
legend { "Sort By" }
@use SortProperty::*;
@for (value, label) in [(Title, "Title"), (ReleaseDate, "Release Date")] {
label { input[type="radio", name="sort_by", value=value, checked=Some(value)==f.sort_by]; @label } br;
}
}
fieldset {
legend { "Sort Order" }
@use SortOrder::*;
@for (value, label) in [(Ascending, "Ascending"), (Descending, "Descending")] {
label { input[type="radio", name="sort_order", value=value, checked=Some(value)==f.sort_order]; @label } br;
}
}
input[type="submit", value="Apply"];
}
}
}
}
impl markup::Render for SortProperty {
fn render(&self, writer: &mut impl std::fmt::Write) -> std::fmt::Result {
writer.write_fmt(format_args!("{}", self as &dyn UriDisplay<Query>))
}
}
impl markup::Render for SortOrder {
fn render(&self, writer: &mut impl std::fmt::Write) -> std::fmt::Result {
writer.write_fmt(format_args!("{}", self as &dyn UriDisplay<Query>))
}
}
struct A(NodeKind);
impl markup::Render for A {
fn render(&self, writer: &mut impl std::fmt::Write) -> std::fmt::Result {
writer.write_fmt(format_args!("{}", &self.0 as &dyn UriDisplay<Query>))
}
}
|