use base64::{read::DecoderReader as Base64Decoder, write::EncoderWriter as Base64Encoder};
use flate2::{read::ZlibDecoder, write::ZlibEncoder, Compression};
use objects::{Blueprint, BlueprintBook};
use serde::{Deserialize, Serialize};
use std::io::prelude::*;
use thiserror::Error;
use version_prefix::{VersionPrefixReader, VersionPrefixWriter};
use whitespace_remover::WhitespaceRemover;
pub mod objects;
pub mod version_prefix;
pub mod whitespace_remover;
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum Container {
BlueprintBook(BlueprintBook),
Blueprint(Blueprint),
}
impl From<BlueprintBook> for Container {
fn from(b: BlueprintBook) -> Container {
Container::BlueprintBook(b)
}
}
impl From<Blueprint> for Container {
fn from(b: Blueprint) -> Container {
Container::Blueprint(b)
}
}
impl Container {
pub fn decode<R: Read>(reader: R) -> Result<Self> {
BlueprintCodec::decode(reader)
}
pub fn encode<W: Write>(&self, writer: W) -> Result<()> {
BlueprintCodec::encode(writer, self)
}
}
#[derive(Error, Debug)]
pub enum Error {
#[error("json problem")]
Json(#[from] serde_json::Error),
#[error("failed to write valid utf8")]
Utf8(#[from] std::string::FromUtf8Error),
#[error("io troubles; probably transient")]
Io(#[from] std::io::Error),
#[error("unexpected blueprint string version byte")]
UnknownVersion,
#[error("failed to read any data")]
NoData,
}
pub type Result<T> = std::result::Result<T, Error>;
pub struct BlueprintCodec;
impl BlueprintCodec {
pub fn encode_writer<W, F>(writer: W, inner: F) -> Result<()>
where
W: Write,
F: FnOnce(ZlibEncoder<&mut Base64Encoder<VersionPrefixWriter<W>>>) -> std::io::Result<()>,
{
let mut writer = VersionPrefixWriter::new('0', writer);
let mut writer = Base64Encoder::new(&mut writer, base64::STANDARD);
{
let writer = ZlibEncoder::new(writer.by_ref(), Compression::new(9));
inner(writer)?;
}
writer.finish().map_err(|e| e.into())
}
pub fn encode<W: Write>(writer: W, container: &Container) -> Result<()> {
Self::encode_writer(writer, |writer| {
serde_json::to_writer(writer, container).map_err(|e| e.into())
})
}
pub fn encode_string(container: &Container) -> Result<String> {
let mut out = Vec::new();
Self::encode(&mut out, container)?;
String::from_utf8(out).map_err(|e| e.into())
}
pub fn decode_reader<R, F>(reader: R, inner: F) -> Result<()>
where
R: Read,
F: FnOnce(
ZlibDecoder<Base64Decoder<VersionPrefixReader<WhitespaceRemover<R>>>>,
) -> std::io::Result<()>,
{
let reader = WhitespaceRemover::new(reader);
let mut reader = VersionPrefixReader::new('0', reader);
{
let reader = Base64Decoder::new(reader.by_ref(), base64::STANDARD);
let reader = ZlibDecoder::new(reader);
inner(reader)?;
}
if !reader.had_expected_version().ok_or(Error::NoData)? {
Err(Error::UnknownVersion)?;
}
Ok(())
}
pub fn decode<R: Read>(reader: R) -> Result<Container> {
let mut out = Err(Error::NoData);
Self::decode_reader(reader, |reader| {
out = serde_json::from_reader(reader).map_err(|e| e.into());
Ok(())
})?;
out
}
pub fn decode_string(blueprint: &str) -> Result<Container> {
Self::decode(blueprint.as_bytes())
}
}