use std::collections::HashMap;
use serde::{Deserialize, Serialize};
use crate::contexts::annotation::AnnotationContext;
use crate::contexts::book::BookContext;
use crate::contexts::entry::EntryContext;
use crate::models::datetime::DateTimeUtc;
use crate::render::template::Template;
use crate::result::Result;
use crate::strings;
use crate::utils;
#[derive(Debug, Clone, Deserialize)]
pub struct Names {
#[serde(default = "Names::default_book")]
pub book: String,
#[serde(default = "Names::default_annotation")]
pub annotation: String,
#[serde(default = "Names::default_directory")]
pub directory: String,
}
impl Default for Names {
fn default() -> Self {
Self {
book: Self::default_book(),
annotation: Self::default_annotation(),
directory: Self::default_directory(),
}
}
}
impl Names {
fn default_book() -> String {
super::defaults::FILENAME_TEMPLATE_BOOK.to_owned()
}
fn default_annotation() -> String {
super::defaults::FILENAME_TEMPLATE_ANNOTATION.to_owned()
}
fn default_directory() -> String {
super::defaults::DIRECTORY_TEMPLATE.to_owned()
}
}
#[derive(Debug, Default, Clone, Serialize)]
pub struct NamesRender {
pub book: String,
#[serde(serialize_with = "utils::serialize_hashmap_to_vec")]
pub annotations: HashMap<String, AnnotationNameAttributes>,
pub directory: String,
}
impl NamesRender {
pub fn new(entry: &EntryContext<'_>, template: &Template) -> Result<Self> {
Ok(Self {
book: Self::render_book_filename(entry, template)?,
annotations: Self::render_annotation_filenames(entry, template)?,
directory: Self::render_directory_name(entry, template)?,
})
}
#[must_use]
#[allow(clippy::missing_panics_doc)]
pub fn get_annotation_filename(&self, annotation_id: &str) -> String {
self.annotations
.get(annotation_id)
.expect("`NamesRender` instance missing `Annotation` present in `Entry`")
.filename
.clone()
}
fn render_book_filename(entry: &EntryContext<'_>, template: &Template) -> Result<String> {
let context = NamesContext::book(&entry.book, &entry.annotations);
let filename = strings::render_and_sanitize(&template.names.book, context)?;
let filename = strings::build_filename_and_sanitize(&filename, &template.extension);
Ok(filename)
}
fn render_annotation_filenames(
entry: &EntryContext<'_>,
template: &Template,
) -> Result<HashMap<String, AnnotationNameAttributes>> {
let mut annotations = HashMap::new();
for annotation in &entry.annotations {
let context = NamesContext::annotation(&entry.book, annotation);
let filename = strings::render_and_sanitize(&template.names.annotation, context)?;
let filename = strings::build_filename_and_sanitize(&filename, &template.extension);
annotations.insert(
annotation.metadata.id.clone(),
AnnotationNameAttributes::new(annotation, filename),
);
}
Ok(annotations)
}
fn render_directory_name(entry: &EntryContext<'_>, template: &Template) -> Result<String> {
let context = NamesContext::directory(&entry.book);
strings::render_and_sanitize(&template.names.directory, context)
}
}
#[derive(Debug, Default, Clone, Serialize)]
pub struct AnnotationNameAttributes {
pub filename: String,
#[allow(missing_docs)]
pub created: DateTimeUtc,
#[allow(missing_docs)]
pub modified: DateTimeUtc,
#[allow(missing_docs)]
pub location: String,
}
impl AnnotationNameAttributes {
fn new(annotation: &AnnotationContext<'_>, filename: String) -> Self {
Self {
filename,
created: annotation.metadata.created,
modified: annotation.metadata.modified,
location: annotation.metadata.location.clone(),
}
}
}
#[derive(Debug, Serialize)]
#[serde(untagged)]
enum NamesContext<'a> {
Book {
book: &'a BookContext<'a>,
annotations: &'a [AnnotationContext<'a>],
},
Annotation {
book: &'a BookContext<'a>,
annotation: &'a AnnotationContext<'a>,
},
Directory { book: &'a BookContext<'a> },
}
impl<'a> NamesContext<'a> {
fn book(book: &'a BookContext<'a>, annotations: &'a [AnnotationContext<'a>]) -> Self {
Self::Book { book, annotations }
}
fn annotation(book: &'a BookContext<'a>, annotation: &'a AnnotationContext<'a>) -> Self {
Self::Annotation { book, annotation }
}
fn directory(book: &'a BookContext<'a>) -> Self {
Self::Directory { book }
}
}