From f618926ccd4344233a9a972da7ec0907fc9c905c Mon Sep 17 00:00:00 2001 From: Johnny Graettinger Date: Fri, 13 Sep 2024 12:13:17 -0500 Subject: [PATCH] models: RawValue strips newlines We have several contexts where we'd like to directly serialize types that contain RawValue and presume that they're valid serializations for newline-delimited JSON. To make that assumption, we must know that RawValue can never contain a newline. --- crates/models/src/raw_value.rs | 47 ++++++++++++++++++++++++++++++---- 1 file changed, 42 insertions(+), 5 deletions(-) diff --git a/crates/models/src/raw_value.rs b/crates/models/src/raw_value.rs index e173dbdfb0..c1ec74baea 100644 --- a/crates/models/src/raw_value.rs +++ b/crates/models/src/raw_value.rs @@ -1,4 +1,6 @@ -#[derive(serde::Serialize, serde::Deserialize, Clone)] +/// RawValue is like serde_json::value::RawValue, but removes newlines to ensure +/// values can safely be used in newline-delimited contexts. +#[derive(serde::Serialize, Clone)] pub struct RawValue(Box); impl RawValue { @@ -6,10 +8,12 @@ impl RawValue { return self.get() == "null"; } pub fn from_str(s: &str) -> serde_json::Result { - serde_json::value::RawValue::from_string(s.to_owned()).map(Into::into) + Self::from_string(s.to_owned()) } - pub fn from_string(s: String) -> serde_json::Result { - serde_json::value::RawValue::from_string(s).map(Into::into) + pub fn from_string(mut s: String) -> serde_json::Result { + s.retain(|c| c != '\n'); // Strip newlines. + let value = serde_json::value::RawValue::from_string(s)?; + Ok(Self(value)) } pub fn from_value(value: &serde_json::Value) -> Self { Self::from_string(value.to_string()).unwrap() @@ -19,6 +23,16 @@ impl RawValue { } } +impl<'de> serde::Deserialize<'de> for RawValue { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let inner = Box::::deserialize(deserializer)?; + Ok(inner.into()) + } +} + impl Default for RawValue { fn default() -> Self { Self(serde_json::value::RawValue::from_string("null".to_string()).unwrap()) @@ -27,7 +41,12 @@ impl Default for RawValue { impl From> for RawValue { fn from(value: Box) -> Self { - Self(value) + if value.get().contains('\n') { + let s: Box = value.into(); + Self::from_string(s.into()).unwrap() + } else { + Self(value) + } } } @@ -73,3 +92,21 @@ impl schemars::JsonSchema for RawValue { serde_json::Value::json_schema(gen) } } + +#[cfg(test)] +mod test { + + #[test] + fn test_newlines_are_removed() { + let fixture = serde_json::to_string_pretty(&serde_json::json!({ + "one": 2, + "three": [4, 5] + })) + .unwrap(); + + let v = serde_json::value::RawValue::from_string(fixture).unwrap(); + assert!(v.get().contains('\n')); + let v = super::RawValue::from(v); + assert!(!v.get().contains('\n')); + } +}