use crate::{
text::{ArrayReader, GroupEntry, ObjectReader, Operator, ScalarReader, ValueReader},
Encoding, TextToken,
};
use serde::{
ser::{SerializeMap, SerializeSeq},
Serialize, Serializer,
};
use std::ops::Deref;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct JsonOptions {
pretty: bool,
duplicate_keys: DuplicateKeyMode,
}
impl JsonOptions {
pub fn new() -> Self {
JsonOptions::default()
}
pub fn with_prettyprint(mut self, pretty: bool) -> JsonOptions {
self.pretty = pretty;
self
}
pub fn with_duplicate_keys(mut self, duplicate_keys: DuplicateKeyMode) -> JsonOptions {
self.duplicate_keys = duplicate_keys;
self
}
pub(crate) fn output_len_factor(&self) -> usize {
match (self.pretty, self.duplicate_keys) {
(false, DuplicateKeyMode::Group | DuplicateKeyMode::Preserve) => 10,
(true, DuplicateKeyMode::Group | DuplicateKeyMode::Preserve) => 20,
(false, DuplicateKeyMode::KeyValuePairs) => 15,
(true, DuplicateKeyMode::KeyValuePairs) => 60,
}
}
}
impl Default for JsonOptions {
fn default() -> Self {
Self {
pretty: false,
duplicate_keys: DuplicateKeyMode::Preserve,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DuplicateKeyMode {
Group,
Preserve,
KeyValuePairs,
}
fn writer_json<W, S>(writer: W, pretty: bool, ser: S) -> Result<(), std::io::Error>
where
W: std::io::Write,
S: serde::Serialize,
{
let result = if pretty {
serde_json::to_writer_pretty(writer, &ser)
} else {
serde_json::to_writer(writer, &ser)
};
result.map_err(|e| e.into())
}
fn vec_json<F>(mut out: Vec<u8>, write_fn: F) -> Vec<u8>
where
F: FnOnce(&mut Vec<u8>) -> Result<(), std::io::Error>,
{
if let Err(e) = write_fn(&mut out) {
panic!("failed to serialize json to vector: {}", e)
} else {
out
}
}
fn string_json(json: Vec<u8>) -> String {
unsafe { String::from_utf8_unchecked(json) }
}
pub struct JsonObjectBuilder<'data, 'tokens, E> {
reader: ObjectReader<'data, 'tokens, E>,
options: JsonOptions,
}
impl<'data, 'tokens, E> JsonObjectBuilder<'data, 'tokens, E>
where
E: Encoding + Clone,
{
pub fn with_options(mut self, options: JsonOptions) -> Self {
self.options = options;
self
}
pub fn to_writer<W>(self, writer: W) -> Result<(), std::io::Error>
where
W: std::io::Write,
{
writer_json(writer, self.options.pretty, &self)
}
pub fn to_vec(self) -> Vec<u8> {
let out = Vec::with_capacity(self.reader.tokens_len() * self.options.output_len_factor());
vec_json(out, |x| self.to_writer(x))
}
#[allow(clippy::inherent_to_string)]
pub fn to_string(self) -> String {
string_json(self.to_vec())
}
}
impl<'data, 'tokens, E> ObjectReader<'data, 'tokens, E>
where
E: Encoding + Clone,
{
pub fn json(&self) -> JsonObjectBuilder<'data, 'tokens, E> {
JsonObjectBuilder {
reader: self.clone(),
options: JsonOptions::default(),
}
}
}
pub struct JsonArrayBuilder<'data, 'tokens, E> {
reader: ArrayReader<'data, 'tokens, E>,
options: JsonOptions,
}
impl<'data, 'tokens, E> JsonArrayBuilder<'data, 'tokens, E>
where
E: Encoding + Clone,
{
pub fn with_options(mut self, options: JsonOptions) -> Self {
self.options = options;
self
}
pub fn to_writer<W>(self, writer: W) -> Result<(), std::io::Error>
where
W: std::io::Write,
{
writer_json(writer, self.options.pretty, &self)
}
pub fn to_vec(self) -> Vec<u8> {
let out = Vec::with_capacity(self.reader.tokens_len() * self.options.output_len_factor());
vec_json(out, |x| self.to_writer(x))
}
#[allow(clippy::inherent_to_string)]
pub fn to_string(self) -> String {
string_json(self.to_vec())
}
}
impl<'data, 'tokens, E> ArrayReader<'data, 'tokens, E>
where
E: Encoding + Clone,
{
pub fn json(&self) -> JsonArrayBuilder<'data, 'tokens, E> {
JsonArrayBuilder {
reader: self.clone(),
options: JsonOptions::default(),
}
}
}
pub struct JsonValueBuilder<'data, 'tokens, E> {
reader: ValueReader<'data, 'tokens, E>,
options: JsonOptions,
}
impl<'data, 'tokens, E> JsonValueBuilder<'data, 'tokens, E>
where
E: Encoding + Clone,
{
pub fn with_options(mut self, options: JsonOptions) -> Self {
self.options = options;
self
}
pub fn to_writer<W>(self, writer: W) -> Result<(), std::io::Error>
where
W: std::io::Write,
{
writer_json(writer, self.options.pretty, &self)
}
pub fn to_vec(self) -> Vec<u8> {
let out = Vec::with_capacity(self.reader.tokens_len() * self.options.output_len_factor());
vec_json(out, |x| self.to_writer(x))
}
#[allow(clippy::inherent_to_string)]
pub fn to_string(self) -> String {
string_json(self.to_vec())
}
}
impl<'data, 'tokens, E> ValueReader<'data, 'tokens, E>
where
E: Encoding + Clone,
{
pub fn json(&self) -> JsonValueBuilder<'data, 'tokens, E> {
JsonValueBuilder {
reader: self.clone(),
options: JsonOptions::default(),
}
}
}
fn serialize_scalar<E, S>(reader: &ValueReader<E>, s: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
E: Encoding + Clone,
{
let scalar = reader.read_scalar().unwrap();
if let Ok(x) = scalar.to_bool() {
return s.serialize_bool(x);
}
let signed = scalar.to_i64();
let unsigned = scalar.to_u64();
let float = scalar.to_f64();
match (signed, unsigned, float) {
(Ok(x), _, Ok(_)) => s.serialize_i64(x),
(_, Ok(x), Ok(_)) => s.serialize_u64(x),
(_, _, Ok(f)) => s.serialize_f64(f),
_ => s.serialize_str(reader.read_str().unwrap().deref()),
}
}
fn serialize_parameter<S>(body: &str, defined: bool, s: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut result = String::with_capacity(body.len() + 3);
result.push('[');
if !defined {
result.push('!');
}
result.push_str(body.as_ref());
result.push(']');
s.serialize_str(&result)
}
impl<'data, 'tokens, E> Serialize for JsonValueBuilder<'data, 'tokens, E>
where
E: Encoding + Clone,
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match self.reader.token() {
TextToken::Quoted(_) | TextToken::Unquoted(_) => {
serialize_scalar(&self.reader, serializer)
}
TextToken::Array { .. } => {
let array_reader = self.reader.read_array().unwrap();
array_reader
.json()
.with_options(self.options)
.serialize(serializer)
}
TextToken::Object { .. } => {
let object_reader = self.reader.read_object().unwrap();
object_reader
.json()
.with_options(self.options)
.serialize(serializer)
}
TextToken::Header(_) => {
let arr = self.reader.read_array().unwrap();
let mut values = arr.values();
let key_reader = values.next().unwrap();
let value_reader = values.next().unwrap();
let mut map = serializer.serialize_map(None)?;
map.serialize_entry(
&key_reader.read_str().unwrap(),
&value_reader.json().with_options(self.options),
)?;
map.end()
}
TextToken::End(_)
| TextToken::Operator(_)
| TextToken::Parameter(_)
| TextToken::UndefinedParameter(_)
| TextToken::MixedContainer => serializer.serialize_none(),
}
}
}
impl<'data, 'tokens, E> Serialize for JsonObjectBuilder<'data, 'tokens, E>
where
E: Encoding + Clone,
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match self.options.duplicate_keys {
DuplicateKeyMode::Group => {
let mut field_groups = self.reader.field_groups();
let mut map = serializer.serialize_map(None)?;
for (key, group) in field_groups.by_ref() {
match group {
GroupEntry::One((op, val)) => {
let v = OperatorValue {
operator: op,
value: val,
options: self.options,
};
map.serialize_entry(&KeyScalarWrapper { reader: key }, &v)?;
}
GroupEntry::Multiple(values) => {
let values: Vec<_> = values
.iter()
.map(|(op, val)| OperatorValue {
operator: *op,
value: val.clone(),
options: self.options,
})
.collect();
map.serialize_entry(&KeyScalarWrapper { reader: key }, &values)?;
}
}
}
let rest = field_groups.remainder();
if !rest.is_empty() {
map.serialize_entry(
"remainder",
&InnerSerArray {
reader: rest,
options: self.options,
},
)?;
}
map.end()
}
DuplicateKeyMode::Preserve => {
let mut map = serializer.serialize_map(None)?;
let mut fields = self.reader.fields();
for (key, op, val) in fields.by_ref() {
let v = OperatorValue {
operator: op,
value: val,
options: self.options,
};
map.serialize_entry(&KeyScalarWrapper { reader: key }, &v)?;
}
let remainder = fields.remainder();
if !remainder.is_empty() {
map.serialize_entry(
"remainder",
&InnerSerArray {
reader: remainder,
options: self.options,
},
)?;
}
map.end()
}
DuplicateKeyMode::KeyValuePairs => {
let mut map = serializer.serialize_map(None)?;
map.serialize_entry("type", "obj")?;
map.serialize_entry(
"val",
&SerTapeTyped {
reader: self.reader.clone(),
options: self.options,
},
)?;
map.end()
}
}
}
}
pub(crate) struct OperatorValue<'data, 'tokens, E> {
operator: Option<Operator>,
value: ValueReader<'data, 'tokens, E>,
options: JsonOptions,
}
impl<'data, 'tokens, E> Serialize for OperatorValue<'data, 'tokens, E>
where
E: Encoding + Clone,
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
if let Some(op) = self.operator {
let mut map = serializer.serialize_map(None)?;
let reader = &self.value;
map.serialize_entry(op.name(), &reader.json().with_options(self.options))?;
map.end()
} else {
self.value
.json()
.with_options(self.options)
.serialize(serializer)
}
}
}
pub(crate) struct InnerSerArray<'data, 'tokens, E> {
reader: ArrayReader<'data, 'tokens, E>,
options: JsonOptions,
}
impl<'data, 'tokens, E> Serialize for InnerSerArray<'data, 'tokens, E>
where
E: Encoding + Clone,
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut seq = serializer.serialize_seq(None)?;
let mut iter = self.reader.values();
let mut window = [iter.next(), iter.next(), iter.next()];
loop {
let first_val = match &window[0] {
Some(x) => x,
None => break,
};
if first_val.token() == &TextToken::MixedContainer {
window.swap(1, 0);
window.swap(2, 1);
window[2] = iter.next();
continue;
}
if let Some(op_reader) = &window[1] {
if let TextToken::Operator(op) = op_reader.token() {
if let Some(value) = &window[2] {
seq.serialize_element(&SingleObject {
key: first_val.clone(),
op: *op,
value: value.clone(),
options: self.options,
})?;
window = [iter.next(), iter.next(), iter.next()];
continue;
}
}
}
let v = OperatorValue {
operator: None,
value: first_val.clone(),
options: self.options,
};
seq.serialize_element(&v)?;
window.swap(1, 0);
window.swap(2, 1);
window[2] = iter.next();
}
seq.end()
}
}
impl<'data, 'tokens, E> Serialize for JsonArrayBuilder<'data, 'tokens, E>
where
E: Encoding + Clone,
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let inner = InnerSerArray {
reader: self.reader.clone(),
options: self.options,
};
if self.options.duplicate_keys != DuplicateKeyMode::KeyValuePairs {
inner.serialize(serializer)
} else {
let mut map = serializer.serialize_map(None)?;
map.serialize_entry("type", "array")?;
map.serialize_entry("val", &inner)?;
map.end()
}
}
}
pub(crate) struct SingleObject<'data, 'tokens, E> {
key: ValueReader<'data, 'tokens, E>,
op: Operator,
value: ValueReader<'data, 'tokens, E>,
options: JsonOptions,
}
impl<'data, 'tokens, E> Serialize for SingleObject<'data, 'tokens, E>
where
E: Encoding + Clone,
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut map = serializer.serialize_map(None)?;
let op = if self.op == Operator::Equal {
None
} else {
Some(self.op)
};
let value = OperatorValue {
operator: op,
value: self.value.clone(),
options: self.options,
};
if let Ok(x) = self.key.read_str() {
map.serialize_key(&x)?;
} else {
map.serialize_key("__invalid_key")?;
}
map.serialize_value(&value)?;
map.end()
}
}
pub(crate) struct SerTapeTyped<'data, 'tokens, E> {
reader: ObjectReader<'data, 'tokens, E>,
options: JsonOptions,
}
impl<'data, 'tokens, E> Serialize for SerTapeTyped<'data, 'tokens, E>
where
E: Encoding + Clone,
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut seq = serializer.serialize_seq(None)?;
let mut fields = self.reader.fields();
for (key, op, val) in fields.by_ref() {
let v = OperatorValue {
operator: op,
value: val,
options: self.options,
};
seq.serialize_element(&(KeyScalarWrapper { reader: key }, &v))?;
}
let remainder = fields.remainder();
if !remainder.is_empty() {
let trailer_array = InnerSerArray {
reader: remainder,
options: self.options,
};
seq.serialize_element(&trailer_array)?;
}
seq.end()
}
}
pub(crate) struct KeyScalarWrapper<'data, E> {
reader: ScalarReader<'data, E>,
}
impl<'data, E> Serialize for KeyScalarWrapper<'data, E>
where
E: Encoding + Clone,
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let body = self.reader.read_str();
match self.reader.token() {
TextToken::Parameter(_) => serialize_parameter(&body, true, serializer),
TextToken::UndefinedParameter(_) => serialize_parameter(&body, false, serializer),
_ => serializer.serialize_str(&body),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{TextTape, Windows1252Encoding};
fn serialize_with(data: &[u8], options: JsonOptions) -> String {
let tape = TextTape::from_slice(data).unwrap();
let reader = tape.windows1252_reader();
reader.json().with_options(options).to_string()
}
fn serialize(data: &[u8]) -> String {
serialize_with(data, JsonOptions::default())
}
#[test]
fn test_serialize_to_json() {
let json = serialize(b"foo=bar");
assert_eq!(&json, r#"{"foo":"bar"}"#);
}
#[test]
fn test_simple_types() {
let json = serialize(b"foo=bar num=1 bool=no bool2=yes pi=3.14");
let expected = r#"{"foo":"bar","num":1,"bool":false,"bool2":true,"pi":3.14}"#;
assert_eq!(&json, expected);
}
#[test]
fn test_object() {
let json = serialize(b"foo={prop=a bar={num=1}}");
let expected = r#"{"foo":{"prop":"a","bar":{"num":1}}}"#;
assert_eq!(&json, expected);
}
#[test]
fn test_array() {
let json = serialize(b"nums={1 2 3 4}");
let expected = r#"{"nums":[1,2,3,4]}"#;
assert_eq!(&json, expected);
}
#[test]
fn test_duplicate_fields() {
let json = serialize(b"core=AAA core=BBB");
let expected = r#"{"core":"AAA","core":"BBB"}"#;
assert_eq!(&json, expected);
}
#[test]
fn test_duplicate_fields_grouped() {
let json = serialize_with(
b"core=AAA core=BBB",
JsonOptions {
duplicate_keys: DuplicateKeyMode::Group,
..JsonOptions::default()
},
);
let expected = r#"{"core":["AAA","BBB"]}"#;
assert_eq!(&json, expected);
}
#[test]
fn test_duplicate_fields_typed() {
let json = serialize_with(
b"core=AAA core=BBB",
JsonOptions {
duplicate_keys: DuplicateKeyMode::KeyValuePairs,
..JsonOptions::default()
},
);
let expected = r#"{"type":"obj","val":[["core","AAA"],["core","BBB"]]}"#;
assert_eq!(&json, expected);
}
#[test]
fn test_header() {
let json = serialize(b"color = rgb { 100 200 150 }");
let expected = r#"{"color":{"rgb":[100,200,150]}}"#;
assert_eq!(&json, expected);
}
#[test]
fn test_large_numbers() {
let json = serialize(b"identity = 18446744073709547616");
let expected = r#"{"identity":"18446744073709547616"}"#;
assert_eq!(&json, expected);
}
#[test]
fn test_large_negative_numbers() {
let json = serialize(b"identity = -90071992547409097");
let expected = r#"{"identity":"-90071992547409097"}"#;
assert_eq!(&json, expected);
}
#[test]
fn test_object_pretty() {
let json = serialize_with(
b"foo={prop=a bar={num=1}}",
JsonOptions {
pretty: true,
..JsonOptions::default()
},
);
let expected = r#"{
"foo": {
"prop": "a",
"bar": {
"num": 1
}
}
}"#;
assert_eq!(&json, expected);
}
#[test]
fn test_array_typed() {
let json = serialize_with(
b"nums={1 2}",
JsonOptions {
duplicate_keys: DuplicateKeyMode::KeyValuePairs,
..JsonOptions::default()
},
);
let expected = r#"{"type":"obj","val":[["nums",{"type":"array","val":[1,2]}]]}"#;
assert_eq!(&json, expected);
}
#[test]
fn test_mixed_container_1() {
let json = serialize(b"area = { color = { 10 } 1 2 }");
let expected = r#"{"area":{"color":[10],"remainder":[1,2]}}"#;
assert_eq!(&json, expected);
}
#[test]
fn test_mixed_container_2() {
let json = serialize_with(
b"area = { color = { 10 } 1 2 }",
JsonOptions {
duplicate_keys: DuplicateKeyMode::Group,
..JsonOptions::default()
},
);
let expected = r#"{"area":{"color":[10],"remainder":[1,2]}}"#;
assert_eq!(&json, expected);
}
#[test]
fn test_mixed_container_3() {
let json = serialize_with(
b"area = { color = { 10 } 1 2 }",
JsonOptions {
duplicate_keys: DuplicateKeyMode::KeyValuePairs,
..JsonOptions::default()
},
);
let expected = r#"{"type":"obj","val":[["area",{"type":"obj","val":[["color",{"type":"array","val":[10]}],[1,2]]}]]}"#;
assert_eq!(&json, expected);
}
#[test]
fn test_mixed_container_4() {
let json = serialize(b"levels={ 10 0=2 1=2 }");
let expected = r#"{"levels":[10,{"0":2},{"1":2}]}"#;
assert_eq!(&json, expected);
}
#[test]
fn test_mixed_container_5() {
let json = serialize(b"mixed={ a=b 10 c=d 20 }");
let expected = r#"{"mixed":{"a":"b","remainder":[10,{"c":"d"},20]}}"#;
assert_eq!(&json, expected);
}
#[test]
fn test_mixed_container_10() {
let json = serialize(
br"on_actions = {
faith_holy_order_land_acquisition_pulse
delay = { days = { 5 10 }}
faith_heresy_events_pulse
delay = { days = { 15 20 }}
faith_fervor_events_pulse
}",
);
let expected = r#"{"on_actions":["faith_holy_order_land_acquisition_pulse",{"delay":{"days":[5,10]}},"faith_heresy_events_pulse",{"delay":{"days":[15,20]}},"faith_fervor_events_pulse"]}"#;
assert_eq!(&json, expected);
}
#[test]
fn test_parameter_definitions_typed() {
let json = serialize_with(
b"generate_advisor = { [[scaled_skill] a=b ] [[!scaled_skill] c=d ] }",
JsonOptions {
duplicate_keys: DuplicateKeyMode::KeyValuePairs,
..JsonOptions::default()
},
);
let expected = r#"{"type":"obj","val":[["generate_advisor",{"type":"obj","val":[["[scaled_skill]",{"type":"obj","val":[["a","b"]]}],["[!scaled_skill]",{"type":"obj","val":[["c","d"]]}]]}]]}"#;
assert_eq!(&json, expected);
}
#[test]
fn test_parameter_definition_value_typed() {
let json = serialize_with(
b"foo = { [[add] $add$]}",
JsonOptions {
duplicate_keys: DuplicateKeyMode::KeyValuePairs,
..JsonOptions::default()
},
);
let expected = r#"{"type":"obj","val":[["foo",{"type":"obj","val":[["[add]","$add$"]]}]]}"#;
assert_eq!(&json, expected);
}
#[test]
fn test_subobject() {
let tape = TextTape::from_slice(b"bar={num=1}").unwrap();
let reader = tape.windows1252_reader();
let mut fields = reader.fields();
let (_key, _op, value) = fields.next().unwrap();
let actual = value.json().to_string();
let expected = r#"{"num":1}"#;
assert_eq!(&actual, expected);
}
#[test]
fn test_array_direct() {
let tape = TextTape::from_slice(b"nums={1 2 3 4}").unwrap();
let reader = tape.windows1252_reader();
let mut fields = reader.fields();
let (_key, _op, value) = fields.next().unwrap();
let array = value.read_array().unwrap();
let actual = array.json().to_string();
let expected = r#"[1,2,3,4]"#;
assert_eq!(&actual, expected);
}
#[test]
fn test_value_direct() {
let tape = TextTape::from_slice(b"core=1").unwrap();
let reader = tape.windows1252_reader();
let mut fields = reader.fields();
let (_key, _op, value) = fields.next().unwrap();
let actual = value.json().to_string();
let expected = r#"1"#;
assert_eq!(&actual, expected);
}
#[test]
fn test_builder_serialization() {
#[derive(Serialize)]
struct MyStruct<'a, 'b> {
bar: JsonValueBuilder<'a, 'b, Windows1252Encoding>,
qux: JsonValueBuilder<'a, 'b, Windows1252Encoding>,
}
let tape = TextTape::from_slice(b"bar={num=1} qux={num=2}").unwrap();
let reader = tape.windows1252_reader();
let mut fields = reader.fields();
let (_key, _op, value) = fields.next().unwrap();
let bar = value.json();
let (_key, _op, value) = fields.next().unwrap();
let qux = value.json();
let actual = serde_json::to_string(&MyStruct { bar, qux }).unwrap();
let expected = r#"{"bar":{"num":1},"qux":{"num":2}}"#;
assert_eq!(&actual, expected);
}
}