conf_json 0.1.4

A human editable configure file in JSON parser
Documentation
//! # A JSON compatable configure file parser
//! 
//! It's mainly for `read` not `write` from a human editable configure file in JSON.
//! Most of the configure key accessing methods are immutable.
//!
//! Besides standard JSON, following features are supported:
//!  - Comments begin with '#' which can take a whole line or trailing part of a line
//!  - Both single and double quotation marks strings are allowed
//!  - For JSON Arry and Object, trailing comma is allowed
//!
//! # Examples
//! Parse from `str`
//!
//! ```
//! use conf_json::value;
//!	let s = r##"
//!		## this is a json conf file
//!		##
//!		{
//!			## comment line
//!			"null_key": null,
//!			"i64_key": 1,
//!			"i64_sci_key": 123E+4,
//!			"i64_0x_key": 0xAf,
//!			"f64_key": 3.14,
//!			"f64_sci_key": 3.14e-2,
//!			"true_key": true,
//!			"false_key": false,  # comment in line
//!			'single_quote_key': 'a single quote value',
//!			"object_key": {
//!				"inner_key1": "abc",
//!				"inner_key2": 3.14E3,
//!			},
//!			"array_key": [1, "abc", ],
//!		}
//!	"##;
//!	println!("data source:");
//!	println!("{}", s);
//!	let v = s.parse::<value::Value>().unwrap();
//!
//!	// basic accessing
//!	println!("\nbasic accessing assertion begin ...");
//!	assert!(v.is_object());
//!	assert!(!v.is_null());
//!	assert!(v.has("i64_key"));
//!	assert!(!v["null_key"].as_bool());
//!	assert!(v["i64_key"].as_bool());
//!	assert_eq!(v["i64_key"].as_i64(), 1);
//!	assert_eq!(v["f64_key"].as_f64(), 3.14);
//!	assert_eq!(v["object_key"]["inner_key1"].as_str(), "abc");
//!	assert_eq!(v["array_key"][1].as_str(), "abc");
//!	println!("basic accessing assertion pass");
//!
//!	// iter over JSON Array
//!	println!("\niter over JSON Array:");
//!	v["array_key"].iter_array().for_each(|v| {println!("{:?}", v); });
//!
//!	// iter over JSON Object
//!	println!("\niter over JSON Object:");
//!	v.iter_object().for_each(|(k, v)| { println!("{}: {:?}", k, v); });
//! ```
//! 
//! Load from `str`
//!
//! ```
//! use conf_json;
//!	let v: conf_json::value::Value = "\"abc\"".as_bytes().into();
//!	assert_eq!(v.as_str(), "abc");
//! ```
//!
//! Load from file
//!
//! ```
//! use conf_json;
//! let v = conf_json::load_from_file("sample.conf").unwrap();
//! assert!(v.is_object());
//! assert_eq!(v["f64_key"].as_f64(), 3.14);
//! ```
//!
//! Load from any type T t that implements std::io::Read trait
//! ```
//! use conf_json;
//! let t = b"123";
//!	let v: conf_json::value::Value = t.as_slice().into();
//! assert_eq!(v.as_i64(), 123);
//! ```
//!

use std::fs;
use std::path::Path;
use std::fmt::Display;
use std::error::Error;

use crate::value::Value;
use crate::parser::Parser;

pub mod value;
pub mod parser;

pub fn load_from_file<P: AsRef<Path> + Display>(path: P) -> Result<Value, Box<dyn Error>> {
	Ok(Parser::new(fs::File::open(path)?).parse()?)
}

#[cfg(test)]
mod tests {
	use crate::value;

	#[test]
	fn test_empty_object_and_array() {
		let empty_obj: value::Value = b"{}".as_slice().into();
		assert!(empty_obj .is_object());
		assert!(!empty_obj.as_bool());

		let empty_array: value::Value = "[]".as_bytes().into();
		assert!(empty_array.is_array());
		assert!(!empty_array.as_bool());
	}

	#[test]
	fn test_weird_numbers() {
		let mut v :value::Value = "0.0".as_bytes().into();
		assert_eq!(v.as_i64(), 0);

		v = "0E0".as_bytes().into();
		assert_eq!(v.as_i64(), 0);

		v = "0e-0".as_bytes().into();
		assert_eq!(v.as_i64(), 0);
	}

	#[test]
	fn test_string() {
		let mut v :value::Value = "\"abc\"".as_bytes().into();
		assert_eq!(v.as_str(), "abc");

		v = "'ABC'".as_bytes().into();
		assert_eq!(v.as_str(), "ABC");

		v = "'\\r\\n\\t'".as_bytes().into();
		assert_eq!(v.as_str(), "\r\n\t");
	}

	#[test]
	fn all() {
		let s = r##"
			# this is a json conf file
			#
			{
				"null_key": null,
				"i64_key": 1,
				"i64_sci_key": 123E+4,
				"i64_0x_key": 0xAf,
				"f64_key": 3.14,
				"f64_sci_key": 3.14e-2,
				"true_key": true,
				"false_key": false,  # comment in line
				'single_quote_key': 'a single quote value',
				"object_key": {
					"inner_key1": "abc",
					"inner_key2": 3.14E3,
				},
				"array_key": [1, "abc", ],
			}
		"##;
		let v = s.parse::<value::Value>().unwrap();

		// basic accessing
		assert!(v.is_object());
		assert!(!v.is_null());
		assert!(v.has("i64_key"));
		assert!(!v["null_key"].as_bool());
		assert!(v["i64_key"].as_bool());
		assert_eq!(v["i64_key"].as_i64(), 1);
		assert_eq!(v["f64_key"].as_f64(), 3.14);
		assert_eq!(v["object_key"]["inner_key1"].as_str(), "abc");
		assert_eq!(v["array_key"][1].as_str(), "abc");

		// iter over JSON Array
		v["array_key"].iter_array().for_each(|v| {println!("{:?}", v); });

		// iter over JSON Object
		v.iter_object().for_each(|(k, v)| { println!("{}: {:?}", k, v); });
	}
}