use std::borrow::Cow;
use std::error;
use std::fmt;
use std::io::{self, Write};
use std::str;
use serde::{Deserialize, de, ser};
use crate::input::{self, Ref};
pub(crate) fn input_matches(mut input: Ref) -> io::Result<bool> {
let input_buf = match input {
Ref::Slice(b) => b,
Ref::Reader(_) => {
const SIZE_CUTOFF: usize = 2 * 1024_usize.pow(2);
let prefix = input.prefix(SIZE_CUTOFF)?;
if prefix.len() >= SIZE_CUTOFF {
return Ok(false);
}
prefix
}
};
let Ok(input_str) = str::from_utf8(input_buf) else {
return Ok(false);
};
let Ok(de) = ::toml::Deserializer::parse(input_str) else {
return Ok(false);
};
Ok(de::IgnoredAny::deserialize(de).is_ok())
}
pub(crate) fn transcode<O>(input: input::Handle, mut output: O) -> crate::Result<()>
where
O: crate::Output,
{
let input: Cow<'_, [u8]> = input.try_into()?;
let de = ::toml::Deserializer::parse(str::from_utf8(&input)?)?;
output.transcode_from(de)
}
pub(crate) struct Output<W: Write> {
w: W,
used: bool,
}
impl<W: Write> Output<W> {
pub(crate) fn new(w: W) -> Output<W> {
Output { w, used: false }
}
fn ensure_one_use(&mut self) -> crate::Result<()> {
if self.used {
return Err(TomlOutputError::MultiDocument.into());
}
self.used = true;
Ok(())
}
fn output_value(&mut self, value: &::toml::Value) -> crate::Result<()> {
if let toml::Value::Table(table) = value {
let output = ::toml::to_string_pretty(table)?;
self.w.write_all(output.as_bytes())?;
Ok(())
} else {
Err(TomlOutputError::NonTableRoot.into())
}
}
}
impl<W: Write> crate::Output for Output<W> {
fn transcode_from<'de, D, E>(&mut self, de: D) -> crate::Result<()>
where
D: de::Deserializer<'de, Error = E>,
E: de::Error + Send + Sync + 'static,
{
self.ensure_one_use()?;
let value = ::toml::Value::deserialize(de)?;
self.output_value(&value)
}
fn transcode_value<S>(&mut self, value: S) -> crate::Result<()>
where
S: ser::Serialize,
{
self.ensure_one_use()?;
let value = ::toml::Value::try_from(value)?;
self.output_value(&value)
}
fn flush(&mut self) -> io::Result<()> {
self.w.flush()
}
}
#[derive(Debug)]
enum TomlOutputError {
NonTableRoot,
MultiDocument,
}
impl error::Error for TomlOutputError {}
impl fmt::Display for TomlOutputError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
TomlOutputError::NonTableRoot => f.write_str("root of TOML output must be a table"),
TomlOutputError::MultiDocument => {
f.write_str("TOML does not support multi-document output")
}
}
}
}