#![deny(
// Enforce some additional strictness on unsafe code.
unsafe_op_in_unsafe_fn,
clippy::undocumented_unsafe_blocks,
// Deny a number of `as` casts in favor of safer alternatives.
clippy::as_underscore,
clippy::ptr_as_ptr,
clippy::cast_lossless,
clippy::cast_possible_truncation,
clippy::checked_conversions,
clippy::unnecessary_cast,
// More general style-type things.
clippy::from_over_into,
clippy::needless_raw_string_hashes,
clippy::semicolon_if_nothing_returned,
)]
#![warn(
// Print macros can panic, and should only be for temporary debugging.
clippy::print_stderr,
clippy::print_stdout,
// The following macros represent incomplete implementation work.
clippy::todo,
clippy::unimplemented,
// Style-type things that might not need an _immediate_ fix.
clippy::doc_markdown,
clippy::similar_names,
)]
use std::fmt;
use std::io::{self, Read, Write};
use serde::{de, ser};
mod detect;
mod error;
mod input;
mod json;
mod msgpack;
mod toml;
mod transcode;
mod yaml;
pub use error::{Error, Result};
pub fn translate_slice<W>(input: &[u8], from: Option<Format>, to: Format, output: W) -> Result<()>
where
W: Write,
{
Translator::new(output, to).translate_slice(input, from)
}
pub fn translate_reader<R, W>(input: R, from: Option<Format>, to: Format, output: W) -> Result<()>
where
R: Read,
W: Write,
{
Translator::new(output, to).translate_reader(input, from)
}
pub struct Translator<W>(Dispatcher<W>)
where
W: Write;
impl<W> Translator<W>
where
W: Write,
{
pub fn new(output: W, to: Format) -> Translator<W> {
Translator(Dispatcher::new(output, to))
}
pub fn translate_slice(&mut self, input: &[u8], from: Option<Format>) -> Result<()> {
self.translate(input::Handle::from_slice(input), from)
}
pub fn translate_reader<R>(&mut self, input: R, from: Option<Format>) -> Result<()>
where
R: Read,
{
self.translate(input::Handle::from_reader(input), from)
}
fn translate(&mut self, mut input: input::Handle<'_>, from: Option<Format>) -> Result<()> {
let from = match from {
Some(format) => format,
None => match detect::detect_format(&mut input)? {
Some(format) => format,
None => return Err("unable to detect input format".into()),
},
};
match from {
Format::Json => json::transcode(input, &mut self.0),
Format::Msgpack => msgpack::transcode(input, &mut self.0),
Format::Toml => toml::transcode(input, &mut self.0),
Format::Yaml => yaml::transcode(input, &mut self.0),
}
}
pub fn flush(&mut self) -> io::Result<()> {
(&mut self.0).flush()
}
}
trait Output {
fn transcode_from<'de, D, E>(&mut self, de: D) -> Result<()>
where
D: de::Deserializer<'de, Error = E>,
E: de::Error + Send + Sync + 'static;
fn transcode_value<S>(&mut self, value: S) -> Result<()>
where
S: ser::Serialize;
fn flush(&mut self) -> io::Result<()>;
}
enum Dispatcher<W>
where
W: Write,
{
Json(json::Output<W>),
Msgpack(msgpack::Output<W>),
Toml(toml::Output<W>),
Yaml(yaml::Output<W>),
}
impl<W> Dispatcher<W>
where
W: Write,
{
fn new(writer: W, to: Format) -> Dispatcher<W> {
match to {
Format::Json => Dispatcher::Json(json::Output::new(writer)),
Format::Msgpack => Dispatcher::Msgpack(msgpack::Output::new(writer)),
Format::Toml => Dispatcher::Toml(toml::Output::new(writer)),
Format::Yaml => Dispatcher::Yaml(yaml::Output::new(writer)),
}
}
}
impl<W> Output for &mut Dispatcher<W>
where
W: Write,
{
fn transcode_from<'de, D, E>(&mut self, de: D) -> Result<()>
where
D: de::Deserializer<'de, Error = E>,
E: de::Error + Send + Sync + 'static,
{
match self {
Dispatcher::Json(output) => output.transcode_from(de),
Dispatcher::Msgpack(output) => output.transcode_from(de),
Dispatcher::Toml(output) => output.transcode_from(de),
Dispatcher::Yaml(output) => output.transcode_from(de),
}
}
fn transcode_value<S>(&mut self, value: S) -> Result<()>
where
S: ser::Serialize,
{
match self {
Dispatcher::Json(output) => output.transcode_value(value),
Dispatcher::Msgpack(output) => output.transcode_value(value),
Dispatcher::Toml(output) => output.transcode_value(value),
Dispatcher::Yaml(output) => output.transcode_value(value),
}
}
fn flush(&mut self) -> io::Result<()> {
match self {
Dispatcher::Json(output) => output.flush(),
Dispatcher::Msgpack(output) => output.flush(),
Dispatcher::Toml(output) => output.flush(),
Dispatcher::Yaml(output) => output.flush(),
}
}
}
#[derive(Copy, Clone)]
#[non_exhaustive]
pub enum Format {
Json,
Msgpack,
Toml,
Yaml,
}
impl fmt::Display for Format {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(match self {
Self::Json => "JSON",
Self::Msgpack => "MessagePack",
Self::Toml => "TOML",
Self::Yaml => "YAML",
})
}
}