cobalt/cobalt_model/
frontmatter.rs1use std::fmt;
2
3use cobalt_config::DateTime;
4use cobalt_config::SourceFormat;
5use liquid;
6use serde::Serialize;
7
8use super::pagination;
9use crate::error::Result;
10
11#[derive(Debug, Eq, PartialEq, Default, Clone, Serialize)]
12#[serde(deny_unknown_fields, default)]
13pub struct Frontmatter {
14 pub permalink: cobalt_config::Permalink,
15 pub slug: liquid::model::KString,
16 pub title: liquid::model::KString,
17 pub description: Option<liquid::model::KString>,
18 pub excerpt: Option<liquid::model::KString>,
19 pub categories: Vec<liquid::model::KString>,
20 pub tags: Vec<liquid::model::KString>,
21 pub excerpt_separator: liquid::model::KString,
22 pub published_date: Option<DateTime>,
23 pub format: SourceFormat,
24 pub templated: bool,
25 pub layout: Option<liquid::model::KString>,
26 pub is_draft: bool,
27 pub weight: i32,
28 pub collection: liquid::model::KString,
29 pub data: liquid::Object,
30 pub pagination: Option<pagination::PaginationConfig>,
31}
32
33impl Frontmatter {
34 pub fn from_config(config: cobalt_config::Frontmatter) -> Result<Frontmatter> {
35 let cobalt_config::Frontmatter {
36 permalink,
37 slug,
38 title,
39 description,
40 excerpt,
41 categories,
42 tags,
43 excerpt_separator,
44 published_date,
45 format,
46 templated,
47 layout,
48 is_draft,
49 weight,
50 collection,
51 data,
52 pagination,
53 } = config;
54
55 let collection = collection.unwrap_or_default();
56
57 let permalink = permalink.unwrap_or_default();
58
59 if let Some(tags) = &tags {
60 if tags.iter().any(|x| x.trim().is_empty()) {
61 anyhow::bail!("Empty strings are not allowed in tags");
62 }
63 }
64 let fm = Frontmatter {
65 pagination: pagination
66 .and_then(|p| pagination::PaginationConfig::from_config(p, &permalink)),
67 permalink,
68 slug: slug.ok_or_else(|| anyhow::format_err!("No slug"))?,
69 title: title.ok_or_else(|| anyhow::format_err!("No title"))?,
70 description,
71 excerpt,
72 categories: categories.unwrap_or_default(),
73 tags: tags.unwrap_or_default(),
74 excerpt_separator: excerpt_separator.unwrap_or_else(|| "\n\n".into()),
75 published_date,
76 format: format.unwrap_or_default(),
77 #[cfg(feature = "preview_unstable")]
78 templated: templated.unwrap_or(false),
79 #[cfg(not(feature = "preview_unstable"))]
80 templated: templated.unwrap_or(true),
81 layout,
82 is_draft: is_draft.unwrap_or(false),
83 weight: weight.unwrap_or(0),
84 collection,
85 data,
86 };
87
88 if let Some(pagination) = &fm.pagination {
89 if !pagination::is_date_index_sorted(&pagination.date_index) {
90 anyhow::bail!("date_index is not correctly sorted: Year > Month > Day...");
91 }
92 }
93 Ok(fm)
94 }
95}
96
97impl fmt::Display for Frontmatter {
98 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
99 let converted = serde_yaml::to_string(self).expect("should always be valid");
100 let subset = converted
101 .strip_prefix("---")
102 .unwrap_or(converted.as_str())
103 .trim();
104 let converted = if subset == "{}" { "" } else { subset };
105 if converted.is_empty() {
106 Ok(())
107 } else {
108 write!(f, "{converted}")
109 }
110 }
111}