json_diff 0.1.2

A small diff tool utility for comparing jsons
Documentation
use colored::*;
use json_diff::{
    compare_jsons,
    constants::Message,
    ds::{key_node::KeyNode, mismatch::Mismatch},
};
use std::{
    fmt, fs,
    io::{self, Write},
    process as proc,
    str::FromStr,
};
use structopt::StructOpt;

const HELP: &str = r#"
Example:
json_diff f source1.json source2.json
json_diff d '{...}' '{...}'

Option:
f   :   read input from json files
d   :   read input from command line"#;

#[derive(Debug)]
struct AppError {
    message: Message,
}
impl fmt::Display for AppError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.message)
    }
}

enum InputReadMode {
    D,
    F,
}
impl FromStr for InputReadMode {
    type Err = AppError;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "d" => Ok(InputReadMode::D),
            "f" => Ok(InputReadMode::F),
            _ => Err(Self::Err {
                message: Message::BadOption,
            }),
        }
    }
}

#[derive(StructOpt)]
#[structopt(about = HELP)]
struct Cli {
    read_mode: InputReadMode,
    source1: String,
    source2: String,
}

fn main() {
    let args = Cli::from_args();

    let (data1, data2) = match args.read_mode {
        InputReadMode::D => (args.source1, args.source2),
        InputReadMode::F => {
            if let Ok(d1) = fs::read_to_string(args.source1) {
                if let Ok(d2) = fs::read_to_string(args.source2) {
                    (d1, d2)
                } else {
                    error_exit(Message::SOURCE2);
                }
            } else {
                error_exit(Message::SOURCE1);
            }
        }
    };
    let mismatch = match compare_jsons(&data1, &data2) {
        Ok(mismatch) => mismatch,
        Err(err) => {
            eprintln!("{}", err);
            proc::exit(1)
        }
    };
    match display_output(mismatch) {
        Ok(_) => (),
        Err(err) => eprintln!("{}", err),
    };
}

fn error_exit(message: Message) -> ! {
    eprintln!("{}", message);
    proc::exit(1);
}

pub fn display_output(result: Mismatch) -> Result<(), std::io::Error> {
    let no_mismatch = Mismatch {
        left_only_keys: KeyNode::Nil,
        right_only_keys: KeyNode::Nil,
        keys_in_both: KeyNode::Nil,
    };

    let stdout = io::stdout();
    let mut handle = io::BufWriter::new(stdout.lock());
    Ok(if no_mismatch == result {
        writeln!(handle, "\n{}", Message::NoMismatch)?;
    } else {
        match result.keys_in_both {
            KeyNode::Node(_) => {
                let mut keys = Vec::new();
                result.keys_in_both.absolute_keys(&mut keys, None);
                writeln!(handle, "\n{}:", Message::Mismatch)?;
                for key in keys {
                    writeln!(handle, "{}", key)?;
                }
            }
            KeyNode::Value(_, _) => writeln!(handle, "{}", Message::RootMismatch)?,
            KeyNode::Nil => (),
        }
        match result.left_only_keys {
            KeyNode::Node(_) => {
                let mut keys = Vec::new();
                result.left_only_keys.absolute_keys(&mut keys, None);
                writeln!(handle, "\n{}:", Message::LeftExtra)?;
                for key in keys {
                    writeln!(handle, "{}", key.red().bold())?;
                }
            }
            KeyNode::Value(_, _) => (),
            KeyNode::Nil => (),
        }
        match result.right_only_keys {
            KeyNode::Node(_) => {
                let mut keys = Vec::new();
                result.right_only_keys.absolute_keys(&mut keys, None);
                writeln!(handle, "\n{}:", Message::RightExtra)?;
                for key in keys {
                    writeln!(handle, "{}", key.green().bold())?;
                }
            }
            KeyNode::Value(_, _) => (),
            KeyNode::Nil => (),
        }
    })
}