use crate::{impl_clone_box, CloneBox, parse::tokens::Site};
use std::{convert, error::Error, fmt::{self, Debug}};
use colored::*;
use unicode_width::UnicodeWidthStr;
#[derive(Debug, Clone)]
pub struct GenerationError<'a> {
pub markup: &'static str,
pub message: String,
pub site: Site<'a>,
}
impl<'a> GenerationError<'a> {
pub fn new(ml: &'static str, msg: &str, site: &Site<'a>) -> Self {
Self {
markup: ml,
message: msg.to_owned(),
site: site.to_owned(),
}
}
}
impl<'a> fmt::Display for GenerationError<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let line_prefix = format!(" {} |", self.site.line);
let line_view = self.site.line_slice();
writeln!(f, "{} {}", line_prefix, line_view)?;
writeln!(f, "{:>prefix_offset$} {:~>text_offset$}{:^>length$}", "|", "", "",
prefix_offset=UnicodeWidthStr::width(line_prefix.as_str()),
text_offset=self.site.line_column() - 1,
length=self.site.width())?;
write!(f, "{}: {}",
format!("[{}] Error Generating {} ({}:{}:{})",
"**".red().bold(),
self.markup.bold(),
self.site.source,
self.site.line,
self.site.line_column(),
).black(),
self.message)
}
}
impl<'a> Error for GenerationError<'a> { }
impl<'a> From<std::io::Error> for GenerationError<'a> {
fn from(e: std::io::Error) -> Self {
Self {
markup: "<markup>",
message: format!("IO error: {}", e),
site: Site::unknown(),
}
}
}
impl<'a> convert::From<fmt::Error> for GenerationError<'a> {
fn from(e: fmt::Error) -> Self {
Self {
markup: "<markup>",
message: format!("Format buffer error: {}", e),
site: Site::unknown(),
}
}
}
pub type Formatter<'a> = &'a mut dyn fmt::Write;
pub trait MarkupFormatter: Debug + CloneBox {
fn generate(&self, buf: Formatter) -> Result<(), GenerationError>;
fn document(&self) -> Result<String, GenerationError>;
fn display(&self) -> Result<String, GenerationError> {
let mut buf = String::new();
self.generate(&mut buf)?;
Ok(buf)
}
}
impl_clone_box! { 'a; dyn MarkupFormatter + 'a}
impl fmt::Display for dyn MarkupFormatter {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.generate(f).map_err(|_| fmt::Error)
}
}
pub fn escape_xml(string: &str) -> String {
let mut bytes = string.bytes();
let mut byte_builder: Vec<u8> = Vec::with_capacity(bytes.len());
while let Some(byte) = bytes.next() {
match byte {
b'<' => byte_builder.extend(b"<"),
b'>' => byte_builder.extend(b">"),
b'"' => byte_builder.extend(b"""),
b'\'' => byte_builder.extend(b"'"),
b'&' => byte_builder.extend(b"&"),
_ => byte_builder.push(byte)
}
}
unsafe {
String::from_utf8_unchecked(byte_builder)
}
}
pub mod sexp;
pub mod text;
pub mod xml;
pub mod css;
pub mod html;