use std::io::Read;
use paste::paste;
#[cfg(feature = "custom")]
mod custom;
#[cfg(feature = "custom")]
pub use custom::CustomFormat;
#[cfg(feature = "csv")]
mod csv;
#[cfg(feature = "ini")]
mod ini;
#[cfg(feature = "json")]
mod json;
#[cfg(feature = "plaintext")]
mod plaintext;
#[cfg(feature = "toml")]
mod toml;
#[cfg(feature = "xml")]
mod xml;
#[cfg(feature = "yaml")]
mod yaml;
use serde::{Serialize, de::DeserializeOwned};
use thiserror::Error;
macro_rules! define_formats {
(
$(#[$enum_meta:meta])*
$vis:vis enum $name:ident {
$($body:tt)*
}
) => {
define_formats!(@parse
[$($enum_meta)*]
$vis
$name
[]
[]
$($body)*
);
};
(@parse
[$($enum_meta:meta)*]
$vis:vis
$name:ident
[$($enum_variants:tt)*]
[$($spec_entries:tt)*]
) => {
define_formats!(@emit
[$($enum_meta)*]
$vis
$name
[$($enum_variants)*]
[$($spec_entries)*]
$
);
};
(@emit
[$($enum_meta:meta)*]
$vis:vis
$name:ident
[$($enum_variants:tt)*]
[$($spec_entries:tt)*]
$d:tt
) => {
$(#[$enum_meta])*
$vis enum $name {
$($enum_variants)*
}
impl Copy for $name {}
macro_rules! format_spec {
($d mac:ident ( $d($d args:tt)* )) => {
$d mac! {
$d($d args)*
$($spec_entries)*
}
};
($d mac:ident) => {
$d mac! {
$($spec_entries)*
}
};
}
};
(@parse
[$($enum_meta:meta)*]
$vis:vis
$name:ident
[$($enum_variants:tt)*]
[$($spec_entries:tt)*]
$(#[$var_meta:meta])*
$variant:ident => (
$cat:ident,
$module:ident,
$canonical:literal,
[$($ext:literal),* $(,)?],
[$($alias:literal),* $(,)?]
)
, $($tail:tt)*
) => {
define_formats!(@parse
[$($enum_meta)*]
$vis
$name
[
$($enum_variants)*
$(#[$var_meta])*
$variant,
]
[
$($spec_entries)*
($cat, $variant, $canonical, $module, $canonical, [$($ext),*], [$($alias),*])
]
$($tail)*
);
};
(@parse
[$($enum_meta:meta)*]
$vis:vis
$name:ident
[$($enum_variants:tt)*]
[$($spec_entries:tt)*]
$(#[$var_meta:meta])*
$variant:ident => (
$cat:ident,
$module:ident,
$canonical:literal,
[$($ext:literal),* $(,)?],
[$($alias:literal),* $(,)?]
)
) => {
define_formats!(@parse
[$($enum_meta)*]
$vis
$name
[
$($enum_variants)*
$(#[$var_meta])*
$variant,
]
[
$($spec_entries)*
($cat, $variant, $canonical, $module, $canonical, [$($ext),*], [$($alias),*])
]
);
};
(@parse
[$($enum_meta:meta)*]
$vis:vis
$name:ident
[$($enum_variants:tt)*]
[$($spec_entries:tt)*]
$(#[$var_meta:meta])*
$variant:ident => (
$cat:ident,
$module:ident,
$canonical:literal,
[$($alias:literal),* $(,)?]
)
, $($tail:tt)*
) => {
define_formats!(@parse
[$($enum_meta)*]
$vis
$name
[
$($enum_variants)*
$(#[$var_meta])*
$variant,
]
[
$($spec_entries)*
($cat, $variant, $canonical, $module, $canonical, [$($alias),*], [$($alias),*])
]
$($tail)*
);
};
(@parse
[$($enum_meta:meta)*]
$vis:vis
$name:ident
[$($enum_variants:tt)*]
[$($spec_entries:tt)*]
$(#[$var_meta:meta])*
$variant:ident => (
$cat:ident,
$module:ident,
$canonical:literal,
[$($alias:literal),* $(,)?]
)
) => {
define_formats!(@parse
[$($enum_meta)*]
$vis
$name
[
$($enum_variants)*
$(#[$var_meta])*
$variant,
]
[
$($spec_entries)*
($cat, $variant, $canonical, $module, $canonical, [$($alias),*], [$($alias),*])
]
);
};
(@parse
[$($enum_meta:meta)*]
$vis:vis
$name:ident
[$($enum_variants:tt)*]
[$($spec_entries:tt)*]
$(#[$var_meta:meta])*
$variant:ident => (
$cat:ident,
$feat:literal,
$module:ident,
$display:literal,
[$($ext:literal),* $(,)?],
[$($alias:literal),* $(,)?]
)
, $($tail:tt)*
) => {
define_formats!(@parse
[$($enum_meta)*]
$vis
$name
[
$($enum_variants)*
$(#[$var_meta])*
$variant,
]
[
$($spec_entries)*
($cat, $variant, $feat, $module, $display, [$($ext),*], [$($alias),*])
]
$($tail)*
);
};
(@parse
[$($enum_meta:meta)*]
$vis:vis
$name:ident
[$($enum_variants:tt)*]
[$($spec_entries:tt)*]
$(#[$var_meta:meta])*
$variant:ident => (
$cat:ident,
$feat:literal,
$module:ident,
$display:literal,
[$($ext:literal),* $(,)?],
[$($alias:literal),* $(,)?]
)
) => {
define_formats!(@parse
[$($enum_meta)*]
$vis
$name
[
$($enum_variants)*
$(#[$var_meta])*
$variant,
]
[
$($spec_entries)*
($cat, $variant, $feat, $module, $display, [$($ext),*], [$($alias),*])
]
);
};
(@parse
[$($enum_meta:meta)*]
$vis:vis
$name:ident
[$($enum_variants:tt)*]
[$($spec_entries:tt)*]
$(#[$var_meta:meta])*
$variant:ident
, $($tail:tt)*
) => {
define_formats!(@parse
[$($enum_meta)*]
$vis
$name
[
$($enum_variants)*
$(#[$var_meta])*
$variant,
]
[$($spec_entries)*]
$($tail)*
);
};
(@parse
[$($enum_meta:meta)*]
$vis:vis
$name:ident
[$($enum_variants:tt)*]
[$($spec_entries:tt)*]
$(#[$var_meta:meta])*
$variant:ident
) => {
define_formats!(@parse
[$($enum_meta)*]
$vis
$name
[
$($enum_variants)*
$(#[$var_meta])*
$variant,
]
[$($spec_entries)*]
);
};
(@parse
[$($enum_meta:meta)*]
$vis:vis
$name:ident
[$($enum_variants:tt)*]
[$($spec_entries:tt)*]
$(#[$var_meta:meta])*
$variant:ident ( $($fields:tt)* )
, $($tail:tt)*
) => {
define_formats!(@parse
[$($enum_meta)*]
$vis
$name
[
$($enum_variants)*
$(#[$var_meta])*
$variant ( $($fields)* ),
]
[$($spec_entries)*]
$($tail)*
);
};
(@parse
[$($enum_meta:meta)*]
$vis:vis
$name:ident
[$($enum_variants:tt)*]
[$($spec_entries:tt)*]
$(#[$var_meta:meta])*
$variant:ident ( $($fields:tt)* )
) => {
define_formats!(@parse
[$($enum_meta)*]
$vis
$name
[
$($enum_variants)*
$(#[$var_meta])*
$variant ( $($fields)* ),
]
[$($spec_entries)*]
);
};
}
define_formats!(
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum FormatKind {
Json => (Structured, json, "json", ["json"]),
Yaml => (Structured, yaml, "yaml", ["yaml", "yml"]),
Toml => (Structured, toml, "toml", ["toml"]),
Ini => (Structured, ini, "ini", ["ini"]),
Csv => (Other, csv, "csv", ["csv"]),
Xml => (Other, xml, "xml", ["xml"]),
Custom(&'static str),
Plaintext => (Other, plaintext, "plaintext", ["plaintext", "text", "txt"]),
}
);
macro_rules! impl_formatkind_display {
( $(($cat:ident, $kind:ident, $feat:literal, $module:ident,
$display:literal, [$($ext:literal),*], [$($alias:literal),*]))* ) => {
impl std::fmt::Display for FormatKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
$( FormatKind::$kind => write!(f, $display), )*
FormatKind::Custom(name) => write!(f, "{}", name),
}
}
}
};
}
macro_rules! define_default_order_from_spec {
( $(($cat:ident, $kind:ident, $feat:literal, $module:ident,
$display:literal, [$($ext:literal),*], [$($alias:literal),*]))* ) => {
pub(crate) const DEFAULT_FORMAT_ORDER: &[FormatKind] = &[
$( FormatKind::$kind ),*
];
};
}
macro_rules! define_structured_text_from_spec {
(
$(
(Structured, $kind:ident, $feat:literal, $module:ident,
$display:literal, [$($ext:literal),*], [$($alias:literal),*])
)*
$(
(Other, $other_kind:ident, $other_feat:literal, $other_module:ident,
$other_display:literal, [$($other_ext:literal),*], [$($other_alias:literal),*])
)*
) => {
#[allow(dead_code)]
pub(crate) const STRUCTURED_TEXT_FORMATS: &[FormatKind] = &[
$( FormatKind::$kind, )*
];
};
}
format_spec!(define_default_order_from_spec);
format_spec!(define_structured_text_from_spec);
format_spec!(impl_formatkind_display);
macro_rules! impl_for_each_enabled_builtin {
( $(($cat:ident, $kind:ident, $feat:literal, $module:ident,
$display:literal, [$($ext:literal),*], [$($alias:literal),*]))* ) => {
pub(crate) fn for_each_enabled_builtin<F>(mut f: F)
where
F: FnMut(FormatKind),
{
let _ = &mut f;
for kind in DEFAULT_FORMAT_ORDER {
match kind {
$(
FormatKind::$kind => {
#[cfg(feature = $feat)]
f(FormatKind::$kind);
}
)*
FormatKind::Custom(_) => {}
}
}
}
};
}
format_spec!(impl_for_each_enabled_builtin);
macro_rules! impl_formatkind_extensions_body {
($self:ident
$(($cat:ident, $kind:ident, $feat:literal, $module:ident,
$display:literal, [$($ext:literal),*], [$($alias:literal),*]))*
) => {{
match $self {
$( FormatKind::$kind => &[$($ext),*], )*
FormatKind::Custom(_) => &[],
}
}};
}
macro_rules! impl_formatkind_is_available_body {
($self:ident
$(($cat:ident, $kind:ident, $feat:literal, $module:ident,
$display:literal, [$($ext:literal),*], [$($alias:literal),*]))*
) => {{
match $self {
$(
#[cfg(feature = $feat)]
FormatKind::$kind => true,
#[cfg(not(feature = $feat))]
FormatKind::$kind => false,
)*
FormatKind::Custom(_) => true,
}
}};
}
macro_rules! impl_formatkind_from_str_body {
($lower:ident
$(($cat:ident, $kind:ident, $feat:literal, $module:ident,
$display:literal, [$($ext:literal),*], [$($alias:literal),*]))*
) => {{
let kind = match $lower.as_str() {
$(
$( $alias )|* => FormatKind::$kind,
)*
_ => return Err(()),
};
Ok(kind)
}};
}
impl FormatKind {
pub fn custom(name: &'static str) -> Self {
FormatKind::Custom(name)
}
pub fn extensions(&self) -> &'static [&'static str] {
format_spec!(impl_formatkind_extensions_body(self))
}
pub fn is_available(&self) -> bool {
format_spec!(impl_formatkind_is_available_body(self))
}
}
impl std::str::FromStr for FormatKind {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
let lower = s.to_ascii_lowercase();
if let Some(rest) = lower.strip_prefix("custom:") {
let leaked: &'static str = Box::leak(rest.to_string().into_boxed_str());
return Ok(FormatKind::Custom(leaked));
}
format_spec!(impl_formatkind_from_str_body(lower))
}
}
#[derive(Debug, Error)]
pub enum FormatError {
#[error("Unknown format: {0}")]
UnknownFormat(FormatKind),
#[error("No format matched the input")]
NoFormatMatched,
#[error("Format '{0}' is not enabled. Enable the corresponding feature.")]
NotEnabled(FormatKind),
#[error("I/O error: {0}")]
Io(#[from] std::io::Error),
#[error("Serde error: {0}")]
Serde(Box<dyn std::error::Error + Send + Sync>),
#[error("Format error: {0}")]
Other(Box<dyn std::error::Error + Send + Sync>),
}
macro_rules! impl_deserialize_body {
($bytes:ident, $kind:ident
$(($cat:ident, $fmt_kind:ident, $feat:literal, $module:ident,
$display:literal, [$($ext:literal),*], [$($alias:literal),*]))*
) => {{
match $kind {
$(
#[cfg(feature = $feat)]
FormatKind::$fmt_kind => $module::deserialize($bytes),
)*
#[allow(unreachable_patterns)]
_ => Err(FormatError::NotEnabled($kind)),
}
}};
}
macro_rules! impl_serialize_body {
($value:ident, $kind:ident
$(($cat:ident, $fmt_kind:ident, $feat:literal, $module:ident,
$display:literal, [$($ext:literal),*], [$($alias:literal),*]))*
) => {{
match $kind {
$(
#[cfg(feature = $feat)]
FormatKind::$fmt_kind => $module::serialize($value),
)*
#[allow(unreachable_patterns)]
_ => Err(FormatError::NotEnabled($kind)),
}
}};
}
pub fn deserialize<T: DeserializeOwned>(kind: FormatKind, bytes: &[u8]) -> Result<T, FormatError> {
let _ = bytes;
format_spec!(impl_deserialize_body(bytes, kind))
}
pub fn serialize<T: Serialize>(kind: FormatKind, value: &T) -> Result<Vec<u8>, FormatError> {
let _ = value;
format_spec!(impl_serialize_body(value, kind))
}
pub fn deserialize_from_reader<T: DeserializeOwned>(
kind: FormatKind,
reader: &mut dyn Read,
) -> Result<T, FormatError> {
let mut bytes = Vec::new();
reader.read_to_end(&mut bytes)?;
deserialize(kind, &bytes)
}
macro_rules! define_stream_deserialize_fn_read {
(
$(#[$meta:meta])*
[$cfg_feat:literal]
$module:ident
) => {
paste! {
$(#[$meta])*
#[cfg(feature = $cfg_feat)]
pub fn [<deserialize_ $module _stream>]<T, R>(
reader: R,
) -> impl Iterator<Item = Result<T, FormatError>>
where
T: DeserializeOwned,
R: Read,
{
$module::stream_deserialize(reader)
}
}
};
}
macro_rules! define_stream_deserialize_fn_read_static {
(
$(#[$meta:meta])*
[$cfg_feat:literal]
$module:ident
) => {
paste! {
$(#[$meta])*
#[cfg(feature = $cfg_feat)]
pub fn [<deserialize_ $module _stream>]<T, R>(
reader: R,
) -> impl Iterator<Item = Result<T, FormatError>>
where
T: DeserializeOwned,
R: Read + 'static,
{
$module::stream_deserialize(reader)
}
}
};
}
define_stream_deserialize_fn_read!(
["json"]
json
);
define_stream_deserialize_fn_read!(
["csv"]
csv
);
define_stream_deserialize_fn_read_static!(
["yaml"]
yaml
);
define_stream_deserialize_fn_read!(
["plaintext"]
plaintext
);
#[derive(Default)]
pub struct FormatRegistry {
formats: Vec<FormatKind>,
#[cfg(feature = "custom")]
custom_formats: Vec<CustomFormat>,
}
impl FormatRegistry {
pub fn new() -> Self {
Self {
formats: Vec::new(),
#[cfg(feature = "custom")]
custom_formats: Vec::new(),
}
}
pub fn register(&mut self, kind: FormatKind) {
if !self.formats.contains(&kind) {
self.formats.push(kind);
}
}
pub fn with_format(mut self, kind: FormatKind) -> Self {
self.register(kind);
self
}
#[cfg(feature = "custom")]
pub fn register_custom(&mut self, format: CustomFormat) {
let kind = FormatKind::Custom(format.name);
if !self.formats.contains(&kind) {
self.formats.push(kind);
}
self.custom_formats.push(format);
}
#[cfg(feature = "custom")]
pub fn with_custom_format(mut self, format: CustomFormat) -> Self {
self.register_custom(format);
self
}
pub fn has_format(&self, kind: &FormatKind) -> bool {
self.formats.contains(kind)
}
#[cfg(feature = "custom")]
pub fn get_custom(&self, name: &str) -> Option<&CustomFormat> {
self.custom_formats.iter().find(|f| f.name == name)
}
pub fn kind_for_extension(&self, ext: &str) -> Option<FormatKind> {
let ext_lower = ext.to_ascii_lowercase();
for kind in &self.formats {
if kind
.extensions()
.iter()
.any(|e| e.eq_ignore_ascii_case(&ext_lower))
{
return Some(*kind);
}
}
#[cfg(feature = "custom")]
for custom in &self.custom_formats {
if custom.matches_extension(&ext_lower) {
return Some(FormatKind::Custom(custom.name));
}
}
None
}
pub fn resolve(
&self,
explicit: Option<&FormatKind>,
candidates: &[FormatKind],
) -> Result<FormatKind, FormatError> {
if let Some(k) = explicit {
if self.has_format(k) && k.is_available() {
return Ok(*k);
}
return Err(FormatError::UnknownFormat(*k));
}
for k in candidates {
if self.has_format(k) && k.is_available() {
return Ok(*k);
}
}
Err(FormatError::NoFormatMatched)
}
pub fn formats(&self) -> &[FormatKind] {
&self.formats
}
#[cfg(feature = "custom")]
pub fn custom_formats(&self) -> &[CustomFormat] {
&self.custom_formats
}
pub fn deserialize_value<T: DeserializeOwned>(
&self,
explicit: Option<&FormatKind>,
candidates: &[FormatKind],
bytes: &[u8],
) -> Result<T, FormatError> {
let kind = self.resolve(explicit, candidates)?;
if let FormatKind::Custom(_name) = &kind {
#[cfg(feature = "custom")]
{
let custom = self
.get_custom(_name)
.ok_or_else(|| FormatError::UnknownFormat(kind))?;
return custom.deserialize(bytes);
}
#[cfg(not(feature = "custom"))]
{
return Err(FormatError::NotEnabled(kind));
}
}
deserialize(kind, bytes)
}
pub fn serialize_value<T: Serialize>(
&self,
explicit: Option<&FormatKind>,
candidates: &[FormatKind],
value: &T,
) -> Result<Vec<u8>, FormatError> {
let kind = self.resolve(explicit, candidates)?;
if let FormatKind::Custom(_name) = &kind {
#[cfg(feature = "custom")]
{
let custom = self
.get_custom(_name)
.ok_or_else(|| FormatError::UnknownFormat(kind))?;
return custom.serialize(value);
}
#[cfg(not(feature = "custom"))]
{
return Err(FormatError::NotEnabled(kind));
}
}
serialize(kind, value)
}
pub fn stream_deserialize_into<T>(
&self,
explicit: Option<&FormatKind>,
candidates: &[FormatKind],
reader: Box<dyn Read>,
) -> Result<Box<dyn Iterator<Item = Result<T, FormatError>>>, FormatError>
where
T: DeserializeOwned + 'static,
{
let kind = self.resolve(explicit, candidates)?;
if let FormatKind::Json = kind {
#[cfg(feature = "json")]
{
let iter = crate::format::deserialize_json_stream::<T, _>(reader);
return Ok(Box::new(iter));
}
#[cfg(not(feature = "json"))]
{
return Err(FormatError::NotEnabled(kind));
}
}
if let FormatKind::Csv = kind {
#[cfg(feature = "csv")]
{
let iter = crate::format::deserialize_csv_stream::<T, _>(reader);
return Ok(Box::new(iter));
}
#[cfg(not(feature = "csv"))]
{
return Err(FormatError::NotEnabled(kind));
}
}
if let FormatKind::Yaml = kind {
#[cfg(feature = "yaml")]
{
let iter = crate::format::deserialize_yaml_stream::<T, _>(reader);
return Ok(Box::new(iter));
}
#[cfg(not(feature = "yaml"))]
{
return Err(FormatError::NotEnabled(kind));
}
}
if let FormatKind::Plaintext = kind {
#[cfg(feature = "plaintext")]
{
let iter = crate::format::deserialize_plaintext_stream::<T, _>(reader);
return Ok(Box::new(iter));
}
#[cfg(not(feature = "plaintext"))]
{
return Err(FormatError::NotEnabled(kind));
}
}
if let FormatKind::Custom(name) = kind {
#[cfg(feature = "custom")]
{
let custom = self
.get_custom(name)
.ok_or_else(|| FormatError::UnknownFormat(FormatKind::Custom(name)))?;
if custom.stream_deserialize_fn.is_some() {
let iter = custom.stream_deserialize_values(reader)?.map(|res| {
res.and_then(|value| {
serde_json::from_value::<T>(value)
.map_err(|e| FormatError::Serde(Box::new(e)))
})
});
return Ok(Box::new(iter));
} else {
let mut r = reader;
let mut bytes = Vec::new();
r.read_to_end(&mut bytes)?;
let value = custom.deserialize::<T>(&bytes)?;
return Ok(Box::new(std::iter::once(Ok(value))));
}
}
#[cfg(not(feature = "custom"))]
{
return Err(FormatError::NotEnabled(FormatKind::Custom(name)));
}
}
let mut r = reader;
let mut bytes = Vec::new();
r.read_to_end(&mut bytes)?;
let value = deserialize::<T>(kind, &bytes)?;
Ok(Box::new(std::iter::once(Ok(value))))
}
}
pub fn default_registry() -> FormatRegistry {
let mut registry = FormatRegistry::new();
for_each_enabled_builtin(|k| registry.register(k));
registry
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn default_format_order_is_expected() {
assert_eq!(
DEFAULT_FORMAT_ORDER,
&[
FormatKind::Json,
FormatKind::Yaml,
FormatKind::Toml,
FormatKind::Ini,
FormatKind::Csv,
FormatKind::Xml,
FormatKind::Plaintext,
],
);
}
#[test]
fn structured_text_formats_are_prefix_of_default_order() {
assert_eq!(
STRUCTURED_TEXT_FORMATS,
&DEFAULT_FORMAT_ORDER[..STRUCTURED_TEXT_FORMATS.len()],
);
}
}
#[cfg(feature = "async")]
mod async_format;
#[cfg(feature = "async")]
pub use async_format::*;