1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
//! This library exists to convert [MinTyML](https://youngspe.github.io/mintyml)
//! (for <u>Min</u>imalist H<u>TML</u>) markup to its equivalent HTML.
//!
//! This should be considered the reference implementation for MinTyML.
#![cfg_attr(not(feature = "std"), no_std)]
extern crate alloc;
extern crate either;
extern crate gramma;
#[cfg(feature = "std")]
extern crate thiserror;
pub(crate) mod ast;
pub(crate) mod config;
pub(crate) mod document;
pub(crate) mod escape;
pub(crate) mod output;
pub(crate) mod transform;
pub(crate) mod utils;
use alloc::{string::String, vec::Vec};
use core::{borrow::Borrow, fmt};
use document::{Document, Src, ToStatic};
use output::OutputError;
pub use config::{OutputConfig, SpecialTagConfig};
pub use document::{SyntaxError, SyntaxErrorKind};
/// Represents an error that occurred while converting MinTyML.
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "std", derive(thiserror::Error))]
pub enum ConvertError<'src> {
/// The conversion failed due to one or more syntax errors.
#[cfg_attr(feature = "std", error("{}", utils::join_display(syntax_errors.iter().map(|x| x.display_with_src(src)), "; ")))]
Syntax {
syntax_errors: Vec<SyntaxError>,
src: Src<'src>,
},
/// The conversion failed for some other reason.
#[cfg_attr(feature = "std", error("Unknown"))]
Unknown,
}
impl<'src> ConvertError<'src> {
/// Copies all borrowed data so the error can outlive the source str.
pub fn to_static(self) -> ConvertError<'static> {
match self {
ConvertError::Syntax { syntax_errors, src } => ConvertError::Syntax {
syntax_errors,
src: src.to_static(),
},
Self::Unknown => ConvertError::Unknown,
}
}
}
impl<'src> ToStatic for ConvertError<'src> {
type Static = ConvertError<'static>;
fn to_static(self) -> ConvertError<'static> {
self.to_static()
}
}
impl From<OutputError> for ConvertError<'_> {
fn from(value: OutputError) -> Self {
match value {
output::OutputError::WriteError(fmt::Error) => Self::Unknown,
}
}
}
/// Converts the given MinTyML string `src` using `config` for configuration options.
/// If successful, returns a string containing the converted HTML document.
///
/// # Example
///
/// ```
/// # use mintyml::OutputConfig;
/// let out = mintyml::convert(r#"
/// {
/// Hello there,
/// world!
/// img[src="./pic.png"]>
///
/// > Click <(a.example-link[href=www.example.com]> here )>
/// for more.
/// .empty>
/// .foo#bar.baz> Goodbye
/// }
/// "#, OutputConfig::new()).unwrap();
///
/// assert_eq!(out, concat!(
/// r#"<div>"#,
/// r#"<p>Hello there, world!</p>"#,
/// r#" <img src="./pic.png">"#,
/// r#" <p>Click <a class="example-link" href="www.example.com">here</a> for more.</p>"#,
/// r#" <p class="empty"></p>"#,
/// r#" <p id="bar" class="foo baz">Goodbye</p>"#,
/// r#"</div>"#,
/// ));
/// ```
pub fn convert<'src>(
src: &'src str,
config: impl Borrow<OutputConfig<'src>>,
) -> Result<String, ConvertError<'src>> {
let mut out = String::new();
convert_to(src, config, &mut out)?;
Ok(out)
}
/// Converts the given MinTyML string `src` using `config` for configuration options.
/// The converted HTML document will be written to `out`.
///
/// # Example
///
/// ```
/// # use mintyml::OutputConfig;
/// let mut out = String::new();
/// mintyml::convert_to(r#"
/// {
/// Hello there,
/// world!
/// img[src="./pic.png"]>
///
/// > Click <(a.example-link[href=www.example.com]> here )>
/// for more.
/// .empty>
/// .foo#bar.baz> Goodbye
/// }
/// "#, OutputConfig::new(), &mut out).unwrap();
///
/// assert_eq!(out, concat!(
/// r#"<div>"#,
/// r#"<p>Hello there, world!</p>"#,
/// r#" <img src="./pic.png">"#,
/// r#" <p>Click <a class="example-link" href="www.example.com">here</a> for more.</p>"#,
/// r#" <p class="empty"></p>"#,
/// r#" <p id="bar" class="foo baz">Goodbye</p>"#,
/// r#"</div>"#,
/// ));
/// ```
pub fn convert_to<'src>(
src: &'src str,
config: impl Borrow<OutputConfig<'src>>,
out: &mut impl fmt::Write,
) -> Result<(), ConvertError<'src>> {
let config: &OutputConfig = config.borrow();
let mut document = Document::parse(src).map_err(|e| ConvertError::Syntax {
syntax_errors: e,
src: src.into(),
})?;
if config.complete_page.unwrap_or(false) {
transform::complete_page::complete_page(&mut document, &config);
}
transform::infer_elements::infer_elements(&mut document, &config.special_tags);
transform::apply_lang(&mut document, &config.lang);
output::output_html_to(&document, out, config).map_err(|e| match e {
OutputError::WriteError(fmt::Error) => ConvertError::Unknown,
})?;
Ok(())
}