cobalt/pagination/
categories.rs1use std::collections::BTreeMap;
2
3use crate::document::Document;
4
5use super::{
6 PaginationConfig, Result, ValueView, create_all_paginators, helpers, paginator, sort_posts,
7};
8use helpers::extract_categories;
9use paginator::Paginator;
10
11pub(crate) fn create_categories_paginators(
12 all_posts: &[&liquid::model::Value],
13 doc: &Document,
14 pagination_cfg: &PaginationConfig,
15) -> Result<Vec<Paginator>> {
16 let mut root_cat = distribute_posts_by_categories(all_posts)?;
17 let paginators_holder = walk_categories(&mut root_cat, pagination_cfg, doc)?;
18 Ok(paginators_holder)
19}
20
21fn distribute_posts_by_categories<'a>(
22 all_posts: &[&'a liquid::model::Value],
23) -> Result<Category<'a>> {
24 let mut root = Category::new();
25 for post in all_posts {
26 if let Some(categories) = extract_categories(post.as_view()) {
27 let categories: Vec<_> = categories.values().collect();
28 parse_categories_list(&mut root, categories.as_slice(), post)?;
29 }
30 }
31 Ok(root)
32}
33
34fn parse_categories_list<'a>(
36 mut parent: &mut Category<'a>,
37 post_categories: &[&dyn ValueView],
38 post: &'a liquid::model::Value,
39) -> Result<()> {
40 for i in 0..post_categories.len() {
41 let cat_name = post_categories[i].to_kstr().to_string();
42 parent = parent
43 .sub_cats
44 .entry(cat_name)
45 .or_insert_with(|| Category::with_path(post_categories[0..=i].iter().copied()));
46 }
47 parent.add_post(post);
48 Ok(())
49}
50
51#[derive(Default, Debug)]
52struct Category<'a> {
53 cat_path: liquid::model::Array,
54 posts: Vec<&'a liquid::model::Value>,
55 sub_cats: BTreeMap<String, Category<'a>>,
56}
57
58impl<'a> Category<'a> {
59 fn new() -> Self {
60 Default::default()
61 }
62
63 fn with_path<'v>(path: impl Iterator<Item = &'v dyn ValueView>) -> Self {
64 let mut c = Self::new();
65 c.cat_path = path.map(|v| v.to_value()).collect();
66 c
67 }
68
69 fn add_post(&mut self, post: &'a liquid::model::Value) {
70 self.posts.push(post);
71 }
72}
73
74fn walk_categories(
77 category: &mut Category<'_>,
78 config: &PaginationConfig,
79 doc: &Document,
80) -> Result<Vec<Paginator>> {
81 let mut cur_cat_paginators_holder: Vec<Paginator> = vec![];
82 if !category.cat_path.is_empty() {
83 sort_posts(&mut category.posts, config);
84 let cur_cat_paginators = create_all_paginators(
85 &category.posts,
86 doc,
87 config,
88 Some(&liquid::model::Value::array(category.cat_path.clone())),
89 )?;
90 if !cur_cat_paginators.is_empty() {
91 cur_cat_paginators_holder.extend(cur_cat_paginators);
92 } else {
93 let p = Paginator {
94 index_title: Some(liquid::model::Value::array(category.cat_path.clone())),
95 ..Default::default()
96 };
97 cur_cat_paginators_holder.push(p);
98 }
99 } else {
100 cur_cat_paginators_holder.push(Paginator::default());
101 }
102 for c in category.sub_cats.values_mut() {
103 let mut sub_paginators_holder = walk_categories(c, config, doc)?;
104
105 if let Some(indexes) = cur_cat_paginators_holder[0].indexes.as_mut() {
106 indexes.push(sub_paginators_holder[0].clone());
107 } else {
108 cur_cat_paginators_holder[0].indexes = Some(vec![sub_paginators_holder[0].clone()]);
109 }
110 cur_cat_paginators_holder.append(&mut sub_paginators_holder);
111 }
112 Ok(cur_cat_paginators_holder)
113}