diff --git a/crates/nu-protocol/src/value/record.rs b/crates/nu-protocol/src/value/record.rs index 1d08fbf744..50996ac2b0 100644 --- a/crates/nu-protocol/src/value/record.rs +++ b/crates/nu-protocol/src/value/record.rs @@ -2,9 +2,9 @@ use std::ops::RangeBounds; use crate::{ShellError, Span, Value}; -use serde::{Deserialize, Serialize}; +use serde::{de::Visitor, ser::SerializeMap, Deserialize, Serialize}; -#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[derive(Debug, Clone, Default)] pub struct Record { inner: Vec<(String, Value)>, } @@ -285,6 +285,78 @@ impl Record { } } +impl Serialize for Record { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut map = serializer.serialize_map(Some(self.len()))?; + for (k, v) in self { + map.serialize_entry(k, v)?; + } + map.end() + } +} + +impl<'de> Deserialize<'de> for Record { + /// Special deserialization implementation that turns a map-pattern into a [`Record`] + /// + /// Denies duplicate keys + /// + /// ```rust + /// use serde_json::{from_str, Result}; + /// use nu_protocol::{Record, Value, record}; + /// + /// // A `Record` in json is a Record with a packed `Value` + /// // The `Value` record has a single key indicating its type and the inner record describing + /// // its representation of value and the associated `Span` + /// let ok = r#"{"a": {"Int": {"val": 42, "span": {"start": 0, "end": 0}}}, + /// "b": {"Int": {"val": 37, "span": {"start": 0, "end": 0}}}}"#; + /// let ok_rec: Record = from_str(ok).unwrap(); + /// assert_eq!(Value::test_record(ok_rec), + /// Value::test_record(record!{"a" => Value::test_int(42), + /// "b" => Value::test_int(37)})); + /// // A repeated key will lead to a deserialization error + /// let bad = r#"{"a": {"Int": {"val": 42, "span": {"start": 0, "end": 0}}}, + /// "a": {"Int": {"val": 37, "span": {"start": 0, "end": 0}}}}"#; + /// let bad_rec: Result = from_str(bad); + /// assert!(bad_rec.is_err()); + /// ``` + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + deserializer.deserialize_map(RecordVisitor) + } +} + +struct RecordVisitor; + +impl<'de> Visitor<'de> for RecordVisitor { + type Value = Record; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("a nushell `Record` mapping string keys/columns to nushell `Value`") + } + + fn visit_map(self, mut map: A) -> Result + where + A: serde::de::MapAccess<'de>, + { + let mut record = Record::with_capacity(map.size_hint().unwrap_or(0)); + + while let Some((key, value)) = map.next_entry::()? { + if record.insert(key, value).is_some() { + return Err(serde::de::Error::custom( + "invalid entry, duplicate keys are not allowed for `Record`", + )); + } + } + + Ok(record) + } +} + impl FromIterator<(String, Value)> for Record { fn from_iter>(iter: T) -> Self { // TODO: should this check for duplicate keys/columns?