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: Option<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(ref 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 tags = if tags.as_ref().map(|t| t.len()).unwrap_or(0) == 0 {
65 None
66 } else {
67 tags
68 };
69 let fm = Frontmatter {
70 pagination: pagination
71 .and_then(|p| pagination::PaginationConfig::from_config(p, &permalink)),
72 permalink,
73 slug: slug.ok_or_else(|| anyhow::format_err!("No slug"))?,
74 title: title.ok_or_else(|| anyhow::format_err!("No title"))?,
75 description,
76 excerpt,
77 categories: categories.unwrap_or_default(),
78 tags,
79 excerpt_separator: excerpt_separator.unwrap_or_else(|| "\n\n".into()),
80 published_date,
81 format: format.unwrap_or_default(),
82 #[cfg(feature = "preview_unstable")]
83 templated: templated.unwrap_or(false),
84 #[cfg(not(feature = "preview_unstable"))]
85 templated: templated.unwrap_or(true),
86 layout,
87 is_draft: is_draft.unwrap_or(false),
88 weight: weight.unwrap_or(0),
89 collection,
90 data,
91 };
92
93 if let Some(pagination) = &fm.pagination {
94 if !pagination::is_date_index_sorted(&pagination.date_index) {
95 anyhow::bail!("date_index is not correctly sorted: Year > Month > Day...");
96 }
97 }
98 Ok(fm)
99 }
100}
101
102impl fmt::Display for Frontmatter {
103 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
104 let converted = serde_yaml::to_string(self).expect("should always be valid");
105 let subset = converted
106 .strip_prefix("---")
107 .unwrap_or(converted.as_str())
108 .trim();
109 let converted = if subset == "{}" { "" } else { subset };
110 if converted.is_empty() {
111 Ok(())
112 } else {
113 write!(f, "{converted}")
114 }
115 }
116}