#![cfg_attr(feature = "nightly", feature(map_get_key_value))]
extern crate serde_json;
extern crate regex;
use std::collections::HashMap;
use std::path::Path;
use std::fs::File;
use regex::Regex;
type Context = HashMap<String, HashMap<String, String>>;
#[derive(Debug)]
pub struct JSONGetTextBuilder {
default_key: String,
context: Context,
}
impl JSONGetTextBuilder {
pub fn new(default_key: &str) -> JSONGetTextBuilder {
JSONGetTextBuilder {
default_key: default_key.to_string(),
context: HashMap::new(),
}
}
pub fn add_json_string_to_context(&mut self, key: &str, json: &str) -> Result<&Self, String> {
if self.context.contains_key(key) {
return Err("The key exists.".to_string());
}
let map: HashMap<String, String> = serde_json::from_str(json).map_err(|err| { err.to_string() })?;
self.context.insert(key.to_string(), map);
Ok(self)
}
pub fn add_json_bytes_to_context(&mut self, key: &str, json: &[u8]) -> Result<&Self, String> {
if self.context.contains_key(key) {
return Err("The key exists.".to_string());
}
let map: HashMap<String, String> = serde_json::from_slice(json).map_err(|err| { err.to_string() })?;
self.context.insert(key.to_string(), map);
Ok(self)
}
pub fn add_json_file_to_context<P: AsRef<Path>>(&mut self, key: &str, path: P) -> Result<&Self, String> {
if self.context.contains_key(key) {
return Err("The key exists.".to_string());
}
let file = File::open(path).map_err(|err| { err.to_string() })?;
let map: HashMap<String, String> = serde_json::from_reader(&file).map_err(|err| { err.to_string() })?;
self.context.insert(key.to_string(), map);
Ok(self)
}
pub fn add_map_to_context<P: AsRef<Path>>(&mut self, key: &str, map: HashMap<String, String>) -> Result<&Self, String> {
if self.context.contains_key(key) {
return Err("The key exists.".to_string());
}
self.context.insert(key.to_string(), map);
Ok(self)
}
pub fn build(self) -> Result<JSONGetText, String> {
JSONGetText::from_context_inner(self.default_key, self.context)
}
}
#[derive(Debug)]
pub struct JSONGetText {
default_key: String,
context: Context,
}
impl JSONGetText {
pub fn build(default_key: &str) -> JSONGetTextBuilder {
JSONGetTextBuilder::new(default_key)
}
pub fn from_context(default_key: &str, context: Context) -> Result<JSONGetText, String> {
JSONGetText::from_context_inner(default_key.to_string(), context)
}
fn from_context_inner(default_key: String, mut context: Context) -> Result<JSONGetText, String> {
if !context.contains_key(&default_key) {
return Err("Cannot find the default key in the context.".to_string());
}
if !context.contains_key(&default_key) {
return Err("Context should contain the default key.".to_string());
}
{
let default_map = context.remove(&default_key).unwrap();
for (key, map) in context.iter_mut() {
{
let map_keys = map.keys();
for map_key in map_keys {
if !default_map.contains_key(map_key) {
return Err(format! {"The text `{}` in the key `{}` is not in the map of the default key.", map_key, key});
}
}
}
let map_keys = default_map.keys();
for map_key in map_keys {
if !map.contains_key(map_key) {
map.insert(map_key.clone(), default_map.get(map_key).unwrap().clone());
}
}
}
context.insert(default_key.clone(), default_map);
}
Ok(JSONGetText {
default_key: default_key,
context,
})
}
pub fn get_keys(&self) -> Vec<&str> {
let mut vec = Vec::new();
for key in self.context.keys() {
vec.push(key.as_str());
}
vec
}
pub fn get_default_key(&self) -> &str {
&self.default_key
}
pub fn get(&self, key: &str) -> &HashMap<String, String> {
match self.context.get(key) {
Some(m) => m,
None => self.context.get(&self.default_key).unwrap()
}
}
pub fn get_text(&self, text: &str) -> Option<&str> {
let map = self.context.get(&self.default_key).unwrap();
map.get(text).map(|s| s.as_str())
}
pub fn get_text_with_key(&self, key: &str, text: &str) -> Option<&str> {
let map = match self.context.get(key) {
Some(m) => m,
None => self.context.get(&self.default_key).unwrap()
};
map.get(text).map(|s| s.as_str())
}
#[cfg(feature = "nightly")]
pub fn get_multiple_text(&self, text_array: &[&str]) -> Option<HashMap<&str, &str>> {
let map = self.context.get(&self.default_key).unwrap();
let mut new_map = HashMap::new();
for &text in text_array.iter() {
let (key, value) = map.get_key_value(text)?;
new_map.insert(key.as_str(), value.as_str());
}
Some(new_map)
}
#[cfg(feature = "nightly")]
pub fn get_multiple_text_with_key(&self, key: &str, text_array: &[&str]) -> Option<HashMap<&str, &str>> {
let map = match self.context.get(key) {
Some(m) => m,
None => self.context.get(&self.default_key).unwrap()
};
let mut new_map = HashMap::new();
for &text in text_array.iter() {
let (key, value) = map.get_key_value(text)?;
new_map.insert(key.as_str(), value.as_str());
}
Some(new_map)
}
pub fn get_filtered_text(&self, regex: &Regex) -> Option<HashMap<&str, &str>> {
let map = self.context.get(&self.default_key).unwrap();
let mut new_map = HashMap::new();
for (key, value) in map.iter() {
if !regex.is_match(key) {
continue;
}
new_map.insert(key.as_str(), value.as_str());
}
Some(new_map)
}
pub fn get_filtered_text_with_key(&self, key: &str, regex: &Regex) -> Option<HashMap<&str, &str>> {
let map = match self.context.get(key) {
Some(m) => m,
None => self.context.get(&self.default_key).unwrap()
};
let mut new_map = HashMap::new();
for (key, value) in map.iter() {
if !regex.is_match(key) {
continue;
}
new_map.insert(key.as_str(), value.as_str());
}
Some(new_map)
}
}
#[macro_export]
macro_rules! static_json_gettext_build {
( $default_key:expr, $($key:expr, $path:expr), * ) => {
{
use self::json_gettext::JSONGetText;
let mut builder = JSONGetText::build($default_key);
$(
{
let data = include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/", $path));
builder.add_json_bytes_to_context($key, data).unwrap();
}
)*
builder.build()
}
};
}
#[macro_export]
macro_rules! get_text {
( $ctx:ident, $text:expr ) => {
{
$ctx.get_text($text)
}
};
( $ctx:ident, $key:expr, $text:expr ) => {
{
$ctx.get_text_with_key($key, $text)
}
};
( $ctx:ident, $key:expr, $text:expr, $($text_array:expr), + ) => {
{
let mut text_array = vec![$text];
$(
{
text_array.push($text_array);
}
)*
$ctx.get_multiple_text_with_key($key, &text_array)
}
};
}