factorio_blueprint/
lib.rs1use base64::{read::DecoderReader as Base64Decoder, write::EncoderWriter as Base64Encoder};
2use flate2::{read::ZlibDecoder, write::ZlibEncoder, Compression};
3use objects::{Blueprint, BlueprintBook};
4use serde::{Deserialize, Serialize};
5use std::io::prelude::*;
6use thiserror::Error;
7use version_prefix::{VersionPrefixReader, VersionPrefixWriter};
8use whitespace_remover::WhitespaceRemover;
9
10pub mod objects;
11pub mod version_prefix;
12pub mod whitespace_remover;
13
14#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)]
17#[serde(rename_all = "snake_case")]
18pub enum Container {
19 BlueprintBook(BlueprintBook),
20 Blueprint(Blueprint),
21}
22
23impl From<BlueprintBook> for Container {
24 fn from(b: BlueprintBook) -> Container {
25 Container::BlueprintBook(b)
26 }
27}
28
29impl From<Blueprint> for Container {
30 fn from(b: Blueprint) -> Container {
31 Container::Blueprint(b)
32 }
33}
34
35impl Container {
36 pub fn decode<R: Read>(reader: R) -> Result<Self> {
37 BlueprintCodec::decode(reader)
38 }
39
40 pub fn encode<W: Write>(&self, writer: W) -> Result<()> {
41 BlueprintCodec::encode(writer, self)
42 }
43}
44
45#[derive(Error, Debug)]
46pub enum Error {
47 #[error("json problem")]
48 Json(#[from] serde_json::Error),
49 #[error("failed to write valid utf8")]
50 Utf8(#[from] std::string::FromUtf8Error),
51 #[error("io troubles; probably transient")]
52 Io(#[from] std::io::Error),
53 #[error("unexpected blueprint string version byte")]
54 UnknownVersion,
55 #[error("failed to read any data")]
56 NoData,
57}
58
59pub type Result<T> = std::result::Result<T, Error>;
60
61pub struct BlueprintCodec;
63
64impl BlueprintCodec {
65 pub fn encode_writer<W, F>(writer: W, inner: F) -> Result<()>
70 where
71 W: Write,
72 F: FnOnce(ZlibEncoder<&mut Base64Encoder<VersionPrefixWriter<W>>>) -> std::io::Result<()>,
73 {
74 let mut writer = VersionPrefixWriter::new('0', writer);
76 let mut writer = Base64Encoder::new(&mut writer, base64::STANDARD);
78 {
81 let writer = ZlibEncoder::new(writer.by_ref(), Compression::new(9));
83 inner(writer)?;
85 }
86 writer.finish().map_err(|e| e.into())
87 }
88
89 pub fn encode<W: Write>(writer: W, container: &Container) -> Result<()> {
91 Self::encode_writer(writer, |writer| {
92 serde_json::to_writer(writer, container).map_err(|e| e.into())
94 })
95 }
96
97 pub fn encode_string(container: &Container) -> Result<String> {
99 let mut out = Vec::new();
100 Self::encode(&mut out, container)?;
101 String::from_utf8(out).map_err(|e| e.into())
102 }
103
104 pub fn decode_reader<R, F>(reader: R, inner: F) -> Result<()>
109 where
110 R: Read,
111 F: FnOnce(
112 ZlibDecoder<Base64Decoder<VersionPrefixReader<WhitespaceRemover<R>>>>,
113 ) -> std::io::Result<()>,
114 {
115 let reader = WhitespaceRemover::new(reader);
119 let mut reader = VersionPrefixReader::new('0', reader);
121 {
124 let reader = Base64Decoder::new(reader.by_ref(), base64::STANDARD);
126 let reader = ZlibDecoder::new(reader);
128 inner(reader)?;
130 }
131 if !reader.had_expected_version().ok_or(Error::NoData)? {
132 Err(Error::UnknownVersion)?;
133 }
134 Ok(())
135 }
136
137 pub fn decode<R: Read>(reader: R) -> Result<Container> {
139 let mut out = Err(Error::NoData);
140 Self::decode_reader(reader, |reader| {
141 out = serde_json::from_reader(reader).map_err(|e| e.into());
142 Ok(())
143 })?;
144 out
145 }
146
147 pub fn decode_string(blueprint: &str) -> Result<Container> {
149 Self::decode(blueprint.as_bytes())
150 }
151}