1mod tests;
2pub mod parsing;
3pub mod output;
4pub mod actions;
5pub mod reference_doc;
6
7use std::{
8 num::ParseIntError,
9 collections::{ HashMap, HashSet },
10};
11
12use pest_derive::Parser;
13
14#[derive(Parser)]
16#[grammar = "parse/incodoc.pest"]
17pub struct IncodocParser;
18
19#[derive(Clone, Default, Debug, Eq, PartialEq)]
21pub struct Doc {
22 pub tags: Tags,
23 pub props: Props,
24 pub items: Vec<DocItem>,
25}
26
27#[derive(Clone, Debug, Eq, PartialEq)]
29pub enum DocItem {
30 Nav(Nav),
32 Paragraph(Paragraph),
33 Section(Section),
34}
35
36pub type Tags = HashSet<String>;
38
39pub type Props = HashMap<String, PropVal>;
41
42#[derive(Clone, Debug, Eq, PartialEq)]
44pub enum PropVal {
45 String(String),
46 Text(String),
47 Int(i64),
48 Date(Date),
49 Error(PropValError),
50}
51
52#[derive(Clone, Debug, Eq, PartialEq)]
54pub enum PropValError {
55 Int(ParseIntError),
56 Date(DateError),
57}
58
59impl PropVal {
60 fn is_error(&self) -> bool {
61 matches![self, PropVal::Error(_)]
62 }
63}
64
65fn insert_prop(props: &mut Props, (k, v): (String, PropVal)) {
66 let mut insert = true;
67 if v.is_error() && let Some(ov) = props.get(&k) && !ov.is_error() {
68 insert = false;
69 }
70 if insert {
71 props.insert(k, v);
72 }
73}
74
75#[derive(Clone, Default, Debug, Eq, PartialEq)]
77pub struct Section {
78 pub heading: Heading,
79 pub items: Vec<SectionItem>,
80 pub tags: Tags,
81 pub props: Props,
82}
83
84#[derive(Clone, Debug, Eq, PartialEq)]
86pub enum SectionItem {
87 Paragraph(Paragraph),
88 Section(Section),
89}
90
91#[derive(Clone, Default, Debug, Eq, PartialEq)]
93pub struct Heading {
94 pub level: u8,
95 pub items: Vec<HeadingItem>,
96 pub tags: Tags,
97 pub props: Props,
98}
99
100#[derive(Clone, Debug, Eq, PartialEq)]
102pub enum HeadingItem {
103 String(String),
104 Em(Emphasis),
105}
106
107#[derive(Clone, Default, Debug, Eq, PartialEq)]
109pub struct Paragraph {
110 pub items: Vec<ParagraphItem>,
111 pub tags: Tags,
112 pub props: Props,
113}
114
115#[derive(Clone, Debug, Eq, PartialEq)]
117pub enum ParagraphItem {
118 Text(String),
119 MText(TextWithMeta),
120 Em(Emphasis),
121 Code(Result<CodeBlock, CodeIdentError>),
122 Link(Link),
123 List(List),
124}
125
126#[derive(Clone, Default, Debug, Eq, PartialEq)]
128pub struct Emphasis {
129 pub strength: EmStrength,
130 pub etype: EmType,
131 pub text: String,
132 pub tags: Tags,
133 pub props: Props,
134}
135
136#[derive(Clone, Copy, Default, Hash, Debug, Eq, PartialEq, Ord, PartialOrd)]
138pub enum EmStrength {
139 #[default]
140 Light,
141 Medium,
142 Strong,
143}
144
145#[derive(Clone, Copy, Default, Hash, Debug, Eq, PartialEq, Ord, PartialOrd)]
147pub enum EmType {
148 #[default]
149 Emphasis,
150 Deemphasis,
151}
152
153#[derive(Clone, Default, Debug, Eq, PartialEq)]
155pub struct List {
156 pub ltype: ListType,
157 pub items: Vec<Paragraph>,
158 pub tags: Tags,
159 pub props: Props,
160}
161
162#[derive(Clone, Copy, Default, Hash, Debug, Eq, PartialEq, Ord, PartialOrd)]
163pub enum ListType {
164 Distinct,
166 #[default] Identical,
168 Checked,
170}
171
172pub type Nav = Vec<SNav>;
174
175#[derive(Clone, Default, Debug, Eq, PartialEq)]
177pub struct SNav {
178 pub description: String,
179 pub subs: Vec<SNav>,
180 pub links: Vec<Link>,
181 pub tags: Tags,
182 pub props: Props,
183}
184
185#[derive(Clone, Default, Debug, Eq, PartialEq)]
187pub struct Link {
188 pub url: String,
189 pub items: Vec<LinkItem>,
190 pub tags: Tags,
191 pub props: Props,
192}
193
194#[derive(Clone, Debug, Eq, PartialEq)]
196pub enum LinkItem {
197 String(String),
198 Em(Emphasis),
199}
200
201#[derive(Clone, Default, Debug, Eq, PartialEq)]
203pub struct CodeBlock {
204 pub language: String,
206 pub mode: CodeModeHint,
208 pub code: String,
210 pub tags: Tags,
211 pub props: Props,
212}
213
214#[derive(Clone, Copy, Default, Hash, Debug, Eq, PartialEq, Ord, PartialOrd)]
216pub enum CodeModeHint {
217 #[default] Show,
219 Runnable,
221 Run,
223 Replace,
225}
226
227#[derive(Clone, Default, Debug, Eq, PartialEq)]
229pub struct TextWithMeta {
230 pub text: String,
231 pub tags: Tags,
232 pub props: Props,
233}
234
235impl TextWithMeta {
236 fn meta_is_empty(&self) -> bool {
237 self.tags.is_empty() && self.props.is_empty()
238 }
239}
240
241#[derive(Clone, Copy, Default, Hash, Debug, Eq, PartialEq, Ord, PartialOrd)]
243pub struct CodeIdentError;
244
245#[derive(Clone, Copy, Default, Hash, Debug, Eq, PartialEq, Ord, PartialOrd)]
247pub struct Date {
248 pub year: i16,
249 pub month: u8,
250 pub day: u8,
251}
252
253#[derive(Clone, Debug, Eq, PartialEq)]
254pub enum DateError {
255 YearRange(i64),
257 MonthRange(u64),
259 DayRange(u64),
261 Parsing(ParseIntError),
263}
264
265impl Date {
266 pub fn new(y: i64, m: u64, d: u64) -> Result<Self, DateError> {
267 let year: i16 = y.try_into().map_err(|_| DateError::YearRange(y))?;
268 let month: u8 = m.try_into()
269 .map_err(|_| DateError::MonthRange(m))
270 .and_then(|m| if m == 0 { Err(DateError::MonthRange(d)) } else { Ok(m) } )?;
271 let day: u8 = d.try_into()
272 .map_err(|_| DateError::DayRange(d))
273 .and_then(|d| if d == 0 { Err(DateError::DayRange(u64::from(d))) } else { Ok(d) } )?;
274 if month > 12 { return Err(DateError::MonthRange(m)); }
275 if day > 31 { return Err(DateError::DayRange(d)); }
276 Ok(Self { year, month, day })
277 }
278}
279