use std::collections::HashMap;
use std::io;
use std::path::Path;
use std::fs::File;
use crate::serde::Serialize;
use crate::serde_json::{Value, Map, Error as JSONError};
use crate::{Context, JSONGetTextValue, JSONGetText};
#[derive(Debug)]
pub enum JSONGetTextBuildError {
DefaultKeyNotFound,
TextInKeyNotInDefaultKey {
key: String,
text: String,
},
DuplicatedKey(String),
IOError(io::Error),
SerdeJSONError(JSONError),
}
impl ToString for JSONGetTextBuildError {
#[inline]
fn to_string(&self) -> String {
match self {
JSONGetTextBuildError::DefaultKeyNotFound => "The default key is not found.".to_string(),
JSONGetTextBuildError::TextInKeyNotInDefaultKey { key, text } => format!("The text `{}` in the key `{}` is not found in the default key.", text, key),
JSONGetTextBuildError::DuplicatedKey(s) => s.clone(),
JSONGetTextBuildError::IOError(err) => err.to_string(),
JSONGetTextBuildError::SerdeJSONError(err) => err.to_string()
}
}
}
impl From<io::Error> for JSONGetTextBuildError {
#[inline]
fn from(v: io::Error) -> JSONGetTextBuildError {
JSONGetTextBuildError::IOError(v)
}
}
impl From<JSONError> for JSONGetTextBuildError {
#[inline]
fn from(v: JSONError) -> JSONGetTextBuildError {
JSONGetTextBuildError::SerdeJSONError(v)
}
}
#[derive(Debug, Clone)]
pub struct JSONGetTextBuilder<'a> {
default_key: String,
context: Context<'a>,
}
impl<'a> JSONGetTextBuilder<'a> {
#[inline]
pub fn new<S: Into<String>>(default_key: S) -> JSONGetTextBuilder<'a> {
JSONGetTextBuilder {
default_key: default_key.into(),
context: HashMap::new(),
}
}
pub fn add_json<K: AsRef<str> + Into<String>, J: AsRef<str> + ?Sized>(&mut self, key: K, json: &'a J) -> Result<&mut Self, JSONGetTextBuildError> {
if self.context.contains_key(key.as_ref()) {
return Err(JSONGetTextBuildError::DuplicatedKey(key.into()));
}
let map: HashMap<String, JSONGetTextValue<'a>> = serde_json::from_str(json.as_ref())?;
let key = key.into();
self.context.insert(key, map);
Ok(self)
}
pub fn add_json_owned<K: AsRef<str> + Into<String>, J: AsRef<str>>(&mut self, key: K, json: J) -> Result<&mut Self, JSONGetTextBuildError> {
if self.context.contains_key(key.as_ref()) {
return Err(JSONGetTextBuildError::DuplicatedKey(key.into()));
}
let value: Map<String, Value> = serde_json::from_str(json.as_ref())?;
let mut map: HashMap<String, JSONGetTextValue<'static>> = HashMap::with_capacity(value.len());
for (k, v) in value {
map.insert(k, JSONGetTextValue::from_json_value(v));
}
let key = key.into();
self.context.insert(key, map);
Ok(self)
}
pub fn add_json_file<K: AsRef<str> + Into<String>, P: AsRef<Path>>(&mut self, key: K, path: P) -> Result<&mut Self, JSONGetTextBuildError> {
if self.context.contains_key(key.as_ref()) {
return Err(JSONGetTextBuildError::DuplicatedKey(key.into()));
}
let path = path.as_ref();
let value: Map<String, Value> = serde_json::from_reader(File::open(&path)?)?;
let mut map: HashMap<String, JSONGetTextValue<'static>> = HashMap::with_capacity(value.len());
for (k, v) in value {
map.insert(k, JSONGetTextValue::from_json_value(v));
}
let key = key.into();
self.context.insert(key, map);
Ok(self)
}
pub fn add_serialize<K: AsRef<str> + Into<String>, S: Serialize>(&mut self, key: K, value: S) -> Result<&mut Self, JSONGetTextBuildError> {
if self.context.contains_key(key.as_ref()) {
return Err(JSONGetTextBuildError::DuplicatedKey(key.into()));
}
let value: Value = serde_json::to_value(value)?;
match value {
Value::Object(value) => {
let mut map: HashMap<String, JSONGetTextValue<'static>> = HashMap::with_capacity(value.len());
for (k, v) in value {
map.insert(k, JSONGetTextValue::from_json_value(v));
}
let key = key.into();
self.context.insert(key, map);
Ok(self)
}
_ => {
serde_json::from_str::<Map<String, Value>>("\"MagicLen\"")?;
unreachable!()
}
}
}
pub fn add_map<K: AsRef<str> + Into<String>>(&mut self, key: K, map: HashMap<String, JSONGetTextValue<'a>>) -> Result<&mut Self, JSONGetTextBuildError> {
if self.context.contains_key(key.as_ref()) {
return Err(JSONGetTextBuildError::DuplicatedKey(key.into()));
}
let key = key.into();
self.context.insert(key, map);
Ok(self)
}
pub fn build(self) -> Result<JSONGetText<'a>, JSONGetTextBuildError> {
JSONGetText::from_context_with_default_key(self.default_key, self.context)
}
}
impl<'a> From<String> for JSONGetTextBuilder<'a> {
#[inline]
fn from(v: String) -> JSONGetTextBuilder<'a> {
JSONGetTextBuilder::new(v)
}
}