canonical.rs (4281B)
1 #[cfg(not(feature = "std"))] 2 use alloc::{ 3 string::{String, ToString}, 4 vec::Vec, 5 }; 6 7 use serde::Serialize; 8 use serde_json::{Map, Value}; 9 10 use crate::error::RadrootsReplicaEventsError; 11 12 #[cfg(test)] 13 pub(crate) mod failpoints { 14 use std::cell::Cell; 15 16 thread_local! { 17 static FORCE_ERROR: Cell<bool> = const { Cell::new(false) }; 18 } 19 20 pub(crate) fn set_error() { 21 FORCE_ERROR.with(|flag| flag.set(true)); 22 } 23 24 pub(crate) fn take_error() -> bool { 25 FORCE_ERROR.with(|flag| { 26 let value = flag.get(); 27 flag.set(false); 28 value 29 }) 30 } 31 } 32 33 pub fn canonical_json_string<T: Serialize>( 34 value: &T, 35 ) -> Result<String, RadrootsReplicaEventsError> { 36 let value = serde_json::to_value(value).map_err(map_canonical_serialize_error)?; 37 canonical_json_value(value) 38 } 39 40 fn canonical_json_value(value: Value) -> Result<String, RadrootsReplicaEventsError> { 41 #[cfg(test)] 42 if failpoints::take_error() { 43 return Err(RadrootsReplicaEventsError::InvalidData( 44 canonical_error_message(), 45 )); 46 } 47 Ok(canonicalize_value(value).to_string()) 48 } 49 50 fn canonical_error_message() -> String { 51 "canonical json serialization failed".to_string() 52 } 53 54 fn map_canonical_serialize_error(_err: serde_json::Error) -> RadrootsReplicaEventsError { 55 RadrootsReplicaEventsError::InvalidData(canonical_error_message()) 56 } 57 58 fn canonicalize_value(value: Value) -> Value { 59 match value { 60 Value::Object(map) => canonicalize_object(map), 61 Value::Array(values) => { 62 let values = values 63 .into_iter() 64 .map(canonicalize_value) 65 .collect::<Vec<_>>(); 66 Value::Array(values) 67 } 68 other => other, 69 } 70 } 71 72 fn canonicalize_object(map: Map<String, Value>) -> Value { 73 let mut entries = map.into_iter().collect::<Vec<_>>(); 74 entries.sort_by(|a, b| a.0.cmp(&b.0)); 75 let mut ordered = Map::new(); 76 for (key, value) in entries { 77 ordered.insert(key, canonicalize_value(value)); 78 } 79 Value::Object(ordered) 80 } 81 82 #[cfg(test)] 83 mod tests { 84 use super::canonical_json_string; 85 use serde::Serialize; 86 87 #[derive(Serialize)] 88 struct CanonicalFixture { 89 z: u32, 90 a: NestedFixture, 91 } 92 93 #[derive(Serialize)] 94 struct NestedFixture { 95 b: u32, 96 a: u32, 97 } 98 99 #[test] 100 fn canonical_json_string_sorts_object_keys_recursively() { 101 let value = CanonicalFixture { 102 z: 2, 103 a: NestedFixture { b: 3, a: 1 }, 104 }; 105 let json = canonical_json_string(&value).expect("json"); 106 assert_eq!(json, r#"{"a":{"a":1,"b":3},"z":2}"#); 107 } 108 109 #[test] 110 fn canonical_json_string_handles_arrays() { 111 let json = canonical_json_string(&serde_json::json!([{"b": 2, "a": 1}])).expect("json"); 112 assert_eq!(json, r#"[{"a":1,"b":2}]"#); 113 } 114 115 #[test] 116 fn canonical_json_string_handles_scalar_values() { 117 let json = canonical_json_string(&"value").expect("json"); 118 assert_eq!(json, r#""value""#); 119 } 120 121 #[test] 122 fn canonical_json_string_failpoint_returns_error() { 123 super::failpoints::set_error(); 124 let err = canonical_json_string(&"value").expect_err("failpoint"); 125 assert!( 126 err.to_string() 127 .contains("canonical json serialization failed") 128 ); 129 } 130 131 struct FlakySerialize { 132 fail: bool, 133 } 134 135 impl Serialize for FlakySerialize { 136 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> 137 where 138 S: serde::Serializer, 139 { 140 if self.fail { 141 Err(serde::ser::Error::custom("always fail")) 142 } else { 143 serializer.serialize_str("ok") 144 } 145 } 146 } 147 148 #[test] 149 fn canonical_json_string_propagates_serialization_errors() { 150 let ok = canonical_json_string(&FlakySerialize { fail: false }).expect("serialize ok"); 151 assert_eq!(ok, r#""ok""#); 152 153 let err = 154 canonical_json_string(&FlakySerialize { fail: true }).expect_err("serialize fail"); 155 assert!( 156 err.to_string() 157 .contains("canonical json serialization failed") 158 ); 159 } 160 }