cobalt/pagination/
dates.rs1use crate::cobalt_model::DateTime;
2use crate::cobalt_model::pagination::DateIndex;
3use crate::document::Document;
4
5use super::{PaginationConfig, Result, ValueView, all, helpers, paginator, sort_posts};
6use helpers::extract_scalar;
7use paginator::Paginator;
8
9pub(crate) fn create_dates_paginators(
10 all_posts: &[&liquid::model::Value],
11 doc: &Document,
12 config: &PaginationConfig,
13) -> Result<Vec<Paginator>> {
14 let mut root_date = distribute_posts_by_dates(all_posts, config)?;
15 walk_dates(&mut root_date, config, doc, None)
16}
17
18fn distribute_posts_by_dates<'a>(
19 all_posts: &[&'a liquid::model::Value],
20 config: &PaginationConfig,
21) -> Result<DateIndexHolder<'a>> {
22 let date_index = &config.date_index;
23 let mut root = DateIndexHolder::new(0u32, None);
24 for post in all_posts {
25 if let Some(published_date) = extract_published_date(post.as_view()) {
26 for idx in date_index {
27 find_or_create_date_holder_and_put_post(&mut root, &published_date, *idx, post);
28 }
29 }
30 }
31 Ok(root)
32}
33#[derive(Debug, Clone)]
34struct DateIndexHolder<'a> {
35 value: u32,
36 field: Option<DateIndex>,
37 posts: Vec<&'a liquid::model::Value>,
38 sub_date: Vec<DateIndexHolder<'a>>,
39}
40
41impl DateIndexHolder<'_> {
42 fn new(value: u32, field: Option<DateIndex>) -> Self {
43 DateIndexHolder {
44 value,
45 field,
46 posts: vec![],
47 sub_date: vec![],
48 }
49 }
50}
51
52fn extract_published_date(value: &'_ dyn ValueView) -> Option<DateTime> {
53 let published_date = extract_scalar(value, "published_date")?;
54 published_date.to_date_time()
55}
56
57fn format_date_holder(d: &DateIndexHolder<'_>) -> liquid::model::Value {
58 let field = d
59 .field
60 .expect("Should not be called with the root DateIndexHolder");
61 let formatted = match field {
62 DateIndex::Year => d.value.to_string(),
63 _ => format!("{:02}", d.value),
64 };
65 liquid::model::Value::scalar(formatted)
66}
67
68fn date_fields_to_array(date: &[DateIndexHolder<'_>]) -> liquid::model::Array {
69 date.iter().map(format_date_holder).collect()
70}
71
72fn find_or_create_date_holder_and_put_post<'a>(
73 date_holder: &mut DateIndexHolder<'a>,
74 published_date: &DateTime,
75 wanted_field: DateIndex,
76 post: &'a liquid::model::Value,
77) {
78 let value = get_date_field_value(published_date, wanted_field);
79 let mut not_found = true;
80 for dh in date_holder.sub_date.iter_mut() {
81 let dh_field = dh
82 .field
83 .expect("Only root has None, we should always have a field");
84 if dh_field < wanted_field {
85 let parent_value = get_date_field_value(published_date, dh_field);
88 if dh.value == parent_value {
89 find_or_create_date_holder_and_put_post(dh, published_date, wanted_field, post);
90 not_found = false;
91 }
92 } else if dh_field == wanted_field && dh.value == value {
93 dh.posts.push(post);
94 not_found = false;
95 }
96 }
97 if not_found {
99 let mut holder = DateIndexHolder::new(value, Some(wanted_field));
100 holder.posts.push(post);
101 date_holder.sub_date.push(holder);
102 }
103}
104
105fn get_date_field_value(date: &DateTime, field: DateIndex) -> u32 {
106 match field {
107 DateIndex::Year => {
108 if date.year() < 0 {
109 panic!("Negative year is not supported");
110 }
111 date.year() as u32
112 }
113 DateIndex::Month => date.month() as u32,
114 DateIndex::Day => date.day() as u32,
115 DateIndex::Hour => date.hour() as u32,
116 DateIndex::Minute => date.minute() as u32,
117 }
118}
119
120fn walk_dates(
121 date_holder: &mut DateIndexHolder<'_>,
122 config: &PaginationConfig,
123 doc: &Document,
124 parent_dates: Option<Vec<DateIndexHolder<'_>>>,
125) -> Result<Vec<Paginator>> {
126 let mut paginators: Vec<Paginator> = vec![];
127 let mut current_date = parent_dates.unwrap_or_default();
128 if let Some(_field) = date_holder.field {
129 sort_posts(&mut date_holder.posts, config);
130 current_date.push(date_holder.clone());
131 let index_title = liquid::model::Value::array(date_fields_to_array(¤t_date));
132 let cur_paginators =
133 all::create_all_paginators(&date_holder.posts, doc, config, Some(&index_title))?;
134 if !cur_paginators.is_empty() {
135 paginators.extend(cur_paginators);
136 } else {
137 let p = Paginator {
138 index_title: Some(index_title),
139 ..Default::default()
140 };
141 paginators.push(p);
142 }
143 } else {
144 paginators.push(Paginator::default());
145 }
146 for dh in &mut date_holder.sub_date {
147 let mut sub_paginators = walk_dates(dh, config, doc, Some(current_date.clone()))?;
148
149 if let Some(indexes) = paginators[0].indexes.as_mut() {
150 indexes.push(sub_paginators[0].clone());
151 } else {
152 paginators[0].indexes = Some(vec![sub_paginators[0].clone()]);
153 }
154 paginators.append(&mut sub_paginators);
155 }
156 Ok(paginators)
157}