cobalt/pagination/
tags.rs

1use std::collections::HashMap;
2
3use crate::cobalt_model::pagination::PaginationConfig;
4use crate::cobalt_model::slug;
5use crate::document::Document;
6
7use super::{Result, ValueView, create_all_paginators, helpers, paginator, sort_posts};
8use helpers::extract_tags;
9use paginator::Paginator;
10
11#[allow(clippy::bind_instead_of_map)]
12pub(crate) fn create_tags_paginators(
13    all_posts: &[&liquid::model::Value],
14    doc: &Document,
15    pagination_cfg: &PaginationConfig,
16) -> Result<Vec<Paginator>> {
17    let mut per_tags = distribute_posts_by_tags(all_posts)?;
18
19    // create all other paginators
20    let mut tag_paginators: TagPaginators = per_tags
21        .iter_mut()
22        .try_fold(TagPaginators::default(), |mut acc, (tag, posts)| {
23            sort_posts(posts, pagination_cfg);
24            let cur_tag_paginators = create_all_paginators(
25                posts,
26                doc,
27                pagination_cfg,
28                Some(&liquid::model::Value::scalar(tag.to_owned())),
29            )?;
30            acc.firsts_of_tags.push(cur_tag_paginators[0].clone());
31            acc.paginators.extend(cur_tag_paginators.into_iter());
32            Ok(acc)
33        })
34        .or_else(std::result::Result::<_, anyhow::Error>::Err)?;
35
36    tag_paginators.firsts_of_tags.sort_unstable_by_key(|p| {
37        p.index_title
38            .as_ref()
39            .map(|index_title| slug::slugify(index_title.to_kstr()).to_lowercase())
40    });
41    let first = Paginator {
42        indexes: Some(tag_paginators.firsts_of_tags),
43        ..Default::default()
44    };
45    tag_paginators.paginators.insert(0, first);
46    Ok(tag_paginators.paginators)
47}
48
49fn distribute_posts_by_tags<'a>(
50    all_posts: &[&'a liquid::model::Value],
51) -> Result<HashMap<String, Vec<&'a liquid::model::Value>>> {
52    let mut per_tags: HashMap<String, Vec<&'a liquid::model::Value>> = HashMap::new();
53    for post in all_posts {
54        if let Some(tags) = extract_tags(post.as_view()) {
55            for tag in tags.values() {
56                let tag = tag
57                    .as_scalar()
58                    .ok_or_else(|| anyhow::format_err!("Should have string tags"))?
59                    .to_kstr()
60                    .into_string();
61                let cur_tag = per_tags.entry(tag).or_default();
62                cur_tag.push(post);
63            }
64        }
65    }
66    Ok(per_tags)
67}
68
69#[derive(Default, Debug)]
70struct TagPaginators {
71    firsts_of_tags: Vec<Paginator>,
72    paginators: Vec<Paginator>,
73}