typst_library/model/
document.rs1use ecow::EcoString;
2
3use crate::diag::{bail, HintedStrResult, SourceResult};
4use crate::engine::Engine;
5use crate::foundations::{
6 cast, elem, Args, Array, Construct, Content, Datetime, Fields, OneOrMultiple, Smart,
7 StyleChain, Styles, Value,
8};
9
10#[elem(Construct)]
27pub struct DocumentElem {
28 #[ghost]
34 pub title: Option<Content>,
35
36 #[ghost]
38 pub author: OneOrMultiple<EcoString>,
39
40 #[ghost]
42 pub description: Option<Content>,
43
44 #[ghost]
46 pub keywords: OneOrMultiple<EcoString>,
47
48 #[ghost]
60 pub date: Smart<Option<Datetime>>,
61}
62
63impl Construct for DocumentElem {
64 fn construct(_: &mut Engine, args: &mut Args) -> SourceResult<Content> {
65 bail!(args.span, "can only be used in set rules")
66 }
67}
68
69#[derive(Debug, Default, Clone, PartialEq, Hash)]
71pub struct Author(Vec<EcoString>);
72
73cast! {
74 Author,
75 self => self.0.into_value(),
76 v: EcoString => Self(vec![v]),
77 v: Array => Self(v.into_iter().map(Value::cast).collect::<HintedStrResult<_>>()?),
78}
79
80#[derive(Debug, Default, Clone, PartialEq, Hash)]
82pub struct Keywords(Vec<EcoString>);
83
84cast! {
85 Keywords,
86 self => self.0.into_value(),
87 v: EcoString => Self(vec![v]),
88 v: Array => Self(v.into_iter().map(Value::cast).collect::<HintedStrResult<_>>()?),
89}
90
91#[derive(Debug, Default, Clone, PartialEq, Hash)]
93pub struct DocumentInfo {
94 pub title: Option<EcoString>,
96 pub author: Vec<EcoString>,
98 pub description: Option<EcoString>,
100 pub keywords: Vec<EcoString>,
102 pub date: Smart<Option<Datetime>>,
104}
105
106impl DocumentInfo {
107 pub fn populate(&mut self, styles: &Styles) {
111 let chain = StyleChain::new(styles);
112 let has = |field| styles.has::<DocumentElem>(field as _);
113 if has(<DocumentElem as Fields>::Enum::Title) {
114 self.title =
115 DocumentElem::title_in(chain).map(|content| content.plain_text());
116 }
117 if has(<DocumentElem as Fields>::Enum::Author) {
118 self.author = DocumentElem::author_in(chain).0;
119 }
120 if has(<DocumentElem as Fields>::Enum::Description) {
121 self.description =
122 DocumentElem::description_in(chain).map(|content| content.plain_text());
123 }
124 if has(<DocumentElem as Fields>::Enum::Keywords) {
125 self.keywords = DocumentElem::keywords_in(chain).0;
126 }
127 if has(<DocumentElem as Fields>::Enum::Date) {
128 self.date = DocumentElem::date_in(chain);
129 }
130 }
131}