1mod tests;
2pub mod parsing;
3pub mod output;
4pub mod reference_doc;
5
6use std::{
7 num::ParseIntError,
8 collections::{ HashMap, HashSet },
9};
10
11use pest_derive::Parser;
12
13#[derive(Parser)]
15#[grammar = "parse/incodoc.pest"]
16pub struct IncodocParser;
17
18pub trait Absorb {
20 type Other;
21 fn absorb(&mut self, other: Self::Other);
23}
24
25pub trait RemoveErrors {
27 fn remove_errors(&mut self);
28}
29
30#[derive(Clone, Default, Debug, Eq, PartialEq)]
32pub struct Doc {
33 pub tags: Tags,
34 pub props: Props,
35 pub items: Vec<DocItem>,
36}
37
38#[derive(Clone, Debug, Eq, PartialEq)]
40pub enum DocItem {
41 Text(String),
42 MText(TextWithMeta),
44 Emphasis(Emphasis),
45 Code(Result<CodeBlock, CodeIdentError>),
47 Link(Link),
48 Nav(Nav),
50 List(List),
51 Paragraph(Paragraph),
52 Section(Section),
53}
54
55impl RemoveErrors for Doc {
56 fn remove_errors(&mut self) {
57 self.props.remove_errors();
58 self.items.retain(|i| !matches!(i, DocItem::Code(Err(_))));
59 for item in &mut self.items {
60 item.remove_errors();
61 }
62 }
63}
64
65impl RemoveErrors for DocItem {
66 fn remove_errors(&mut self) {
67 match self {
68 DocItem::MText(mtext) => mtext.remove_errors(),
69 DocItem::Emphasis(em) => em.remove_errors(),
70 DocItem::Code(Ok(code)) => code.remove_errors(),
71 DocItem::Link(link) => link.remove_errors(),
72 DocItem::Nav(nav) => nav.remove_errors(),
73 DocItem::List(list) => list.remove_errors(),
74 DocItem::Paragraph(par) => par.remove_errors(),
75 DocItem::Section(section) => section.remove_errors(),
76 _ => {},
77 }
78 }
79}
80
81pub type Tags = HashSet<String>;
83
84impl Absorb for Tags {
85 type Other = Option<Self>;
86 fn absorb(&mut self, other: Self::Other) {
87 if let Some(o) = other {
88 for v in o {
89 self.insert(v);
90 }
91 }
92 }
93}
94
95pub type Props = HashMap<String, PropVal>;
97
98#[derive(Clone, Debug, Eq, PartialEq)]
100pub enum PropVal {
101 String(String),
102 Text(String),
103 Int(i64),
104 Date(Date),
105 Error(PropValError),
106}
107
108#[derive(Clone, Debug, Eq, PartialEq)]
110pub enum PropValError {
111 Int(ParseIntError),
112 Date(DateError),
113}
114
115impl PropVal {
116 fn is_error(&self) -> bool {
117 matches![self, PropVal::Error(_)]
118 }
119}
120
121impl Absorb for Props {
122 type Other = Self;
123 fn absorb(&mut self, other: Self::Other) {
124 for prop in other {
125 insert_prop(self, prop)
126 }
127 }
128}
129
130impl RemoveErrors for Props {
131 fn remove_errors(&mut self) {
132 self.retain(|_, v| !v.is_error());
133 }
134}
135
136fn insert_prop(props: &mut Props, (k, v): (String, PropVal)) {
137 let mut insert = true;
138 if v.is_error() {
139 if let Some(ov) = props.get(&k) {
140 if !ov.is_error() {
141 insert = false;
142 }
143 }
144 }
145 if insert {
146 props.insert(k, v);
147 }
148}
149
150#[derive(Clone, Default, Debug, Eq, PartialEq)]
152pub struct Section {
153 pub heading: Heading,
154 pub items: Vec<SectionItem>,
155 pub tags: Tags,
156 pub props: Props,
157}
158
159#[derive(Clone, Debug, Eq, PartialEq)]
161pub enum SectionItem {
162 Paragraph(Paragraph),
163 Section(Section),
164}
165
166impl RemoveErrors for Section {
167 fn remove_errors(&mut self) {
168 self.props.remove_errors();
169 for item in &mut self.items {
170 item.remove_errors();
171 }
172 }
173}
174
175impl RemoveErrors for SectionItem {
176 fn remove_errors(&mut self) {
177 match self {
178 Self::Paragraph(par) => par.remove_errors(),
179 Self::Section(section) => section.remove_errors(),
180 }
181 }
182}
183
184#[derive(Clone, Default, Debug, Eq, PartialEq)]
186pub struct Heading {
187 pub level: u8,
188 pub items: Vec<HeadingItem>,
189 pub tags: Tags,
190 pub props: Props,
191}
192
193#[derive(Clone, Debug, Eq, PartialEq)]
195pub enum HeadingItem {
196 String(String),
197 Em(Emphasis),
198}
199
200impl RemoveErrors for Heading {
201 fn remove_errors(&mut self) {
202 self.props.remove_errors();
203 for item in &mut self.items {
204 item.remove_errors();
205 }
206 }
207}
208
209impl RemoveErrors for HeadingItem {
210 fn remove_errors(&mut self) {
211 if let Self::Em(em) = self {
212 em.remove_errors();
213 }
214 }
215}
216
217#[derive(Clone, Default, Debug, Eq, PartialEq)]
219pub struct Paragraph {
220 pub items: Vec<ParagraphItem>,
221 pub tags: Tags,
222 pub props: Props,
223}
224
225#[derive(Clone, Debug, Eq, PartialEq)]
227pub enum ParagraphItem {
228 Text(String),
229 MText(TextWithMeta),
230 Em(Emphasis),
231 Code(Result<CodeBlock, CodeIdentError>),
232 Link(Link),
233 List(List),
234}
235
236impl RemoveErrors for Paragraph {
237 fn remove_errors(&mut self) {
238 self.props.remove_errors();
239 for item in &mut self.items {
240 item.remove_errors();
241 }
242 }
243}
244
245impl RemoveErrors for ParagraphItem {
246 fn remove_errors(&mut self) {
247 match self {
248 Self::MText(mtext) => mtext.remove_errors(),
249 Self::Em(em) => em.remove_errors(),
250 Self::Code(Ok(code)) => code.remove_errors(),
251 Self::Link(link) => link.remove_errors(),
252 Self::List(list) => list.remove_errors(),
253 _ => (),
254 }
255 }
256}
257
258#[derive(Clone, Default, Debug, Eq, PartialEq)]
260pub struct Emphasis {
261 pub strength: EmStrength,
262 pub etype: EmType,
263 pub text: String,
264 pub tags: Tags,
265 pub props: Props,
266}
267
268#[derive(Clone, Copy, Default, Hash, Debug, Eq, PartialEq, Ord, PartialOrd)]
270pub enum EmStrength {
271 #[default]
272 Light,
273 Medium,
274 Strong,
275}
276
277#[derive(Clone, Copy, Default, Hash, Debug, Eq, PartialEq, Ord, PartialOrd)]
279pub enum EmType {
280 #[default]
281 Emphasis,
282 Deemphasis,
283}
284
285impl RemoveErrors for Emphasis {
286 fn remove_errors(&mut self) {
287 self.props.remove_errors();
288 }
289}
290
291#[derive(Clone, Default, Debug, Eq, PartialEq)]
293pub struct List {
294 pub ltype: ListType,
295 pub items: Vec<Paragraph>,
296 pub tags: Tags,
297 pub props: Props,
298}
299
300#[derive(Clone, Copy, Default, Hash, Debug, Eq, PartialEq, Ord, PartialOrd)]
301pub enum ListType {
302 Distinct,
304 #[default] Identical,
306 Checked,
308}
309
310impl RemoveErrors for List {
311 fn remove_errors(&mut self) {
312 self.props.remove_errors();
313 for item in &mut self.items {
314 item.remove_errors();
315 }
316 }
317}
318
319pub type Nav = Vec<SNav>;
321
322#[derive(Clone, Default, Debug, Eq, PartialEq)]
324pub struct SNav {
325 pub description: String,
326 pub subs: Vec<SNav>,
327 pub links: Vec<Link>,
328 pub tags: Tags,
329 pub props: Props,
330}
331
332impl RemoveErrors for Nav {
333 fn remove_errors(&mut self) {
334 for snav in self {
335 snav.remove_errors();
336 }
337 }
338}
339
340impl RemoveErrors for SNav {
341 fn remove_errors(&mut self) {
342 self.props.remove_errors();
343 for link in &mut self.links {
344 link.remove_errors();
345 }
346 for sub in &mut self.subs {
347 sub.remove_errors();
348 }
349 }
350}
351
352#[derive(Clone, Default, Debug, Eq, PartialEq)]
354pub struct Link {
355 pub url: String,
356 pub items: Vec<LinkItem>,
357 pub tags: Tags,
358 pub props: Props,
359}
360
361impl RemoveErrors for Link {
362 fn remove_errors(&mut self) {
363 self.props.remove_errors();
364 for item in &mut self.items {
365 item.remove_errors();
366 }
367 }
368}
369
370#[derive(Clone, Debug, Eq, PartialEq)]
372pub enum LinkItem {
373 String(String),
374 Em(Emphasis),
375}
376
377impl RemoveErrors for LinkItem {
378 fn remove_errors(&mut self) {
379 if let Self::Em(em) = self {
380 em.remove_errors();
381 }
382 }
383}
384
385#[derive(Clone, Default, Debug, Eq, PartialEq)]
387pub struct CodeBlock {
388 pub language: String,
390 pub mode: CodeModeHint,
392 pub code: String,
394 pub tags: Tags,
395 pub props: Props,
396}
397
398impl RemoveErrors for CodeBlock {
399 fn remove_errors(&mut self) {
400 self.props.remove_errors();
401 }
402}
403
404#[derive(Clone, Copy, Default, Hash, Debug, Eq, PartialEq, Ord, PartialOrd)]
406pub enum CodeModeHint {
407 #[default] Show,
409 Runnable,
411 Run,
413 Replace,
415}
416
417#[derive(Clone, Default, Debug, Eq, PartialEq)]
419pub struct TextWithMeta {
420 pub text: String,
421 pub tags: Tags,
422 pub props: Props,
423}
424
425impl TextWithMeta {
426 fn meta_is_empty(&self) -> bool {
427 self.tags.is_empty() && self.props.is_empty()
428 }
429}
430
431impl RemoveErrors for TextWithMeta {
432 fn remove_errors(&mut self) {
433 self.props.remove_errors();
434 }
435}
436
437#[derive(Clone, Copy, Default, Hash, Debug, Eq, PartialEq, Ord, PartialOrd)]
439pub struct CodeIdentError;
440
441#[derive(Clone, Copy, Default, Hash, Debug, Eq, PartialEq, Ord, PartialOrd)]
443pub struct Date {
444 pub year: i16,
445 pub month: u8,
446 pub day: u8,
447}
448
449#[derive(Clone, Debug, Eq, PartialEq)]
450pub enum DateError {
451 YearRange(i64),
453 MonthRange(u64),
455 DayRange(u64),
457 Parsing(ParseIntError),
459}
460
461impl Date {
462 pub fn new(y: i64, m: u64, d: u64) -> Result<Self, DateError> {
463 let year: i16 = y.try_into().map_err(|_| DateError::YearRange(y))?;
464 let month: u8 = m.try_into()
465 .map_err(|_| DateError::MonthRange(m))
466 .and_then(|m| if m == 0 { Err(DateError::MonthRange(m as u64)) } else { Ok(m) } )?;
467 let day: u8 = d.try_into()
468 .map_err(|_| DateError::DayRange(d))
469 .and_then(|d| if d == 0 { Err(DateError::DayRange(d as u64)) } else { Ok(d) } )?;
470 if month > 12 { return Err(DateError::MonthRange(m)); }
471 if day > 31 { return Err(DateError::DayRange(d)); }
472 Ok(Self { year, month, day })
473 }
474}
475