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