use crate::events::HighlightEvent;
use crate::languages::Language;
use std::io::{self, Write};
pub mod ansi;
pub mod html;
pub mod html_inline;
pub use html_inline::{HtmlInline, HtmlInlineBuilder};
pub mod html_multi_themes;
pub use html_multi_themes::{HtmlMultiThemes, HtmlMultiThemesBuilder};
pub mod html_linked;
pub use html_linked::{HtmlLinked, HtmlLinkedBuilder};
pub mod terminal;
pub use terminal::{Background as TerminalBackground, Terminal, TerminalBuilder};
pub mod bbcode;
pub use bbcode::{BBCodeScoped, BBCodeScopedBuilder};
pub(crate) fn source_text(source: &[u8], start: usize, end: usize) -> io::Result<&str> {
if start > end || end > source.len() {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
format!(
"invalid source range: {start}..{end} (len={})",
source.len()
),
));
}
std::str::from_utf8(&source[start..end])
.map_err(|error| io::Error::new(io::ErrorKind::InvalidData, error))
}
pub(crate) fn check_source_ranges<T>(
source: &[u8],
events: &[HighlightEvent<'_, T>],
) -> io::Result<()> {
for event in events {
if let HighlightEvent::Source { start, end } = event {
source_text(source, *start, *end)?;
}
}
Ok(())
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct HtmlElement {
pub open_tag: String,
pub close_tag: String,
}
pub trait Formatter<T = ()>: Send + Sync {
fn language(&self) -> Language;
fn render(
&self,
source: &str,
events: &[HighlightEvent<'_, T>],
output: &mut dyn Write,
) -> io::Result<()>;
fn render_budgeted(
&self,
source: &str,
events: &[HighlightEvent<'_, T>],
output: &mut dyn Write,
exhausted: BudgetExhausted,
) -> io::Result<()> {
let _ = exhausted;
self.render(source, events, output)
}
fn render_budgeted_or(
&self,
source: &str,
events: &[HighlightEvent<'_, T>],
output: &mut dyn Write,
exhausted: Option<BudgetExhausted>,
) -> io::Result<()> {
match exhausted {
Some(exhausted) => self.render_budgeted(source, events, output, exhausted),
None => self.render(source, events, output),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum BudgetExhausted {
Time,
Matches,
}
impl BudgetExhausted {
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::Time => "time",
Self::Matches => "matches",
}
}
}
impl<T> Formatter<T> for Box<dyn Formatter<T>> {
fn language(&self) -> Language {
(**self).language()
}
fn render(
&self,
source: &str,
events: &[HighlightEvent<'_, T>],
output: &mut dyn Write,
) -> io::Result<()> {
(**self).render(source, events, output)
}
fn render_budgeted(
&self,
source: &str,
events: &[HighlightEvent<'_, T>],
output: &mut dyn Write,
exhausted: BudgetExhausted,
) -> io::Result<()> {
(**self).render_budgeted(source, events, output, exhausted)
}
}
impl<T, F> Formatter<T> for &F
where
F: Formatter<T> + ?Sized,
{
fn language(&self) -> Language {
(**self).language()
}
fn render(
&self,
source: &str,
events: &[HighlightEvent<'_, T>],
output: &mut dyn Write,
) -> io::Result<()> {
(**self).render(source, events, output)
}
fn render_budgeted(
&self,
source: &str,
events: &[HighlightEvent<'_, T>],
output: &mut dyn Write,
exhausted: BudgetExhausted,
) -> io::Result<()> {
(**self).render_budgeted(source, events, output, exhausted)
}
}