use serde::ser::{Serialize, SerializeMap, SerializeSeq, SerializeStruct, Serializer};
use std::{borrow::Cow, collections::BTreeMap};
#[derive(Debug, Clone, PartialEq)]
pub struct YsonValue {
pub attributes: Option<BTreeMap<Vec<u8>, YsonValue>>,
pub node: YsonNode,
}
impl YsonValue {
#[must_use]
pub fn as_str(&self) -> Option<&str> {
if let YsonNode::String(bytes) = &self.node {
std::str::from_utf8(bytes).ok()
} else {
None
}
}
#[must_use]
pub fn as_i64(&self) -> Option<i64> {
if let YsonNode::Int64(v) = self.node {
Some(v)
} else {
None
}
}
#[must_use]
pub fn attr(&self, key: &str) -> Option<&YsonValue> {
self.attributes.as_ref()?.get(key.as_bytes())
}
}
impl<'a> std::ops::Index<&'a str> for YsonValue {
type Output = YsonValue;
fn index(&self, key: &'a str) -> &Self::Output {
if let Some(attr_name) = key.strip_prefix('@') {
return self
.attributes
.as_ref()
.and_then(|a| a.get(attr_name.as_bytes()))
.expect("Attribute not found");
}
if let YsonNode::Map(m) = &self.node {
return m.get(key.as_bytes()).expect("Key not found in map");
}
panic!("Value is not a map");
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum YsonNode {
Entity,
Boolean(bool),
Int64(i64),
Uint64(u64),
Double(f64),
String(Vec<u8>),
List(Vec<YsonValue>),
Map(BTreeMap<Vec<u8>, YsonValue>),
}
struct ByteString<'a>(&'a [u8]);
impl Serialize for ByteString<'_> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
match std::str::from_utf8(self.0) {
Ok(s) => serializer.serialize_str(s),
Err(_) => serializer.serialize_bytes(self.0),
}
}
}
struct ByteKeyedMap<'a>(&'a BTreeMap<Vec<u8>, YsonValue>);
impl Serialize for ByteKeyedMap<'_> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut map = serializer.serialize_map(Some(self.0.len()))?;
for (key, value) in self.0 {
map.serialize_entry(&ByteString(key), value)?;
}
map.end()
}
}
impl Serialize for YsonNode {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
match self {
YsonNode::Entity => serializer.serialize_unit(),
YsonNode::Boolean(v) => serializer.serialize_bool(*v),
YsonNode::Int64(v) => serializer.serialize_i64(*v),
YsonNode::Uint64(v) => serializer.serialize_u64(*v),
YsonNode::Double(v) => serializer.serialize_f64(*v),
YsonNode::String(bytes) => ByteString(bytes).serialize(serializer),
YsonNode::List(items) => {
let mut seq = serializer.serialize_seq(Some(items.len()))?;
for item in items {
seq.serialize_element(item)?;
}
seq.end()
}
YsonNode::Map(entries) => ByteKeyedMap(entries).serialize(serializer),
}
}
}
impl Serialize for YsonValue {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
match &self.attributes {
Some(attributes) if !attributes.is_empty() => {
let mut state = serializer.serialize_struct("$__yson_attributes", 2)?;
state.serialize_field("$attributes", &ByteKeyedMap(attributes))?;
state.serialize_field("$value", &self.node)?;
state.end()
}
_ => self.node.serialize(serializer),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum Token<'a> {
BeginAttributes,
EndAttributes,
BeginList,
EndList,
BeginMap,
EndMap,
String(Cow<'a, [u8]>),
Int64(i64),
Uint64(u64),
Double(f64),
Boolean(bool),
Entity,
KeyValueSeparator,
ItemSeparator,
}