use indexmap to preserve array order, implement serialize for Value

This commit is contained in:
Robin Appelman 2025-08-17 19:38:16 +02:00
commit 3bea91855b
5 changed files with 160 additions and 38 deletions

View file

@ -55,14 +55,16 @@ mod string;
use crate::string::is_array_key_numeric;
pub use error::ParseError;
use indexmap::IndexMap;
use serde::de::{self, MapAccess, SeqAccess, Visitor};
use serde::{Deserialize, Deserializer};
use serde::ser::{SerializeMap, SerializeSeq};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
pub use serde_impl::from_str;
use std::borrow::Borrow;
use std::cmp::Ordering;
use std::collections::HashMap;
use std::convert::TryInto;
use std::fmt::{Display, Formatter};
use std::fmt::{Debug, Display, Formatter};
use std::hash::{Hash, Hasher};
use std::ops::Index;
@ -78,11 +80,11 @@ use std::ops::Index;
/// or the key is not found
///
/// ```rust
/// # use maplit::hashmap;
/// # use indexmap::indexmap;
/// # use php_literal_parser::Value;
/// #
/// # fn main() {
/// let value = Value::Array(hashmap!{
/// let value = Value::Array(indexmap!{
/// "key".into() => "value".into(),
/// 10.into() => false.into()
/// });
@ -97,7 +99,7 @@ pub enum Value {
Int(i64),
Float(f64),
String(String),
Array(HashMap<Key, Value>),
Array(IndexMap<Key, Value>),
Null,
}
@ -164,8 +166,8 @@ impl Value {
}
}
/// Convert the value into a hashmap if it is one
pub fn into_hashmap(self) -> Option<HashMap<Key, Value>> {
/// Convert the value into a map if it is one
pub fn into_map(self) -> Option<IndexMap<Key, Value>> {
match self {
Value::Array(map) => Some(map),
_ => None,
@ -291,9 +293,15 @@ impl From<&str> for Value {
}
}
impl From<IndexMap<Key, Value>> for Value {
fn from(value: IndexMap<Key, Value>) -> Self {
Value::Array(value)
}
}
impl From<HashMap<Key, Value>> for Value {
fn from(value: HashMap<Key, Value>) -> Self {
Value::Array(value)
Value::Array(value.into_iter().collect())
}
}
@ -324,6 +332,8 @@ pub enum Key {
}
impl Hash for Key {
// a hash implementation which doesn't include the enum discriminant
// that hash hash("foo") == hash(Key::String("foo"))
fn hash<H: Hasher>(&self, state: &mut H) {
match self {
Key::Int(int) => int.hash(state),
@ -350,6 +360,18 @@ impl From<&str> for Key {
}
}
impl Serialize for Key {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match self {
Key::Int(i) => serializer.serialize_i64(*i),
Key::String(s) => serializer.serialize_str(s),
}
}
}
impl Key {
/// Check if the key is an integer
pub fn is_int(&self) -> bool {
@ -487,8 +509,8 @@ impl Display for Key {
#[test]
fn test_index() {
use maplit::hashmap;
let map = Value::Array(hashmap! {
use indexmap::indexmap;
let map = Value::Array(indexmap! {
Key::String("key".to_string()) => Value::String("value".to_string()),
Key::Int(1) => Value::Bool(true),
});
@ -624,7 +646,7 @@ impl<'de> Visitor<'de> for ValueVisitor {
where
A: SeqAccess<'de>,
{
let mut result = HashMap::new();
let mut result = IndexMap::new();
let mut next_key = 0;
while let Some(value) = seq.next_element::<Value>()? {
let key = Key::Int(next_key);
@ -638,7 +660,7 @@ impl<'de> Visitor<'de> for ValueVisitor {
where
A: MapAccess<'de>,
{
let mut result = HashMap::new();
let mut result = IndexMap::new();
while let Some((key, value)) = map.next_entry()? {
result.insert(key, value);
}
@ -655,6 +677,40 @@ impl<'de> Deserialize<'de> for Value {
}
}
impl Serialize for Value {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match self {
Value::Bool(b) => serializer.serialize_bool(*b),
Value::Int(i) => serializer.serialize_i64(*i),
Value::Float(f) => serializer.serialize_f64(*f),
Value::String(s) => serializer.serialize_str(s),
Value::Array(a) => {
if a.keys()
.enumerate()
.all(|(i, k)| k.as_int() == Some(i as i64))
{
let mut seq = serializer.serialize_seq(Some(a.len()))?;
for value in a.values() {
seq.serialize_element(value)?;
}
seq.end()
} else {
let mut map = serializer.serialize_map(Some(a.len()))?;
for (key, value) in a {
map.serialize_key(key)?;
map.serialize_value(value)?;
}
map.end()
}
}
Value::Null => serializer.serialize_none(),
}
}
}
struct KeyVisitor;
impl<'de> Visitor<'de> for KeyVisitor {