blkar 7.2.7

Multithreaded archiver offering bit rot protection and sector level recoverability
Documentation
use crate::json_utils::split_key_val_pair;
use crate::misc_utils::to_camelcase;
use crate::output_channel::OutputChannel;
use smallvec::SmallVec;
use std::fmt;
use std::sync::Mutex;

#[derive(Copy, Clone, PartialEq, Debug)]
pub enum BracketType {
    Curly,
    Square,
}

#[derive(Clone, Debug)]
struct JSONContext {
    first_item: bool,
    bracket_type: BracketType,
}

impl JSONContext {
    pub fn new(bracket_type: BracketType) -> JSONContext {
        JSONContext {
            first_item: true,
            bracket_type,
        }
    }
}

#[derive(Debug)]
pub struct JSONPrinter {
    json_enabled: bool,
    output_channel: OutputChannel,
    contexts: Mutex<SmallVec<[JSONContext; 8]>>,
}

impl Clone for JSONPrinter {
    fn clone(&self) -> Self {
        JSONPrinter {
            json_enabled: self.json_enabled,
            output_channel: self.output_channel,
            contexts: Mutex::new(self.contexts.lock().unwrap().clone()),
        }
    }
}

fn write_comma_if_not_first(f: &mut fmt::Formatter, context: &mut JSONContext) -> fmt::Result {
    if !context.first_item {
        write!(f, ",")?;
    }
    context.first_item = false;

    Ok(())
}

fn bracket_type_to_str_open(bracket_type: BracketType) -> &'static str {
    match bracket_type {
        BracketType::Curly => "{",
        BracketType::Square => "[",
    }
}

fn bracket_type_to_str_close(bracket_type: BracketType) -> &'static str {
    match bracket_type {
        BracketType::Curly => "}",
        BracketType::Square => "]",
    }
}

impl JSONPrinter {
    pub fn new(json_enabled: bool, output_channel: OutputChannel) -> JSONPrinter {
        JSONPrinter {
            json_enabled,
            output_channel,
            contexts: Mutex::new(SmallVec::new()),
        }
    }

    pub fn json_enabled(&self) -> bool {
        self.json_enabled
    }

    pub fn output_channel(&self) -> OutputChannel {
        self.output_channel
    }

    pub fn set_output_channel(&mut self, output_channel: OutputChannel) {
        self.output_channel = output_channel
    }

    pub fn first_item(&self) -> bool {
        self.contexts.lock().unwrap().last().unwrap().first_item
    }

    fn print_comma_if_not_first(&self, context: &mut JSONContext) {
        if !context.first_item {
            print_at_output_channel!(self.output_channel => ",");
        }
        context.first_item = false;
    }

    pub fn print_open_bracket(&self, name: Option<&str>, bracket_type: BracketType) {
        if !self.json_enabled {
            return;
        }

        match self.contexts.lock().unwrap().last_mut() {
            None => {}
            Some(x) => self.print_comma_if_not_first(x),
        }

        match name {
            None => {}
            Some(n) => print_at_output_channel!(self.output_channel => "\"{}\": ", to_camelcase(n)),
        }

        println_at_output_channel!(self.output_channel => "{}", bracket_type_to_str_open(bracket_type));

        self.contexts
            .lock()
            .unwrap()
            .push(JSONContext::new(bracket_type));
    }

    pub fn write_open_bracket(
        &self,
        f: &mut fmt::Formatter,
        name: Option<&str>,
        bracket_type: BracketType,
    ) -> fmt::Result {
        if !self.json_enabled {
            return Ok(());
        }

        match self.contexts.lock().unwrap().last_mut() {
            None => {}
            Some(x) => write_comma_if_not_first(f, x)?,
        }

        match name {
            None => {}
            Some(n) => write!(f, "\"{}\": ", to_camelcase(n))?,
        }

        writeln!(f, "{}", bracket_type_to_str_open(bracket_type))?;

        self.contexts
            .lock()
            .unwrap()
            .push(JSONContext::new(bracket_type));

        Ok(())
    }

    pub fn print_close_bracket(&self) {
        if !self.json_enabled {
            return;
        }

        let context = self.contexts.lock().unwrap().pop().unwrap();

        println_at_output_channel!(self.output_channel => "{}", bracket_type_to_str_close(context.bracket_type));
    }

    pub fn write_close_bracket(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if !self.json_enabled {
            return Ok(());
        }

        let context = self.contexts.lock().unwrap().pop().unwrap();

        writeln!(f, "{}", bracket_type_to_str_close(context.bracket_type))
    }

    pub fn print_maybe_json(&self, force_quotes: bool, msg: &str) {
        if self.json_enabled {
            let mut contexts = self.contexts.lock().unwrap();
            let context = contexts.last_mut().unwrap();

            let (l, r): (&str, &str) = split_key_val_pair(&msg);

            print_json_field!(self.output_channel => l, r, force_quotes, context.first_item);

            context.first_item = false;
        } else {
            println_at_output_channel!(self.output_channel => "{}", msg);
        }
    }

    pub fn write_maybe_json(
        &self,
        f: &mut fmt::Formatter,
        force_quotes: bool,
        msg: &str,
    ) -> fmt::Result {
        if self.json_enabled {
            let mut contexts = self.contexts.lock().unwrap();
            let context = contexts.last_mut().unwrap();

            let (l, r): (&str, &str) = split_key_val_pair(&msg);

            write_json_field!(f, l, r, force_quotes, context.first_item)?;

            context.first_item = false;

            Ok(())
        } else {
            writeln!(f, "{}", msg)
        }
    }
}