1pub use linera_sdk_derive::StableEnum;
8use serde::{de::DeserializeOwned, Deserialize, Serialize};
9
10#[doc(hidden)]
15pub mod __private {
16 pub use serde;
17 pub use serde_reflection;
18}
19use serde_reflection::{
20 json_converter::{DeserializationContext, EmptyEnvironment},
21 Format, Registry, Samples, Tracer,
22};
23
24#[derive(Serialize, Deserialize, Debug, Eq, Clone, PartialEq)]
27#[serde(rename_all = "UPPERCASE")]
28pub struct Formats {
29 pub registry: Registry,
31 pub operation: Format,
33 pub response: Format,
35 pub message: Format,
37 pub event_value: Format,
39}
40
41pub trait BcsApplication {
43 type Abi;
45
46 fn formats() -> serde_reflection::Result<Formats>;
48}
49
50pub trait StableEnumTrace: Sized + Serialize {
63 const STABLE_VARIANTS: &'static [(&'static str, u32)];
65
66 fn trace_all_variants(
69 tracer: &mut Tracer,
70 samples: &Samples,
71 ) -> serde_reflection::Result<Format>;
72}
73
74pub trait StableEnum: StableEnumTrace + Serialize + DeserializeOwned {}
81
82impl<T> StableEnum for T where T: StableEnumTrace + Serialize + DeserializeOwned {}
83
84pub trait TracerExt {
94 fn trace_stable_enum_type<T>(&mut self, samples: &Samples) -> serde_reflection::Result<Format>
98 where
99 T: StableEnumTrace;
100}
101
102impl TracerExt for Tracer {
103 fn trace_stable_enum_type<T>(&mut self, samples: &Samples) -> serde_reflection::Result<Format>
104 where
105 T: StableEnumTrace,
106 {
107 T::trace_all_variants(self, samples)
108 }
109}
110
111pub fn bcs_to_json(
114 bytes: &[u8],
115 format: &Format,
116 registry: &Registry,
117) -> bcs::Result<serde_json::Value> {
118 let context = DeserializationContext {
119 format: format.clone(),
120 registry,
121 environment: &EmptyEnvironment,
122 };
123 bcs::from_bytes_seed(context, bytes)
124}
125
126impl Formats {
127 pub fn decode_operation(&self, bytes: &[u8]) -> bcs::Result<serde_json::Value> {
129 bcs_to_json(bytes, &self.operation, &self.registry)
130 }
131
132 pub fn decode_response(&self, bytes: &[u8]) -> bcs::Result<serde_json::Value> {
134 bcs_to_json(bytes, &self.response, &self.registry)
135 }
136
137 pub fn decode_message(&self, bytes: &[u8]) -> bcs::Result<serde_json::Value> {
139 bcs_to_json(bytes, &self.message, &self.registry)
140 }
141
142 pub fn decode_event_value(&self, bytes: &[u8]) -> bcs::Result<serde_json::Value> {
144 bcs_to_json(bytes, &self.event_value, &self.registry)
145 }
146}
147
148#[cfg(test)]
149mod tests {
150 use serde::{Deserialize, Serialize};
151 use serde_json::json;
152 use serde_reflection::{ContainerFormat, Samples, Tracer, TracerConfig};
153
154 use super::*;
155
156 fn trace_format<T>() -> (Format, Registry)
157 where
158 T: Serialize + for<'de> Deserialize<'de>,
159 {
160 let mut tracer = Tracer::new(
161 TracerConfig::default()
162 .record_samples_for_newtype_structs(true)
163 .record_samples_for_tuple_structs(true),
164 );
165 let samples = Samples::new();
166 let (format, _) = tracer.trace_type::<T>(&samples).unwrap();
167 let registry = tracer.registry().unwrap();
168 (format, registry)
169 }
170
171 #[test]
172 fn primitive_round_trip() {
173 let (format, registry) = trace_format::<u64>();
174 let bytes = bcs::to_bytes(&42u64).unwrap();
175 let value = bcs_to_json(&bytes, &format, ®istry).unwrap();
176 assert_eq!(value, json!(42));
177 }
178
179 #[test]
180 fn struct_round_trip() {
181 #[derive(Serialize, Deserialize)]
182 struct Point {
183 x: i32,
184 y: i32,
185 }
186
187 let (format, registry) = trace_format::<Point>();
188 let bytes = bcs::to_bytes(&Point { x: 10, y: -7 }).unwrap();
189 let value = bcs_to_json(&bytes, &format, ®istry).unwrap();
190 assert_eq!(value, json!({ "x": 10, "y": -7 }));
191 }
192
193 #[test]
194 fn enum_unit_and_struct_variants() {
195 #[derive(Serialize, Deserialize)]
196 enum Op {
197 Increment,
198 Set { value: u64 },
199 Add(i64, i64),
200 }
201
202 let (format, registry) = trace_format::<Op>();
203
204 let bytes = bcs::to_bytes(&Op::Increment).unwrap();
205 let value = bcs_to_json(&bytes, &format, ®istry).unwrap();
206 assert_eq!(value, json!({ "Increment": null }));
207
208 let bytes = bcs::to_bytes(&Op::Set { value: 99 }).unwrap();
209 let value = bcs_to_json(&bytes, &format, ®istry).unwrap();
210 assert_eq!(value, json!({ "Set": { "value": 99 } }));
211
212 let bytes = bcs::to_bytes(&Op::Add(2, 3)).unwrap();
213 let value = bcs_to_json(&bytes, &format, ®istry).unwrap();
214 assert_eq!(value, json!({ "Add": [2, 3] }));
215 }
216
217 #[test]
218 fn nested_with_option_and_seq() {
219 #[derive(Serialize, Deserialize)]
220 struct Outer {
221 tag: String,
222 items: Vec<u32>,
223 note: Option<String>,
224 }
225
226 let (format, registry) = trace_format::<Outer>();
227 let value = Outer {
228 tag: "hello".to_string(),
229 items: vec![1, 2, 3],
230 note: None,
231 };
232 let bytes = bcs::to_bytes(&value).unwrap();
233 let json_value = bcs_to_json(&bytes, &format, ®istry).unwrap();
234 assert_eq!(
235 json_value,
236 json!({ "tag": "hello", "items": [1, 2, 3], "note": null })
237 );
238 }
239
240 #[test]
241 fn formats_decode_helpers() {
242 #[derive(Serialize, Deserialize)]
243 enum Operation {
244 Ping,
245 Echo(String),
246 }
247 #[derive(Serialize, Deserialize)]
248 struct Response {
249 ok: bool,
250 }
251
252 let (operation, op_registry) = trace_format::<Operation>();
253 let (response, resp_registry) = trace_format::<Response>();
254
255 let mut registry = op_registry;
256 registry.extend(resp_registry);
257
258 let (message, _) = trace_format::<()>();
259 let (event_value, _) = trace_format::<()>();
260
261 let formats = Formats {
262 registry,
263 operation,
264 response,
265 message,
266 event_value,
267 };
268
269 let op_bytes = bcs::to_bytes(&Operation::Echo("hi".to_string())).unwrap();
270 assert_eq!(
271 formats.decode_operation(&op_bytes).unwrap(),
272 json!({ "Echo": "hi" })
273 );
274
275 let resp_bytes = bcs::to_bytes(&Response { ok: true }).unwrap();
276 assert_eq!(
277 formats.decode_response(&resp_bytes).unwrap(),
278 json!({ "ok": true })
279 );
280
281 let unit_bytes = bcs::to_bytes(&()).unwrap();
282 assert_eq!(formats.decode_message(&unit_bytes).unwrap(), json!(null));
283 assert_eq!(
284 formats.decode_event_value(&unit_bytes).unwrap(),
285 json!(null)
286 );
287 }
288
289 #[test]
290 fn malformed_bytes_return_error() {
291 let (format, registry) = trace_format::<u64>();
292 assert!(bcs_to_json(&[1, 2, 3], &format, ®istry).is_err());
293 }
294
295 #[test]
296 fn stable_enum_round_trip() {
297 use linera_sdk_derive::StableEnumInCrate;
298
299 #[derive(Debug, PartialEq, StableEnumInCrate)]
300 enum Op {
301 Increment,
302 Set { value: u64 },
303 Add(i64, i64),
304 Echo(String),
305 }
306
307 for c in [
309 Op::Increment,
310 Op::Set { value: 99 },
311 Op::Add(2, 3),
312 Op::Echo("hi".into()),
313 ] {
314 let bytes = bcs::to_bytes(&c).unwrap();
315 assert!(bytes.len() >= 4, "tag must be at least 4 bytes: {c:?}");
316 assert_eq!(bytes[0] & 0x80, 0x80, "byte 0 has continuation");
318 assert_eq!(bytes[1] & 0x80, 0x80, "byte 1 has continuation");
319 assert_eq!(bytes[2] & 0x80, 0x80, "byte 2 has continuation");
320 assert_eq!(bytes[3] & 0x80, 0x00, "byte 3 terminates");
321
322 let back: Op = bcs::from_bytes(&bytes).unwrap();
323 assert_eq!(back, c);
324 }
325
326 let bogus = bcs::to_bytes(&0u32).unwrap();
328 assert!(bcs::from_bytes::<Op>(&bogus).is_err());
329
330 for &(name, tag) in <Op as StableEnumTrace>::STABLE_VARIANTS {
332 let sample = match name {
333 "Increment" => bcs::to_bytes(&Op::Increment).unwrap(),
334 "Set" => bcs::to_bytes(&Op::Set { value: 0 }).unwrap(),
335 "Add" => bcs::to_bytes(&Op::Add(0, 0)).unwrap(),
336 "Echo" => bcs::to_bytes(&Op::Echo(String::new())).unwrap(),
337 _ => unreachable!(),
338 };
339 let decoded = decode_uleb_u32(&sample[..4]);
340 assert_eq!(decoded, tag, "variant {name} tag mismatch");
341 }
342
343 let mut tracer = Tracer::new(TracerConfig::default());
345 let samples = Samples::new();
346 let format = tracer.trace_stable_enum_type::<Op>(&samples).unwrap();
347 let registry = tracer.registry().unwrap();
348 match registry.get("Op").unwrap() {
349 ContainerFormat::Enum(variants) => {
350 let mut keys: Vec<_> = variants.keys().copied().collect();
351 keys.sort();
352 let mut expected: Vec<u32> = <Op as StableEnumTrace>::STABLE_VARIANTS
353 .iter()
354 .map(|(_, t)| *t)
355 .collect();
356 expected.sort();
357 assert_eq!(keys, expected);
358 }
359 _ => panic!("expected enum"),
360 }
361
362 let bytes = bcs::to_bytes(&Op::Set { value: 99 }).unwrap();
364 let value = bcs_to_json(&bytes, &format, ®istry).unwrap();
365 assert_eq!(value, json!({ "Set": { "value": 99 } }));
366 }
367
368 fn decode_uleb_u32(bytes: &[u8]) -> u32 {
370 let b0 = (bytes[0] & 0x7f) as u32;
371 let b1 = (bytes[1] & 0x7f) as u32;
372 let b2 = (bytes[2] & 0x7f) as u32;
373 let b3 = bytes[3] as u32;
374 b0 | (b1 << 7) | (b2 << 14) | (b3 << 21)
375 }
376}