add support for deserializing a map with int keys as a sequence

This commit is contained in:
Robin Appelman 2025-06-05 20:42:13 +02:00
commit b928f85df6
14 changed files with 226 additions and 9 deletions

View file

@ -1,5 +1,6 @@
use super::Entry;
use super::{Entry, Table};
use crate::entry::Value;
use crate::error::{ParseStringError, UnknownError};
use crate::VdfError;
use serde::de::{DeserializeSeed, SeqAccess};
use serde::{Deserialize, Serialize};
@ -80,3 +81,53 @@ impl<'de> SeqAccess<'de> for ArraySeq {
seed.deserialize(next).map(Some)
}
}
pub(crate) struct TableArraySeq {
iter: std::vec::IntoIter<(String, Entry)>,
last_key: Option<u64>,
}
impl TableArraySeq {
pub(crate) fn new(table: Table) -> Self {
// since the tables map doesn't have a stable order, we need to re-sort them to have the keys in order
let mut items: Vec<_> = table.into_iter().collect();
items.sort_by(|(key_a, _), (key_b, _)| key_a.cmp(key_b));
TableArraySeq {
iter: items.into_iter(),
last_key: None,
}
}
}
impl<'de> SeqAccess<'de> for TableArraySeq {
type Error = VdfError;
fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>, Self::Error>
where
T: DeserializeSeed<'de>,
{
let (key, next) = match self.iter.next() {
Some(next) => next,
None => return Ok(None),
};
let key: u64 = key.parse().map_err(|_| {
VdfError::ParseString(ParseStringError {
ty: "u64",
value: key,
})
})?;
if let Some(last_key) = self.last_key {
let expected_key = last_key + 1;
if expected_key != key {
return Err(VdfError::Other(UnknownError::from(format!(
"Invalid array key {key}, expected {expected_key}"
))));
}
}
self.last_key = Some(key);
seed.deserialize(next).map(Some)
}
}

View file

@ -216,7 +216,7 @@ macro_rules! from_str {
);
}
use crate::entry::array::ArraySeq;
use crate::entry::array::{ArraySeq, TableArraySeq};
use crate::entry::table::TableSeq;
use serde::de::{DeserializeSeed, EnumAccess, Error, MapAccess, SeqAccess, VariantAccess, Visitor};
use serde::{Deserialize, Deserializer, Serialize};
@ -614,7 +614,8 @@ impl<'de> Deserializer<'de> for Entry {
{
match self {
Entry::Array(arr) => visitor.visit_seq(ArraySeq::new(arr)),
_ => Err(UnknownError::from("array2").into()),
Entry::Table(table) => visitor.visit_seq(TableArraySeq::new(table)),
_ => Err(UnknownError::from("array").into()),
}
}

View file

@ -12,7 +12,7 @@ use std::ops::{Deref, DerefMut};
/// A table of entries.
#[derive(Clone, PartialEq, Eq, Debug, Deserialize, Serialize, Default)]
#[serde(transparent)]
pub struct Table(#[serde(serialize_with = "ordered_map")] HashMap<String, Entry>);
pub struct Table(#[serde(serialize_with = "ordered_map")] HashMap<String, Entry>); // todo: switch to a map that maintains item order
impl From<HashMap<String, Entry>> for Table {
fn from(value: HashMap<String, Entry>) -> Self {
@ -118,6 +118,15 @@ impl Table {
}
}
impl IntoIterator for Table {
type Item = (String, Entry);
type IntoIter = hash_map::IntoIter<String, Entry>;
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}
impl From<Table> for Entry {
fn from(table: Table) -> Self {
Entry::Table(table)

View file

@ -181,6 +181,16 @@ impl From<&str> for UnknownError {
}
}
impl From<String> for UnknownError {
fn from(value: String) -> Self {
UnknownError {
error: value,
err_span: (0..0).into(),
src: String::new(),
}
}
}
/// A token that wasn't expected was found while parsing
#[derive(Debug, Clone, Diagnostic)]
#[diagnostic(code(vmt_reader::unexpected_token))]

View file

@ -1,5 +1,5 @@
use crate::entry::{string_is_array, Entry, ParseItem};
use crate::error::{ExpectToken, NoValidTokenError, ResultExt, SerdeParseError};
use crate::error::{ExpectToken, NoValidTokenError, ResultExt, SerdeParseError, UnknownError};
use crate::tokenizer::{SpannedToken, Tokenizer};
use crate::{Token, VdfError};
use logos::Span;
@ -348,7 +348,17 @@ impl<'de> de::Deserializer<'de> for &'_ mut Deserializer<'de> {
where
V: Visitor<'de>,
{
let token = self.peek().expect_token(STRING_ITEMS, self.source())?;
let token = self.peek().expect_token(SEQ_START_ITEMS, self.source())?;
if token.token == Token::GroupStart {
// we allow deserializing a map of consecutive int keys as a seq
let _ = self.next();
return visitor.visit_seq(MapSeqWalker {
table: TableWalker::new(self, false),
last_key: None,
});
}
let value_str = &self.source()[token.span.clone()];
if (value_str.starts_with("\"[") && value_str.ends_with("]\""))
|| (value_str.starts_with("\"{") && value_str.ends_with("}\""))
@ -555,6 +565,15 @@ impl<'de> MapAccess<'de> for TableWalker<'de, '_> {
}
}
/// STRING_ITEMS and GroupStart
const SEQ_START_ITEMS: &[Token] = &[
Token::Item,
Token::QuotedItem,
Token::Statement,
Token::QuotedStatement,
Token::GroupStart,
];
struct SeqWalker<'source, 'a> {
table: TableWalker<'source, 'a>,
key: Cow<'source, str>,
@ -674,6 +693,37 @@ where
}
}
struct MapSeqWalker<'source, 'a> {
table: TableWalker<'source, 'a>,
last_key: Option<u64>,
}
impl<'de> SeqAccess<'de> for MapSeqWalker<'de, '_> {
type Error = VdfError;
fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>, Self::Error>
where
T: DeserializeSeed<'de>,
{
let Some(key) = self.table.next_key()? else {
return Ok(None);
};
if let Some(last_key) = self.last_key {
let expected_key = last_key + 1;
if expected_key != key {
return Err(VdfError::Other(UnknownError::from(format!(
"Invalid array key {key}, expected {expected_key}"
)))
.with_source_span(self.table.de.last_span.clone(), self.table.de.source()));
}
}
self.last_key = Some(key);
Ok(Some(self.table.next_value_seed(seed)?))
}
}
struct Enum<'a, 'de: 'a> {
de: &'a mut Deserializer<'de>,
enclosed: bool,