use crate::cobalt_model::DateTime;
use crate::cobalt_model::pagination::DateIndex;
use crate::document::Document;
use super::{PaginationConfig, Result, ValueView, all, helpers, paginator, sort_posts};
use helpers::extract_scalar;
use paginator::Paginator;
pub(crate) fn create_dates_paginators(
all_posts: &[&liquid::model::Value],
doc: &Document,
config: &PaginationConfig,
) -> Result<Vec<Paginator>> {
let mut root_date = distribute_posts_by_dates(all_posts, config)?;
walk_dates(&mut root_date, config, doc, None)
}
fn distribute_posts_by_dates<'a>(
all_posts: &[&'a liquid::model::Value],
config: &PaginationConfig,
) -> Result<DateIndexHolder<'a>> {
let date_index = &config.date_index;
let mut root = DateIndexHolder::new(0u32, None);
for post in all_posts {
if let Some(published_date) = extract_published_date(post.as_view()) {
for idx in date_index {
find_or_create_date_holder_and_put_post(&mut root, &published_date, *idx, post);
}
}
}
Ok(root)
}
#[derive(Debug, Clone)]
struct DateIndexHolder<'a> {
value: u32,
field: Option<DateIndex>,
posts: Vec<&'a liquid::model::Value>,
sub_date: Vec<DateIndexHolder<'a>>,
}
impl DateIndexHolder<'_> {
fn new(value: u32, field: Option<DateIndex>) -> Self {
DateIndexHolder {
value,
field,
posts: vec![],
sub_date: vec![],
}
}
}
fn extract_published_date(value: &'_ dyn ValueView) -> Option<DateTime> {
let published_date = extract_scalar(value, "published_date")?;
published_date.to_date_time()
}
fn format_date_holder(d: &DateIndexHolder<'_>) -> liquid::model::Value {
let field = d
.field
.expect("Should not be called with the root DateIndexHolder");
let formatted = match field {
DateIndex::Year => d.value.to_string(),
_ => format!("{:02}", d.value),
};
liquid::model::Value::scalar(formatted)
}
fn date_fields_to_array(date: &[DateIndexHolder<'_>]) -> liquid::model::Array {
date.iter().map(format_date_holder).collect()
}
fn find_or_create_date_holder_and_put_post<'a>(
date_holder: &mut DateIndexHolder<'a>,
published_date: &DateTime,
wanted_field: DateIndex,
post: &'a liquid::model::Value,
) {
let value = get_date_field_value(published_date, wanted_field);
let mut not_found = true;
for dh in date_holder.sub_date.iter_mut() {
let dh_field = dh
.field
.expect("Only root has None, we should always have a field");
if dh_field < wanted_field {
let parent_value = get_date_field_value(published_date, dh_field);
if dh.value == parent_value {
find_or_create_date_holder_and_put_post(dh, published_date, wanted_field, post);
not_found = false;
}
} else if dh_field == wanted_field && dh.value == value {
dh.posts.push(post);
not_found = false;
}
}
if not_found {
let mut holder = DateIndexHolder::new(value, Some(wanted_field));
holder.posts.push(post);
date_holder.sub_date.push(holder);
}
}
fn get_date_field_value(date: &DateTime, field: DateIndex) -> u32 {
match field {
DateIndex::Year => {
if date.year() < 0 {
panic!("Negative year is not supported");
}
date.year() as u32
}
DateIndex::Month => date.month() as u32,
DateIndex::Day => date.day() as u32,
DateIndex::Hour => date.hour() as u32,
DateIndex::Minute => date.minute() as u32,
}
}
fn walk_dates(
date_holder: &mut DateIndexHolder<'_>,
config: &PaginationConfig,
doc: &Document,
parent_dates: Option<Vec<DateIndexHolder<'_>>>,
) -> Result<Vec<Paginator>> {
let mut paginators: Vec<Paginator> = vec![];
let mut current_date = parent_dates.unwrap_or_default();
if let Some(_field) = date_holder.field {
sort_posts(&mut date_holder.posts, config);
current_date.push(date_holder.clone());
let index_title = liquid::model::Value::array(date_fields_to_array(¤t_date));
let cur_paginators =
all::create_all_paginators(&date_holder.posts, doc, config, Some(&index_title))?;
if !cur_paginators.is_empty() {
paginators.extend(cur_paginators);
} else {
let p = Paginator {
index_title: Some(index_title),
..Default::default()
};
paginators.push(p);
}
} else {
paginators.push(Paginator::default());
}
for dh in &mut date_holder.sub_date {
let mut sub_paginators = walk_dates(dh, config, doc, Some(current_date.clone()))?;
if let Some(indexes) = paginators[0].indexes.as_mut() {
indexes.push(sub_paginators[0].clone());
} else {
paginators[0].indexes = Some(vec![sub_paginators[0].clone()]);
}
paginators.append(&mut sub_paginators);
}
Ok(paginators)
}