rsbx 2.0.0

Enhanced implementation of SeqBox in Rust
Documentation
use smallvec::SmallVec;
use json_utils::split_key_val_pair;

use std::sync::Mutex;

use misc_utils::to_camelcase;

use std::fmt;

use output_channel::OutputChannel;

#[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,
                            skip_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, skip_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,
                            skip_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, skip_quotes, context.first_item)?;

            context.first_item = false;

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