mod error;
#[cfg(feature = "toml")]
pub mod toml;
#[cfg(feature = "yaml")]
pub mod yaml;
pub use self::error::{UniNodeFmtError, UniNodeLoadError, UniNodeIoError};
use std::collections::HashMap;
use std::sync::RwLock;
use std::path::Path;
use once_cell::sync::Lazy;
use crate::value::UniNode;
type GlobalFormat = Box<dyn UniNodeFormat + Sync + Send + 'static>;
type FormatMap = HashMap<String, GlobalFormat>;
static FORMATS: Lazy<RwLock<FormatMap>> = Lazy::new(|| {
let mut cont = FormatMap::new();
#[cfg(feature = "yaml")]
add_format(&mut cont, yaml::YamlFormat::default());
#[cfg(feature = "toml")]
add_format(&mut cont, toml::TomlFormat::default());
RwLock::new(cont)
});
pub trait UniNodeFormat {
fn extensions(&self) -> &[&str];
fn parse(&self, text: &str) -> Result<UniNode, UniNodeFmtError>;
fn build(&self, node: &UniNode) -> Result<String, UniNodeFmtError>;
}
fn add_format<L>(cont: &mut FormatMap, format: L)
where
L: UniNodeFormat + Clone + Sync + Send + 'static,
{
for ext in format.extensions() {
cont.insert(ext.to_string(), Box::new(format.clone()));
}
}
pub fn register_format<F>(format: F)
where
F: UniNodeFormat + Clone + Sync + Send + 'static,
{
let mut formats = FORMATS.write().unwrap();
add_format(&mut formats, format);
}
pub fn available_extensions() -> Vec<String> {
let formats = FORMATS.read().unwrap();
formats.keys().cloned().collect()
}
impl UniNode {
pub fn parse<P>(path: P, content: &str) -> Result<UniNode, UniNodeLoadError>
where
P: AsRef<Path>,
{
let path = path.as_ref();
let ext = path
.extension()
.and_then(|e| e.to_str())
.or_else(|| path.to_str())
.ok_or_else(|| {
let path = path.to_string_lossy();
let err = UniNodeFmtError::NotDefinedFormat(path.into());
UniNodeLoadError::Fmt(err)
})?;
let formats = FORMATS.read().unwrap();
if !formats.contains_key(ext) {
let err = UniNodeFmtError::NotDefinedFormat(ext.to_string());
return Err(UniNodeLoadError::Fmt(err));
}
formats[ext].parse(content).map_err(UniNodeLoadError::Fmt)
}
pub fn load<P>(path: P) -> Result<UniNode, UniNodeLoadError>
where
P: AsRef<Path>,
{
let path = path.as_ref();
match path.extension().and_then(|e| e.to_str()) {
Some(ext) => {
let formats = FORMATS.read().unwrap();
if !formats.contains_key(ext) {
let err =
UniNodeFmtError::NotDefinedFormat(ext.to_string());
return Err(UniNodeLoadError::Fmt(err));
}
let content = std::fs::read_to_string(path)
.map_err(|e| UniNodeIoError::new(path, e))?;
formats[ext]
.parse(content.as_ref())
.map_err(UniNodeLoadError::Fmt)
},
None => {
for (ext, fmt) in FORMATS.read().unwrap().iter() {
let full_path = path.with_extension(&ext);
if full_path.is_file() {
let content = std::fs::read_to_string(&full_path)
.map_err(|e| UniNodeIoError::new(full_path, e))?;
return fmt
.parse(&content)
.map_err(UniNodeLoadError::Fmt);
}
}
Err(UniNodeLoadError::NotFoundLoader)
},
}
}
}