#![feature(cold_path)]

#[cfg(not(target_arch = "wasm32"))]
#[global_allocator]
static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc;

pub mod json;
pub mod bbnf;
pub mod ebnf;
pub mod bnf;
pub mod css;
pub mod google_sheets;

#[cfg(feature = "vm")]
pub mod vm;

/// Configuration for the prettifier output.
#[derive(Debug, Clone)]
pub struct PrinterConfig {
    pub max_width: usize,
    pub indent: usize,
    pub use_tabs: bool,
}

impl Default for PrinterConfig {
    fn default() -> Self {
        Self {
            max_width: 80,
            indent: 4,
            use_tabs: false,
        }
    }
}

impl PrinterConfig {
    pub fn new(max_width: usize, indent: usize) -> Self {
        Self {
            max_width,
            indent,
            use_tabs: false,
        }
    }

    pub fn to_printer(&self) -> pprint::Printer {
        pprint::Printer::new(self.max_width, self.indent, self.use_tabs)
    }
}

/// Format only AST nodes overlapping `[range.start, range.end)`, emitting
/// original source text for everything else.
///
/// The trait bounds on `T` require `to_doc()` and `source_range()` methods,
/// which are generated by `#[parser(prettify)]`.
pub fn range_to_doc<'a, T: ToDoc<'a> + SourceRange>(
    ast: &'a T,
    input: &'a str,
    range: std::ops::Range<usize>,
) -> pprint::Doc<'a> {
    match ast.source_range() {
        Some((start, end)) => {
            if end <= range.start || start >= range.end {
                // Entirely outside — emit verbatim.
                pprint::Doc::String(std::borrow::Cow::Borrowed(&input[start..end]))
            } else {
                // Overlaps — format normally.
                ast.to_doc()
            }
        }
        None => ast.to_doc(),
    }
}

/// Trait for types that have a `to_doc()` method (generated by prettify).
pub trait ToDoc<'a> {
    fn to_doc(&self) -> pprint::Doc<'a>;
}

impl<'a, T: ToDoc<'a> + ?Sized> ToDoc<'a> for Box<T> {
    fn to_doc(&self) -> pprint::Doc<'a> {
        (**self).to_doc()
    }
}

/// Trait for types that have a `source_range()` method (generated by prettify).
pub trait SourceRange {
    fn source_range(&self) -> Option<(usize, usize)>;
}

impl<T: SourceRange + ?Sized> SourceRange for Box<T> {
    fn source_range(&self) -> Option<(usize, usize)> {
        (**self).source_range()
    }
}