1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
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())
}
}