jjpwrgem-parse 0.14.0

JSON parser and formatter with rich errors
Documentation
mod prettify;
#[cfg(feature = "serde")]
pub mod serde;
pub(crate) mod uglify;

pub use prettify::{prettify_document, prettify_document_into, prettify_str};
pub use uglify::{uglify_document, uglify_document_into, uglify_str, uglify_str_into};

use crate::ast::Document;

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum FormatRequest {
    Json(JsonMode),
    Jsonlines(JsonlinesOptions),
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum JsonMode {
    Prettify(PrettifyOptions),
    Uglify,
}

impl Default for FormatRequest {
    fn default() -> Self {
        Self::Json(JsonMode::default())
    }
}

impl Default for JsonMode {
    fn default() -> Self {
        Self::Prettify(PrettifyOptions::default())
    }
}

#[derive(bon::Builder, Debug, Clone, PartialEq, Eq)]
pub struct PrettifyOptions {
    #[builder(default = 80)]
    pub preferred_width: usize,
    #[builder(default)]
    pub line_ending: LineEnding,
}

impl Default for PrettifyOptions {
    fn default() -> Self {
        Self::builder().build()
    }
}

#[derive(bon::Builder, Default, Debug, Clone, PartialEq, Eq)]
pub struct JsonlinesOptions {
    #[builder(default)]
    pub line_ending: LineEnding,
}

pub fn format_document<S: AsRef<str>>(doc: &Document<S>, mode: &JsonMode) -> String {
    match mode {
        JsonMode::Prettify(opts) => prettify_document(doc, opts.preferred_width, opts.line_ending),
        JsonMode::Uglify => uglify_document(doc),
    }
}

use crate::tokens::{FALSE, NULL, TRUE};

pub(crate) fn join_into<T, B>(
    buf: &mut B,
    items: impl IntoIterator<Item = T>,
    mut item_fmt: impl FnMut(&mut B, &T),
    mut delim_fmt: impl FnMut(&mut B, &T),
) {
    let mut iter = items.into_iter();
    if let Some(first) = iter.next() {
        item_fmt(buf, &first);
        for item in iter {
            delim_fmt(buf, &item);
            item_fmt(buf, &item);
        }
    }
}

#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
pub enum LineEnding {
    #[default]
    Lf,
    CrLf,
}

impl LineEnding {
    pub const fn as_str(self) -> &'static str {
        match self {
            Self::Lf => "\n",
            Self::CrLf => "\r\n",
        }
    }
}

pub(crate) trait Emitter {
    fn push(&mut self, c: char);
    fn push_str(&mut self, s: &str);

    // defaults
    fn push_quoted(&mut self, s: &str) {
        self.push('"');
        self.push_str(s);
        self.push('"');
    }

    fn emit_null(&mut self) {
        self.push_str(NULL);
    }
    fn emit_string(&mut self, s: &str) {
        self.push_quoted(s);
    }
    fn emit_mantissa(&mut self, n: &str) {
        self.push_str(n);
    }
    fn emit_exponent(&mut self, e: &str) {
        self.push('e');
        self.push_str(e);
    }
    fn emit_boolean(&mut self, b: bool) {
        self.push_str(if b { TRUE } else { FALSE });
    }
    fn emit_item_delim(&mut self) {
        self.push(',');
    }
    fn emit_array_open(&mut self) {
        self.push('[');
    }
    fn emit_array_close(&mut self) {
        self.push(']');
    }
    fn emit_object_open(&mut self) {
        self.push('{');
    }
    fn emit_object_close(&mut self) {
        self.push('}');
    }
    fn emit_key_val_delim(&mut self) {
        self.push(':');
    }
}