use std::collections::BTreeMap;
use std::error::Error as StdError;
use std::fmt;
use std::sync::Arc;
use sim_kernel::CodecId;
use crate::asciidoc::AsciiDocBackend;
use crate::latex::LatexBackend;
use crate::markdown::MarkdownBackend;
use crate::markup::{BackendId, MarkupDoc};
use crate::typst_backend::TypstBackend;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct MarkupDecodeOptions {
pub preserve_source: bool,
pub preserve_raw: bool,
}
impl Default for MarkupDecodeOptions {
fn default() -> Self {
Self {
preserve_source: true,
preserve_raw: true,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct MarkupEncodeOptions {
pub fail_on_loss: bool,
pub preserve_raw: bool,
}
impl Default for MarkupEncodeOptions {
fn default() -> Self {
Self {
fail_on_loss: true,
preserve_raw: true,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct MarkupLoss {
pub path: String,
pub reason: String,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct MarkupFidelity {
pub backend: BackendId,
pub preserved_raw: Vec<String>,
pub dropped: Vec<MarkupLoss>,
pub warnings: Vec<String>,
}
impl MarkupFidelity {
pub fn exact(backend: BackendId) -> Self {
Self {
backend,
preserved_raw: Vec::new(),
dropped: Vec::new(),
warnings: Vec::new(),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum MarkupError {
UnknownBackend(BackendId),
Decode(String),
Encode(String),
InvalidDocument(String),
}
impl MarkupError {
pub(crate) fn into_kernel_error(self, codec: CodecId) -> sim_kernel::Error {
sim_kernel::Error::CodecError {
codec,
message: self.to_string(),
}
}
}
impl fmt::Display for MarkupError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::UnknownBackend(id) => write!(f, "unknown markup backend {id}"),
Self::Decode(message) => write!(f, "markup decode failed: {message}"),
Self::Encode(message) => write!(f, "markup encode failed: {message}"),
Self::InvalidDocument(message) => write!(f, "invalid markup document: {message}"),
}
}
}
impl StdError for MarkupError {}
pub trait MarkupBackend: Send + Sync {
fn id(&self) -> BackendId;
fn decode(
&self,
input: &str,
opts: &MarkupDecodeOptions,
) -> Result<(MarkupDoc, MarkupFidelity), MarkupError>;
fn encode(
&self,
doc: &MarkupDoc,
opts: &MarkupEncodeOptions,
) -> Result<(String, MarkupFidelity), MarkupError>;
}
#[derive(Clone, Default)]
pub struct BackendRegistry {
backends: BTreeMap<BackendId, Arc<dyn MarkupBackend>>,
}
impl BackendRegistry {
pub fn new() -> Self {
Self {
backends: BTreeMap::new(),
}
}
pub fn register<B: MarkupBackend + 'static>(
&mut self,
backend: B,
) -> Option<Arc<dyn MarkupBackend>> {
self.register_arc(Arc::new(backend))
}
pub fn register_arc(
&mut self,
backend: Arc<dyn MarkupBackend>,
) -> Option<Arc<dyn MarkupBackend>> {
self.backends.insert(backend.id(), backend)
}
pub fn backend(&self, id: &BackendId) -> Result<Arc<dyn MarkupBackend>, MarkupError> {
self.backends
.get(id)
.cloned()
.ok_or_else(|| MarkupError::UnknownBackend(id.clone()))
}
pub fn ids(&self) -> Vec<BackendId> {
self.backends.keys().cloned().collect()
}
pub fn iter(&self) -> impl Iterator<Item = (&BackendId, &Arc<dyn MarkupBackend>)> {
self.backends.iter()
}
pub fn is_empty(&self) -> bool {
self.backends.is_empty()
}
}
#[derive(Clone, Debug, Default)]
pub struct BasicMarkdownBackend;
impl MarkupBackend for BasicMarkdownBackend {
fn id(&self) -> BackendId {
BackendId::new("markdown")
}
fn decode(
&self,
input: &str,
opts: &MarkupDecodeOptions,
) -> Result<(MarkupDoc, MarkupFidelity), MarkupError> {
MarkdownBackend.decode(input, opts)
}
fn encode(
&self,
doc: &MarkupDoc,
opts: &MarkupEncodeOptions,
) -> Result<(String, MarkupFidelity), MarkupError> {
MarkdownBackend.encode(doc, opts)
}
}
pub fn default_backend_registry() -> BackendRegistry {
let mut registry = BackendRegistry::new();
registry.register(AsciiDocBackend);
registry.register(LatexBackend);
registry.register(MarkdownBackend);
registry.register(TypstBackend);
registry
}