serde for Entry/Table/Statement/Value

This commit is contained in:
Robin Appelman 2023-12-18 22:39:37 +01:00
commit d7d26f530a
14 changed files with 367 additions and 72 deletions

View file

@ -8,12 +8,14 @@ use crate::Item;
pub use array::Array;
pub use statement::Statement;
use std::any::type_name;
use std::collections::HashMap;
use std::fmt::Formatter;
use std::slice;
pub use table::Table;
pub use value::Value;
/// The kinds of entry.
#[derive(Clone, PartialEq, Eq, Debug, Deserialize, Serialize)]
#[derive(Clone, PartialEq, Eq, Debug, Serialize)]
#[serde(untagged)]
pub enum Entry {
/// A table.
@ -22,11 +24,11 @@ pub enum Entry {
/// An array (entries with the same key).
Array(Array),
/// A statement (the values starting with #).
Statement(Statement),
/// A value.
Value(Value),
/// A statement (the values starting with #).
Statement(Statement),
}
impl From<Item<'_>> for Entry {
@ -175,7 +177,8 @@ macro_rules! from_str {
);
}
use serde::{Deserialize, Serialize};
use serde::de::{Error, MapAccess, Visitor};
use serde::{Deserialize, Deserializer, Serialize};
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
from_str!(for IpAddr Ipv4Addr Ipv6Addr SocketAddr SocketAddrV4 SocketAddrV6);
from_str!(for i8 i16 i32 i64 isize u8 u16 u32 u64 usize f32 f64);
@ -230,3 +233,113 @@ impl<T: ParseItem> ParseItem for Option<T> {
T::from_str(item).map(Some)
}
}
impl<'de> Deserialize<'de> for Entry {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct EntryVisitor;
impl<'v> Visitor<'v> for EntryVisitor {
type Value = Entry;
fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result {
write!(formatter, "any string like value or group")
}
fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E>
where
E: Error,
{
Ok(Entry::Value(v.to_string().into()))
}
fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>
where
E: Error,
{
Ok(Entry::Value(v.to_string().into()))
}
fn visit_f64<E>(self, v: f64) -> Result<Self::Value, E>
where
E: Error,
{
Ok(Entry::Value(v.to_string().into()))
}
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: Error,
{
if v.starts_with('#') {
Ok(Entry::Statement(v.to_string().into()))
} else {
Ok(Entry::Value(v.to_string().into()))
}
}
fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
where
E: Error,
{
if v.starts_with('#') {
Ok(Entry::Statement(v.into()))
} else {
Ok(Entry::Value(v.into()))
}
}
fn visit_bool<E>(self, v: bool) -> Result<Self::Value, E>
where
E: Error,
{
let v = if v { "1" } else { "0" };
Ok(Entry::Value(v.to_string().into()))
}
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
where
A: MapAccess<'v>,
{
let mut res = HashMap::new();
while let Some(entry) = map.next_entry()? {
res.insert(entry.0, entry.1);
}
Ok(Entry::Table(res.into()))
}
}
deserializer.deserialize_any(EntryVisitor)
}
}
#[cfg(test)]
#[track_caller]
fn unwrap_err<T>(r: Result<T, crate::VdfError>) -> T {
r.map_err(miette::Error::from).unwrap()
}
#[test]
fn test_serde_entry() {
use maplit::hashmap;
let j = r#"1"#;
assert_eq!(Entry::Value("1".into()), unwrap_err(crate::from_str(j)));
let j = r#""foo bar""#;
assert_eq!(
Entry::Value("foo bar".into()),
unwrap_err(crate::from_str(j))
);
let j = r#"{foo bar}"#;
assert_eq!(
Entry::Table(hashmap! {"foo".into() => Entry::Value("bar".into())}.into()),
unwrap_err(crate::from_str(j))
);
}

View file

