1use crate::{CodeFence, LineEnding};
6use alloc::{borrow::Cow, string::String};
7use core::{fmt, ops::Range};
8
9#[derive(Clone, Debug, PartialEq, Eq)]
11pub enum Text<'a> {
12 Source(Range<usize>),
14 Literal(&'static str),
16 Value(Cow<'a, str>),
18}
19
20#[cfg(feature = "serde")]
21impl serde::Serialize for Text<'_> {
22 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
23 match self {
24 Self::Source(range) => serializer.serialize_newtype_variant("Text", 0, "Source", range),
25 Self::Literal(value) => serializer.serialize_newtype_variant("Text", 1, "Value", value),
26 Self::Value(value) => serializer.serialize_newtype_variant("Text", 1, "Value", value),
27 }
28 }
29}
30#[cfg(feature = "serde")]
31impl<'de> serde::Deserialize<'de> for Text<'_> {
32 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
33 #[derive(serde::Deserialize)]
34 enum Stored {
35 Source(Range<usize>),
36 Value(String),
37 Literal(String),
38 }
39 Ok(match Stored::deserialize(deserializer)? {
40 Stored::Source(range) => Self::Source(range),
41 Stored::Value(value) | Stored::Literal(value) => Self::Value(Cow::Owned(value)),
42 })
43 }
44}
45
46impl Text<'_> {
47 pub fn resolve<'a>(&'a self, source: &'a str) -> Result<&'a str, StreamError> {
52 match self {
53 Self::Source(range) => source.get(range.clone()).ok_or(StreamError::InvalidRange),
54 Self::Literal(value) => Ok(value),
55 Self::Value(value) => Ok(value),
56 }
57 }
58
59 pub(crate) fn borrowed(&self) -> Text<'_> {
60 match self {
61 Self::Source(range) => Text::Source(range.clone()),
62 Self::Literal(value) => Text::Literal(value),
63 Self::Value(value) => Text::Value(Cow::Borrowed(value)),
64 }
65 }
66
67 pub(crate) fn to_mut(&mut self, source: &str) -> Result<&mut String, StreamError> {
68 if !matches!(self, Self::Value(_)) {
69 *self = Self::Value(Cow::Owned(self.resolve(source)?.into()));
70 }
71 match self {
72 Self::Value(value) => Ok(value.to_mut()),
73 _ => unreachable!("source and literal text were materialized above"),
74 }
75 }
76
77 pub(crate) fn materialized(self, source: &str) -> Text<'static> {
78 match self {
79 Self::Source(range) => Text::from(String::from(
80 source.get(range).expect("document ranges are validated"),
81 )),
82 Self::Literal(value) => Text::Literal(value),
83 Self::Value(value) => Text::Value(Cow::Owned(value.into_owned())),
84 }
85 }
86
87 pub(crate) fn owned(self) -> Text<'static> {
88 match self {
89 Self::Source(range) => Text::Source(range),
90 Self::Literal(value) => Text::Literal(value),
91 Self::Value(value) => Text::Value(Cow::Owned(value.into_owned())),
92 }
93 }
94}
95
96impl From<String> for Text<'static> {
97 fn from(value: String) -> Self {
98 Self::Value(Cow::Owned(value))
99 }
100}
101
102#[derive(Clone, Copy, Debug, PartialEq, Eq)]
104#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
105pub enum TagKind {
106 Document,
108 Paragraph,
110 Heading,
112 BlockQuote,
114 List,
116 Item,
118 Emphasis,
120 Strong,
122 Delete,
124 Link,
126 Image,
128 Table,
130 TableHead,
132 TableBody,
134 TableRow,
136 TableCell,
138 FootnoteDefinition,
140}
141
142impl TagKind {
143 pub(crate) fn allows_descendant(self, child: Self) -> bool {
146 !matches!(self, Self::Heading | Self::Image)
147 || !matches!(child, Self::Heading | Self::FootnoteDefinition)
148 }
149}
150
151#[derive(Clone, Debug, PartialEq, Eq)]
153#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
154pub enum Tag<'a> {
155 Document(Option<LineEnding>),
157 Paragraph { tight: bool },
159 Heading(u8),
161 BlockQuote,
163 List { start: Option<u32>, tight: bool },
165 Item { block_end: bool },
167 Emphasis,
169 Strong,
171 Delete,
173 Link {
175 destination: Text<'a>,
176 title: Option<Text<'a>>,
177 },
178 Image {
180 destination: Text<'a>,
181 title: Option<Text<'a>>,
182 },
183 Table,
185 TableHead,
187 TableBody,
189 TableRow,
191 TableCell {
193 alignment: crate::AlignKind,
194 header: bool,
195 },
196 FootnoteDefinition(Text<'a>),
198}
199
200impl<'s> Tag<'s> {
201 pub fn kind(&self) -> TagKind {
203 match self {
204 Self::Document(_) => TagKind::Document,
205 Self::Paragraph { .. } => TagKind::Paragraph,
206 Self::Heading(_) => TagKind::Heading,
207 Self::BlockQuote => TagKind::BlockQuote,
208 Self::List { .. } => TagKind::List,
209 Self::Item { .. } => TagKind::Item,
210 Self::Emphasis => TagKind::Emphasis,
211 Self::Strong => TagKind::Strong,
212 Self::Delete => TagKind::Delete,
213 Self::Link { .. } => TagKind::Link,
214 Self::Image { .. } => TagKind::Image,
215 Self::Table => TagKind::Table,
216 Self::TableHead => TagKind::TableHead,
217 Self::TableBody => TagKind::TableBody,
218 Self::TableRow => TagKind::TableRow,
219 Self::TableCell { .. } => TagKind::TableCell,
220 Self::FootnoteDefinition(_) => TagKind::FootnoteDefinition,
221 }
222 }
223
224 pub(crate) fn map_text<'a>(self, mut map: impl FnMut(Text<'s>) -> Text<'a>) -> Tag<'a> {
225 match self {
226 Self::Document(value) => Tag::Document(value),
227 Self::Paragraph { tight } => Tag::Paragraph { tight },
228 Self::Heading(value) => Tag::Heading(value),
229 Self::BlockQuote => Tag::BlockQuote,
230 Self::List { start, tight } => Tag::List { start, tight },
231 Self::Item { block_end } => Tag::Item { block_end },
232 Self::Emphasis => Tag::Emphasis,
233 Self::Strong => Tag::Strong,
234 Self::Delete => Tag::Delete,
235 Self::Link { destination, title } => Tag::Link {
236 destination: map(destination),
237 title: title.map(&mut map),
238 },
239 Self::Image { destination, title } => Tag::Image {
240 destination: map(destination),
241 title: title.map(&mut map),
242 },
243 Self::Table => Tag::Table,
244 Self::TableHead => Tag::TableHead,
245 Self::TableBody => Tag::TableBody,
246 Self::TableRow => Tag::TableRow,
247 Self::TableCell { alignment, header } => Tag::TableCell { alignment, header },
248 Self::FootnoteDefinition(value) => Tag::FootnoteDefinition(map(value)),
249 }
250 }
251}
252
253#[derive(Clone, Debug, PartialEq, Eq)]
255#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
256pub enum EventKind<'a> {
257 Start(Tag<'a>),
259 End(TagKind),
261 Text(Text<'a>),
263 SoftBreak(Text<'a>),
265 Separator { value: Text<'a>, conditional: bool },
267 HardBreak,
269 Rule,
271 Code {
273 value: Text<'a>,
274 language: Option<Text<'a>>,
275 },
276 CodeBlock {
278 value: Text<'a>,
279 language: Option<Text<'a>>,
280 fence: Option<CodeFence>,
281 math: bool,
282 unclosed: bool,
283 },
284 Math(Text<'a>),
286 Html { value: Text<'a>, block: bool },
288 TaskMarker(bool),
290 Definition {
292 label: Text<'a>,
293 destination: Text<'a>,
294 title: Option<Text<'a>>,
295 },
296 Opaque { value: Text<'a>, block: bool },
298 FootnoteReference(Text<'a>),
300}
301
302impl<'s> EventKind<'s> {
303 pub(crate) fn validate(&self, source: Option<&str>) -> Result<(), StreamError> {
304 let text = |value: &Text<'_>| {
305 if let Some(source) = source {
306 value.resolve(source)?;
307 }
308 Ok(())
309 };
310 let optional = |value: &Option<Text<'_>>| {
311 if let Some(value) = value {
312 text(value)?;
313 }
314 Ok(())
315 };
316 match self {
317 Self::Start(Tag::Heading(level)) if !(1..=6).contains(level) => {
318 Err(StreamError::Unbalanced)
319 }
320 Self::Start(Tag::Link { destination, title } | Tag::Image { destination, title }) => {
321 text(destination)?;
322 optional(title)
323 }
324 Self::Start(Tag::FootnoteDefinition(value))
325 | Self::Text(value)
326 | Self::SoftBreak(value)
327 | Self::Math(value)
328 | Self::Separator { value, .. }
329 | Self::Html { value, .. }
330 | Self::Opaque { value, .. }
331 | Self::FootnoteReference(value) => text(value),
332 Self::Code { value, language }
333 | Self::CodeBlock {
334 value, language, ..
335 } => {
336 text(value)?;
337 optional(language)
338 }
339 Self::Definition {
340 label,
341 destination,
342 title,
343 } => {
344 text(label)?;
345 text(destination)?;
346 optional(title)
347 }
348 _ => Ok(()),
349 }
350 }
351
352 pub(crate) fn map_text<'a>(self, mut map: impl FnMut(Text<'s>) -> Text<'a>) -> EventKind<'a> {
353 match self {
354 Self::Start(tag) => EventKind::Start(tag.map_text(map)),
355 Self::End(kind) => EventKind::End(kind),
356 Self::Text(text) => EventKind::Text(map(text)),
357 Self::SoftBreak(text) => EventKind::SoftBreak(map(text)),
358 Self::Separator { value, conditional } => EventKind::Separator {
359 value: map(value),
360 conditional,
361 },
362 Self::HardBreak => EventKind::HardBreak,
363 Self::Rule => EventKind::Rule,
364 Self::Code { value, language } => EventKind::Code {
365 value: map(value),
366 language: language.map(map),
367 },
368 Self::CodeBlock {
369 value,
370 language,
371 fence,
372 math,
373 unclosed,
374 } => EventKind::CodeBlock {
375 value: map(value),
376 language: language.map(map),
377 fence,
378 math,
379 unclosed,
380 },
381 Self::Math(value) => EventKind::Math(map(value)),
382 Self::Html { value, block } => EventKind::Html {
383 value: map(value),
384 block,
385 },
386 Self::TaskMarker(value) => EventKind::TaskMarker(value),
387 Self::Definition {
388 label,
389 destination,
390 title,
391 } => EventKind::Definition {
392 label: map(label),
393 destination: map(destination),
394 title: title.map(map),
395 },
396 Self::Opaque { value, block } => EventKind::Opaque {
397 value: map(value),
398 block,
399 },
400 Self::FootnoteReference(value) => EventKind::FootnoteReference(map(value)),
401 }
402 }
403}
404
405#[derive(Clone, Debug, PartialEq, Eq)]
407#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
408pub struct Event<'a> {
409 pub kind: EventKind<'a>,
411 pub span: Range<usize>,
413}
414
415impl Event<'_> {
416 pub(crate) fn validate(&self, source: Option<&str>) -> Result<(), StreamError> {
417 if self.span.start > self.span.end
418 || (matches!(self.kind, EventKind::End(_)) && !self.span.is_empty())
419 || source.is_some_and(|source| source.get(self.span.clone()).is_none())
420 {
421 return Err(StreamError::InvalidRange);
422 }
423 self.kind.validate(source)
424 }
425}
426
427#[derive(Debug)]
429pub enum StreamError {
430 Parse(crate::message::Message),
432 InvalidRange,
434 Unbalanced,
436 Write(fmt::Error),
438}
439
440impl fmt::Display for StreamError {
441 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
442 match self {
443 Self::Parse(error) => write!(f, "{error}"),
444 Self::InvalidRange => f.write_str("invalid source range"),
445 Self::Unbalanced => f.write_str("unbalanced semantic event stream"),
446 Self::Write(error) => write!(f, "{error}"),
447 }
448 }
449}
450
451impl From<fmt::Error> for StreamError {
452 fn from(error: fmt::Error) -> Self {
453 Self::Write(error)
454 }
455}
456
457impl From<crate::message::Message> for StreamError {
458 fn from(error: crate::message::Message) -> Self {
459 Self::Parse(error)
460 }
461}