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 Table(Table),
125}
126
127#[derive(Clone, Default, Debug, Eq, PartialEq)]
129pub struct Emphasis {
130 pub strength: EmStrength,
131 pub etype: EmType,
132 pub text: String,
133 pub tags: Tags,
134 pub props: Props,
135}
136
137#[derive(Clone, Copy, Default, Hash, Debug, Eq, PartialEq, Ord, PartialOrd)]
139pub enum EmStrength {
140 #[default]
141 Light,
142 Medium,
143 Strong,
144}
145
146#[derive(Clone, Copy, Default, Hash, Debug, Eq, PartialEq, Ord, PartialOrd)]
148pub enum EmType {
149 #[default]
150 Emphasis,
151 Deemphasis,
152}
153
154#[derive(Clone, Default, Debug, Eq, PartialEq)]
156pub struct List {
157 pub ltype: ListType,
158 pub items: Vec<Paragraph>,
159 pub tags: Tags,
160 pub props: Props,
161}
162
163#[derive(Clone, Copy, Default, Hash, Debug, Eq, PartialEq, Ord, PartialOrd)]
164pub enum ListType {
165 Distinct,
167 #[default] Identical,
169 Checked,
171}
172
173#[derive(Clone, Default, Debug, Eq, PartialEq)]
175pub struct Nav {
176 pub description: String,
177 pub subs: Vec<Nav>,
178 pub links: Vec<Link>,
179 pub tags: Tags,
180 pub props: Props,
181}
182
183#[derive(Clone, Default, Debug, Eq, PartialEq)]
185pub struct Link {
186 pub url: String,
187 pub items: Vec<LinkItem>,
188 pub tags: Tags,
189 pub props: Props,
190}
191
192#[derive(Clone, Debug, Eq, PartialEq)]
194pub enum LinkItem {
195 String(String),
196 Em(Emphasis),
197}
198
199#[derive(Clone, Default, Debug, Eq, PartialEq)]
201pub struct CodeBlock {
202 pub language: String,
204 pub mode: CodeModeHint,
206 pub code: String,
208 pub tags: Tags,
209 pub props: Props,
210}
211
212#[derive(Clone, Copy, Default, Hash, Debug, Eq, PartialEq, Ord, PartialOrd)]
214pub enum CodeModeHint {
215 #[default] Show,
217 Runnable,
219 Run,
221 Replace,
223}
224
225#[derive(Clone, Default, Debug, Eq, PartialEq)]
227pub struct Table {
228 pub rows: Vec<TableRow>,
229 pub tags: Tags,
230 pub props: Props,
231}
232
233#[derive(Clone, Default, Debug, Eq, PartialEq)]
235pub struct TableRow {
236 pub items: Vec<Paragraph>,
237 pub is_header: bool,
238 pub tags: Tags,
239 pub props: Props,
240}
241
242#[derive(Clone, Default, Debug, Eq, PartialEq)]
244pub struct TextWithMeta {
245 pub text: String,
246 pub tags: Tags,
247 pub props: Props,
248}
249
250impl TextWithMeta {
251 fn meta_is_empty(&self) -> bool {
252 self.tags.is_empty() && self.props.is_empty()
253 }
254}
255
256#[derive(Clone, Copy, Default, Hash, Debug, Eq, PartialEq, Ord, PartialOrd)]
258pub struct CodeIdentError;
259
260#[derive(Clone, Copy, Default, Hash, Debug, Eq, PartialEq, Ord, PartialOrd)]
262pub struct Date {
263 pub year: i16,
264 pub month: u8,
265 pub day: u8,
266}
267
268#[derive(Clone, Debug, Eq, PartialEq)]
269pub enum DateError {
270 YearRange(i64),
272 MonthRange(u64),
274 DayRange(u64),
276 Parsing(ParseIntError),
278}
279
280impl Date {
281 pub fn new(y: i64, m: u64, d: u64) -> Result<Self, DateError> {
282 let year: i16 = y.try_into().map_err(|_| DateError::YearRange(y))?;
283 let month: u8 = m.try_into()
284 .map_err(|_| DateError::MonthRange(m))
285 .and_then(|m| if m == 0 { Err(DateError::MonthRange(d)) } else { Ok(m) } )?;
286 let day: u8 = d.try_into()
287 .map_err(|_| DateError::DayRange(d))
288 .and_then(|d| if d == 0 { Err(DateError::DayRange(u64::from(d))) } else { Ok(d) } )?;
289 if month > 12 { return Err(DateError::MonthRange(m)); }
290 if day > 31 { return Err(DateError::DayRange(d)); }
291 Ok(Self { year, month, day })
292 }
293}
294