@ -19,6 +19,17 @@ impl From<Statement> for Entry {
Entry::Statement(statement)
}
}
impl From<&'_ str> for Statement {
fn from(value: &str) -> Statement {
Statement(value.into())
}
}
impl From<String> for Statement {
fn from(value: String) -> Statement {
Statement(value)
}
}
impl From<Statement> for String {
fn from(value: Statement) -> Self {

View file

@ -12,6 +12,12 @@ use std::ops::Deref;
#[serde(transparent)]
pub struct Table(#[serde(serialize_with = "ordered_map")] HashMap<String, Entry>);
impl From<HashMap<String, Entry>> for Table {
fn from(value: HashMap<String, Entry>) -> Self {
Table(value)
}
}
fn ordered_map<S, K: Ord + Serialize, V: Serialize>(
value: &HashMap<K, V>,
serializer: S,
@ -90,3 +96,21 @@ impl Deref for Table {
&self.0
}
}
#[cfg(test)]
#[track_caller]
fn unwrap_err<T>(r: Result<T, crate::VdfError>) -> T {
r.map_err(miette::Error::from).unwrap()
}
#[test]
fn test_serde_table() {
use maplit::hashmap;
let j = r#"{foo bar}"#;
assert_eq!(
Table(hashmap! {"foo".into() => Entry::Value("bar".into())}),
unwrap_err(crate::from_str(j))
);
}

View file

@ -1,9 +1,12 @@
use super::Entry;
use serde::{Deserialize, Serialize};
use serde::de::{Error, Visitor};
use serde::{Deserialize, Deserializer, Serialize};
use std::borrow::Cow;
use std::fmt::Formatter;
use std::ops::Deref;
#[derive(Clone, PartialEq, Eq, Debug, Deserialize, Serialize)]
#[derive(Clone, PartialEq, Eq, Debug, Serialize)]
#[serde(transparent)]
pub struct Value(String);
impl From<Cow<'_, str>> for Value {
@ -11,6 +14,17 @@ impl From<Cow<'_, str>> for Value {
Value(value.into())
}
}
impl From<&'_ str> for Value {
fn from(value: &str) -> Value {
Value(value.into())
}
}
impl From<String> for Value {
fn from(value: String) -> Value {
Value(value)
}
}
impl From<Value> for Entry {
fn from(value: Value) -> Self {
@ -31,3 +45,79 @@ impl Deref for Value {
&self.0
}
}
impl<'de> Deserialize<'de> for Value {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct ValueVisitor;
impl Visitor<'_> for ValueVisitor {
type Value = String;
fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result {
write!(formatter, "any string like value")
}
fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E>
where
E: Error,
{
Ok(v.to_string())
}
fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>
where
E: Error,
{
Ok(v.to_string())
}
fn visit_f64<E>(self, v: f64) -> Result<Self::Value, E>
where
E: Error,
{
Ok(v.to_string())
}
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: Error,
{
Ok(v.into())
}
fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
where
E: Error,
{
Ok(v)
}
fn visit_bool<E>(self, v: bool) -> Result<Self::Value, E>
where
E: Error,
{
Ok(if v { "1".into() } else { "0".into() })
}
}
deserializer.deserialize_str(ValueVisitor).map(Value)
}
}
#[cfg(test)]
#[track_caller]
fn unwrap_err<T>(r: Result<T, crate::VdfError>) -> T {
r.map_err(miette::Error::from).unwrap()
}
#[test]
fn test_serde_value() {
let j = r#"1"#;
assert_eq!(Value("1".into()), unwrap_err(crate::from_str(j)));
let j = r#""foo bar""#;
assert_eq!(Value("foo bar".into()), unwrap_err(crate::from_str(j)));
}

View file

@ -134,9 +134,15 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> {
}
}
Token::GroupStart => {
self.push_peeked(token);
self.deserialize_map(visitor)
.ensure_span(span, self.source())
let res = visitor.visit_map(TableWalker::new(self, false));
let span = span.start..self.last_span.end;
res.ensure_span(span.clone(), self.source()).map_err(|e| {
if e.span().map(|s| s.offset()) == Some(span.start) {
e.with_source_span(span, self.source())
} else {
e
}
})
}
_ => unreachable!(),
}