#[cfg(any(feature = "yaml", feature = "json", feature = "toml"))]
use std::error::Error as StdError;
use std::fmt;
use serde::Serialize;
use serde_json::Value;
use crate::error::Error;
#[cfg(any(feature = "yaml", feature = "json", feature = "toml"))]
use crate::ir::Ir;
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum Format {
#[default]
Yaml,
Json,
Toml,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum FieldsLayout {
#[default]
Fenced,
Bare,
}
impl Format {
pub(crate) fn as_str(self) -> &'static str {
match self {
Self::Yaml => "yaml",
Self::Json => "json",
Self::Toml => "toml",
}
}
}
impl fmt::Display for Format {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
pub(crate) fn dump<T: Serialize + ?Sized>(value: &T, format: Format) -> Result<String, Error> {
match format {
Format::Yaml => dump_yaml(value),
Format::Json => dump_json(value),
Format::Toml => dump_toml(value),
}
}
pub(crate) fn load(src: &str, format: Format) -> Result<Value, Error> {
decode(src, format, false)
}
pub(crate) fn load_body(src: &str, format: Format) -> Result<Value, Error> {
decode(src, format, true)
}
fn decode(src: &str, format: Format, body: bool) -> Result<Value, Error> {
match format {
Format::Yaml => load_yaml(src, body),
Format::Json => load_json(src, body),
Format::Toml => load_toml(src, body),
}
}
#[cfg(any(feature = "yaml", feature = "json", feature = "toml"))]
fn wrap_decoder<E>(err: E, body: bool) -> Error
where
E: Into<Box<dyn StdError + Send + Sync + 'static>>,
{
if body {
Error::body_source("structured section", err)
} else {
Error::front_matter(err)
}
}
fn dump_yaml<T: Serialize + ?Sized>(value: &T) -> Result<String, Error> {
#[cfg(feature = "yaml")]
{
yaml_serde::to_string(value).map_err(Error::type_error)
}
#[cfg(not(feature = "yaml"))]
{
let _ = value;
Err(Error::format_disabled(Format::Yaml))
}
}
fn dump_json<T: Serialize + ?Sized>(value: &T) -> Result<String, Error> {
#[cfg(feature = "json")]
{
serde_json::to_string_pretty(value).map_err(Error::type_error)
}
#[cfg(not(feature = "json"))]
{
let _ = value;
Err(Error::format_disabled(Format::Json))
}
}
fn dump_toml<T: Serialize + ?Sized>(value: &T) -> Result<String, Error> {
#[cfg(feature = "toml")]
{
toml::to_string_pretty(value).map_err(Error::type_error)
}
#[cfg(not(feature = "toml"))]
{
let _ = value;
Err(Error::format_disabled(Format::Toml))
}
}
fn load_yaml(src: &str, body: bool) -> Result<Value, Error> {
#[cfg(feature = "yaml")]
{
yaml_serde::from_str(src)
.map(|Ir(value)| value)
.map_err(|err| wrap_decoder(err, body))
}
#[cfg(not(feature = "yaml"))]
{
let _ = (src, body);
Err(Error::format_disabled(Format::Yaml))
}
}
fn load_json(src: &str, body: bool) -> Result<Value, Error> {
#[cfg(feature = "json")]
{
serde_json::from_str(src)
.map(|Ir(value)| value)
.map_err(|err| wrap_decoder(err, body))
}
#[cfg(not(feature = "json"))]
{
let _ = (src, body);
Err(Error::format_disabled(Format::Json))
}
}
fn load_toml(src: &str, body: bool) -> Result<Value, Error> {
#[cfg(feature = "toml")]
{
toml::from_str(src)
.map(|Ir(value)| value)
.map_err(|err| wrap_decoder(err, body))
}
#[cfg(not(feature = "toml"))]
{
let _ = (src, body);
Err(Error::format_disabled(Format::Toml))
}
}
#[cfg(test)]
mod tests {
use super::{FieldsLayout, Format};
#[test]
fn defaults() {
assert_eq!(Format::default(), Format::Yaml);
assert_eq!(FieldsLayout::default(), FieldsLayout::Fenced);
}
#[test]
fn format_display() {
assert_eq!(Format::Yaml.to_string(), "yaml");
assert_eq!(Format::Json.to_string(), "json");
assert_eq!(Format::Toml.to_string(), "toml");
}
#[test]
fn copy_eq() {
let yaml = Format::Yaml;
let yaml_copied = yaml;
assert_eq!(yaml, yaml_copied);
assert_ne!(Format::Yaml, Format::Json);
let fenced = FieldsLayout::Fenced;
let fenced_copied = fenced;
assert_eq!(fenced, fenced_copied);
assert_ne!(FieldsLayout::Fenced, FieldsLayout::Bare);
}
}