readme_sync/
cmark_item.rs

1use core::ops::Range;
2use std::borrow::Cow;
3use std::boxed::Box;
4use std::string::String;
5use std::sync::Arc;
6use std::vec::Vec;
7
8use pulldown_cmark::Event;
9
10use crate::TextSource;
11
12/// An enum that store Markdown event, its origin, and modifications.
13/// The event modification tree is stored to improve error messages.
14#[derive(Clone, Debug, PartialEq)]
15pub enum CMarkItem {
16    /// A Markdown event parsed from the specified text source.
17    Parsed {
18        /// Parsed Markdown event.
19        event: Event<'static>,
20        /// The corresponding range of the event in the source text.
21        range: Range<usize>,
22        /// Text source.
23        text_source: TextSource,
24    },
25    /// A Markdown event created by data transform functions.
26    Created {
27        /// Created Markdown event.
28        event: Event<'static>,
29        /// Data transformation note.
30        note: Cow<'static, str>,
31    },
32    /// A Markdown event created from other Markdown events.
33    Modified {
34        /// A Markdown event created from other events.
35        event: Event<'static>,
36        /// Source `CMarkItem`s.
37        nodes: Box<[Arc<CMarkItem>]>,
38        /// Data transformation note.
39        note: Cow<'static, str>,
40    },
41    /// A removed Markdown events.
42    Removed {
43        /// Removed `CMarkItem`s.
44        nodes: Box<[Arc<CMarkItem>]>,
45        /// Data transformation note.
46        note: Cow<'static, str>,
47    },
48    /// A non-modified Markdown event that noted by data transform function.
49    Noted {
50        /// Noted `CMarkItem`.
51        node: Arc<CMarkItem>,
52        /// Data transformation note.
53        note: Cow<'static, str>,
54    },
55}
56
57/// Event's region of source text.
58#[allow(single_use_lifetimes)] // false positive in PartialEq, issue: rust-lang/rust/#69952
59#[derive(Clone, Debug, PartialEq)]
60pub struct CMarkSpan<'a> {
61    /// The corresponding range of the original event in the source text.
62    pub range: &'a Range<usize>,
63    /// Text source of original parsed event.
64    pub text_source: &'a TextSource,
65    /// Cumulative data transformation note.
66    pub note: String,
67}
68
69impl CMarkItem {
70    /// Creates `CMarkItem` with the specified Event and its note.
71    pub fn new(event: Event<'static>, note: Cow<'static, str>) -> Arc<Self> {
72        Arc::new(Self::Created { event, note })
73    }
74
75    /// Creates `CMarkItem` as an event parsed from the specified text source range.
76    pub fn from(event: Event<'static>, range: Range<usize>, text_source: TextSource) -> Arc<Self> {
77        Arc::new(Self::Parsed {
78            event,
79            range,
80            text_source,
81        })
82    }
83
84    /// Returns the event or None if it is removed.
85    pub fn event(&self) -> Option<&Event<'static>> {
86        match self {
87            Self::Parsed { event, .. } => Some(event),
88            Self::Created { event, .. } => Some(event),
89            Self::Modified { event, .. } => Some(event),
90            Self::Removed { .. } => None,
91            Self::Noted { node, .. } => node.event(),
92        }
93    }
94
95    /// Returns a `Vec` of event spans.
96    pub fn spans(&self) -> Vec<CMarkSpan<'_>> {
97        match self {
98            Self::Parsed {
99                event,
100                range,
101                text_source,
102            } => std::vec![CMarkSpan {
103                range,
104                text_source,
105                note: std::format!("{:?}", event),
106            }],
107            Self::Created { .. } => Vec::new(),
108            Self::Modified { event, nodes, note } => nodes
109                .iter()
110                .flat_map(|node| node.spans())
111                .map(|span| CMarkSpan {
112                    note: span.note + " : " + note + " -> " + &std::format!("{:?}", event),
113                    ..span
114                })
115                .collect(),
116            Self::Removed { nodes, note } => nodes
117                .iter()
118                .flat_map(|node| node.spans())
119                .map(|span| CMarkSpan {
120                    note: span.note + " : " + note,
121                    ..span
122                })
123                .collect(),
124            Self::Noted { node, note } => node
125                .spans()
126                .into_iter()
127                .map(|span| CMarkSpan {
128                    note: span.note + " : " + note,
129                    ..span
130                })
131                .collect(),
132        }
133    }
134}
135
136/// A helper trait to create an event as modified from other events.
137pub trait CMarkItemAsModified {
138    /// Сreate an event as modified from other events.
139    fn into_modified(self, event: Event<'static>, note: Cow<'static, str>) -> Arc<CMarkItem>;
140}
141
142/// A helper trait to mark events as removed.
143pub trait CMarkItemAsRemoved {
144    /// Mark events as removed.
145    fn into_removed(self, note: Cow<'static, str>) -> Arc<CMarkItem>;
146}
147
148/// A helper trait to mark an event with the specified note.
149pub trait CMarkItemWithNote {
150    /// Mark an event with the specified note.
151    fn with_note(self, note: Cow<'static, str>) -> Arc<CMarkItem>;
152}
153
154impl CMarkItemAsModified for Arc<CMarkItem> {
155    fn into_modified(self, event: Event<'static>, note: Cow<'static, str>) -> Arc<CMarkItem> {
156        Arc::new(CMarkItem::Modified {
157            nodes: Box::new([self]),
158            event,
159            note,
160        })
161    }
162}
163
164impl CMarkItemAsModified for Box<[Arc<CMarkItem>]> {
165    fn into_modified(self, event: Event<'static>, note: Cow<'static, str>) -> Arc<CMarkItem> {
166        assert!(!self.is_empty());
167        Arc::new(CMarkItem::Modified {
168            nodes: self,
169            event,
170            note,
171        })
172    }
173}
174
175impl CMarkItemAsModified for Vec<Arc<CMarkItem>> {
176    fn into_modified(self, event: Event<'static>, note: Cow<'static, str>) -> Arc<CMarkItem> {
177        assert!(!self.is_empty());
178        Arc::new(CMarkItem::Modified {
179            nodes: self.into_boxed_slice(),
180            event,
181            note,
182        })
183    }
184}
185
186impl CMarkItemAsRemoved for Arc<CMarkItem> {
187    fn into_removed(self, note: Cow<'static, str>) -> Arc<CMarkItem> {
188        Arc::new(CMarkItem::Removed {
189            nodes: Box::new([self]),
190            note,
191        })
192    }
193}
194
195impl CMarkItemAsRemoved for Box<[Arc<CMarkItem>]> {
196    fn into_removed(self, note: Cow<'static, str>) -> Arc<CMarkItem> {
197        assert!(!self.is_empty());
198        Arc::new(CMarkItem::Removed { nodes: self, note })
199    }
200}
201
202impl CMarkItemAsRemoved for Vec<Arc<CMarkItem>> {
203    fn into_removed(self, note: Cow<'static, str>) -> Arc<CMarkItem> {
204        assert!(!self.is_empty());
205        Arc::new(CMarkItem::Removed {
206            nodes: self.into_boxed_slice(),
207            note,
208        })
209    }
210}
211
212impl CMarkItemWithNote for Arc<CMarkItem> {
213    fn with_note(self, note: Cow<'static, str>) -> Arc<CMarkItem> {
214        Arc::new(CMarkItem::Noted { node: self, note })
215    }
216}