cmark_writer/writer/runtime/diagnostics/
mod.rs

1//! Diagnostics support for writer subsystems.
2//!
3//! This module introduces traits and lightweight collectors used by the
4//! CommonMark/HTML writers to surface non-fatal warnings (e.g. best-effort
5//! fallbacks) without hard depending on `log`.
6
7use std::cell::RefCell;
8use std::rc::Rc;
9
10use ecow::EcoString;
11
12/// Severity of a diagnostic entry.
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14pub enum DiagnosticSeverity {
15    /// Recoverable issues that do not stop rendering but may affect fidelity.
16    Warning,
17    /// Informational notes for downstream consumers.
18    Info,
19}
20
21/// Diagnostic message emitted during rendering.
22#[derive(Debug, Clone, PartialEq, Eq)]
23pub struct Diagnostic {
24    /// Severity of the diagnostic.
25    pub severity: DiagnosticSeverity,
26    /// Human-readable message.
27    pub message: EcoString,
28}
29
30impl Diagnostic {
31    /// Convenience constructor for warnings.
32    pub fn warning<S: Into<EcoString>>(message: S) -> Self {
33        Self {
34            severity: DiagnosticSeverity::Warning,
35            message: message.into(),
36        }
37    }
38
39    /// Convenience constructor for informational diagnostics.
40    pub fn info<S: Into<EcoString>>(message: S) -> Self {
41        Self {
42            severity: DiagnosticSeverity::Info,
43            message: message.into(),
44        }
45    }
46}
47
48/// Trait used by writer subsystems to report non-fatal diagnostics.
49pub trait DiagnosticSink {
50    /// Emit a diagnostic message.
51    fn emit(&mut self, diagnostic: Diagnostic);
52}
53
54/// A no-op sink used as default when the caller does not provide a collector.
55#[derive(Debug, Default, Clone, Copy)]
56pub struct NullSink;
57
58impl DiagnosticSink for NullSink {
59    fn emit(&mut self, _: Diagnostic) {}
60}
61
62/// Shared sink that stores diagnostics in an `Rc<RefCell<Vec<Diagnostic>>>` for later inspection.
63#[derive(Debug, Clone)]
64pub struct SharedVecSink {
65    target: Rc<RefCell<Vec<Diagnostic>>>,
66}
67
68impl SharedVecSink {
69    /// Create a new shared sink backed by the supplied shared vector.
70    pub fn new(target: Rc<RefCell<Vec<Diagnostic>>>) -> Self {
71        Self { target }
72    }
73
74    /// Access the underlying shared storage.
75    pub fn target(&self) -> Rc<RefCell<Vec<Diagnostic>>> {
76        Rc::clone(&self.target)
77    }
78}
79
80impl DiagnosticSink for SharedVecSink {
81    fn emit(&mut self, diagnostic: Diagnostic) {
82        self.target.borrow_mut().push(diagnostic);
83    }
84}