use std::io;
use carta_ast::{Block, Document, Inline};
pub mod extensions;
pub mod sections;
#[cfg(feature = "template")]
pub mod template;
pub use extensions::{Extension, Extensions, presets};
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("JSON error: {0}")]
Json(#[from] serde_json::Error),
#[error("I/O error: {0}")]
Io(#[from] io::Error),
#[error("input is not valid UTF-8: {0}")]
InvalidUtf8(#[from] std::string::FromUtf8Error),
#[error("unsupported format: {0}")]
UnsupportedFormat(String),
#[error("format '{0}' is recognized but not enabled in this build")]
FormatNotEnabled(String),
#[error("unknown extension: {0}")]
UnknownExtension(String),
#[error(
"The extension '{extension}' is not supported for {format}.\nUse --list-extensions={format} to list supported extensions."
)]
UnsupportedExtension { extension: String, format: String },
#[error("invalid document metadata: {0}")]
InvalidMetadata(String),
#[error("template error: {0}")]
Template(String),
#[error("cannot represent this content in the target format: {0}")]
Unrepresentable(String),
}
#[cfg(feature = "template")]
impl From<template::TemplateError> for Error {
fn from(error: template::TemplateError) -> Self {
Error::Template(error.to_string())
}
}
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug, Clone, Default)]
#[non_exhaustive]
pub struct ReaderOptions {
pub extensions: Extensions,
pub greedy_paragraphs: bool,
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub enum MathMethod {
#[default]
Plain,
MathJax(String),
Katex(String),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum TocStyle {
#[default]
List,
Native,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum WrapMode {
#[default]
Auto,
None,
Preserve,
}
#[derive(Debug, Clone, Default)]
#[non_exhaustive]
pub struct WriterOptions {
pub extensions: Extensions,
pub wrap: WrapMode,
pub columns: Option<usize>,
pub number_sections: bool,
pub toc: bool,
pub toc_depth: Option<usize>,
pub math_method: MathMethod,
#[cfg(feature = "template")]
pub standalone: bool,
#[cfg(feature = "template")]
pub template: Option<String>,
#[cfg(feature = "template")]
pub template_dir: Option<std::path::PathBuf>,
#[cfg(feature = "template")]
pub template_ext: Option<String>,
#[cfg(feature = "template")]
pub variables: Vec<(String, String)>,
#[cfg(feature = "template")]
pub metadata: std::collections::BTreeMap<String, carta_ast::MetaValue>,
#[cfg(feature = "template")]
pub metadata_defaults: std::collections::BTreeMap<String, carta_ast::MetaValue>,
#[cfg(feature = "template")]
pub source_name: Option<String>,
}
pub trait Reader {
fn read(&self, input: &str, options: &ReaderOptions) -> Result<Document>;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum MetaVarStyle {
#[default]
None,
Web,
Pdf,
}
pub trait Writer {
fn write(&self, document: &Document, options: &WriterOptions) -> Result<String>;
fn render_meta_inlines(&self, inlines: &[Inline], options: &WriterOptions) -> Result<String> {
let document = Document {
blocks: vec![Block::Plain(inlines.to_vec())],
..Document::default()
};
Ok(self
.write(&document, options)?
.trim_end_matches('\n')
.to_string())
}
fn render_meta_blocks(&self, blocks: &[Block], options: &WriterOptions) -> Result<String> {
let document = Document {
blocks: blocks.to_vec(),
..Document::default()
};
Ok(self
.write(&document, options)?
.trim_end_matches('\n')
.to_string())
}
fn default_template(&self) -> Option<&'static str> {
None
}
fn standalone_document(
&self,
document: &Document,
options: &WriterOptions,
) -> Result<Option<String>> {
let _ = (document, options);
Ok(None)
}
fn meta_var_style(&self) -> MetaVarStyle {
MetaVarStyle::None
}
fn flatten_block_metadata(&self) -> bool {
false
}
fn title_block(&self, document: &Document, options: &WriterOptions) -> Result<Option<String>> {
let _ = (document, options);
Ok(None)
}
fn body_ends_with_newline(&self) -> bool {
false
}
fn toc_style(&self) -> TocStyle {
TocStyle::List
}
fn toc_link_anchors(&self) -> bool {
true
}
fn numbers_sections_natively(&self) -> bool {
false
}
fn numbers_sections_in_body(&self) -> bool {
false
}
}