runner/
serde.rs

1// Copyright 2025 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use fidl_fuchsia_data::{Dictionary, DictionaryEntry, DictionaryValue};
6use serde::de::value::{Error, MapDeserializer, SeqDeserializer};
7use serde::de::{Deserializer, Error as _, IntoDeserializer, Visitor};
8use serde::ser::Serializer;
9use serde::{Deserialize, Serialize};
10use std::fmt::{Debug, Display};
11use std::str::FromStr;
12
13/// Deserialize the provided `program` into a value of `T`.
14///
15/// Allows runners to define their program interface as a Rust struct and to use all of serde's
16/// helpers for parsing.
17pub fn deserialize_program<'a, T: Deserialize<'a>>(program: &'a Dictionary) -> Result<T, Error> {
18    T::deserialize(DictionaryDeserializer::new(program))
19}
20
21struct DictionaryDeserializer<'de>(&'de [DictionaryEntry]);
22
23impl<'de> DictionaryDeserializer<'de> {
24    fn new(program: &'de Dictionary) -> Self {
25        Self(program.entries.as_ref().map(Vec::as_slice).unwrap_or(&[]))
26    }
27}
28
29impl<'de, 'a> Deserializer<'de> for DictionaryDeserializer<'de> {
30    type Error = Error;
31
32    // The FIDL Dictionary is fundamentally a self-describing map type, so all of the other
33    // deserialize methods can defer to this without any type hinting.
34    fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
35    where
36        V: Visitor<'de>,
37    {
38        Ok(visitor.visit_map(MapDeserializer::new(
39            self.0.iter().map(|e| (e.key.as_str(), ValueDeserializer(&e.value))),
40        ))?)
41    }
42
43    fn deserialize_bool<V>(self, visitor: V) -> Result<V::Value, Self::Error>
44    where
45        V: Visitor<'de>,
46    {
47        self.deserialize_any(visitor)
48    }
49    fn deserialize_i8<V>(self, visitor: V) -> Result<V::Value, Self::Error>
50    where
51        V: Visitor<'de>,
52    {
53        self.deserialize_any(visitor)
54    }
55    fn deserialize_i16<V>(self, visitor: V) -> Result<V::Value, Self::Error>
56    where
57        V: Visitor<'de>,
58    {
59        self.deserialize_any(visitor)
60    }
61    fn deserialize_i32<V>(self, visitor: V) -> Result<V::Value, Self::Error>
62    where
63        V: Visitor<'de>,
64    {
65        self.deserialize_any(visitor)
66    }
67    fn deserialize_i64<V>(self, visitor: V) -> Result<V::Value, Self::Error>
68    where
69        V: Visitor<'de>,
70    {
71        self.deserialize_any(visitor)
72    }
73    fn deserialize_u8<V>(self, visitor: V) -> Result<V::Value, Self::Error>
74    where
75        V: Visitor<'de>,
76    {
77        self.deserialize_any(visitor)
78    }
79    fn deserialize_u16<V>(self, visitor: V) -> Result<V::Value, Self::Error>
80    where
81        V: Visitor<'de>,
82    {
83        self.deserialize_any(visitor)
84    }
85    fn deserialize_u32<V>(self, visitor: V) -> Result<V::Value, Self::Error>
86    where
87        V: Visitor<'de>,
88    {
89        self.deserialize_any(visitor)
90    }
91    fn deserialize_u64<V>(self, visitor: V) -> Result<V::Value, Self::Error>
92    where
93        V: Visitor<'de>,
94    {
95        self.deserialize_any(visitor)
96    }
97    fn deserialize_f32<V>(self, visitor: V) -> Result<V::Value, Self::Error>
98    where
99        V: Visitor<'de>,
100    {
101        self.deserialize_any(visitor)
102    }
103    fn deserialize_f64<V>(self, visitor: V) -> Result<V::Value, Self::Error>
104    where
105        V: Visitor<'de>,
106    {
107        self.deserialize_any(visitor)
108    }
109    fn deserialize_char<V>(self, visitor: V) -> Result<V::Value, Self::Error>
110    where
111        V: Visitor<'de>,
112    {
113        self.deserialize_any(visitor)
114    }
115    fn deserialize_str<V>(self, visitor: V) -> Result<V::Value, Self::Error>
116    where
117        V: Visitor<'de>,
118    {
119        self.deserialize_any(visitor)
120    }
121    fn deserialize_string<V>(self, visitor: V) -> Result<V::Value, Self::Error>
122    where
123        V: Visitor<'de>,
124    {
125        self.deserialize_any(visitor)
126    }
127    fn deserialize_bytes<V>(self, visitor: V) -> Result<V::Value, Self::Error>
128    where
129        V: Visitor<'de>,
130    {
131        self.deserialize_any(visitor)
132    }
133    fn deserialize_byte_buf<V>(self, visitor: V) -> Result<V::Value, Self::Error>
134    where
135        V: Visitor<'de>,
136    {
137        self.deserialize_any(visitor)
138    }
139    fn deserialize_option<V>(self, visitor: V) -> Result<V::Value, Self::Error>
140    where
141        V: Visitor<'de>,
142    {
143        self.deserialize_any(visitor)
144    }
145    fn deserialize_unit<V>(self, visitor: V) -> Result<V::Value, Self::Error>
146    where
147        V: Visitor<'de>,
148    {
149        self.deserialize_any(visitor)
150    }
151    fn deserialize_unit_struct<V>(
152        self,
153        _name: &'static str,
154        visitor: V,
155    ) -> Result<V::Value, Self::Error>
156    where
157        V: Visitor<'de>,
158    {
159        self.deserialize_any(visitor)
160    }
161    fn deserialize_newtype_struct<V>(
162        self,
163        _name: &'static str,
164        visitor: V,
165    ) -> Result<V::Value, Self::Error>
166    where
167        V: Visitor<'de>,
168    {
169        self.deserialize_any(visitor)
170    }
171    fn deserialize_seq<V>(self, visitor: V) -> Result<V::Value, Self::Error>
172    where
173        V: Visitor<'de>,
174    {
175        self.deserialize_any(visitor)
176    }
177    fn deserialize_tuple<V>(self, _len: usize, visitor: V) -> Result<V::Value, Self::Error>
178    where
179        V: Visitor<'de>,
180    {
181        self.deserialize_any(visitor)
182    }
183    fn deserialize_tuple_struct<V>(
184        self,
185        _name: &'static str,
186        _len: usize,
187        visitor: V,
188    ) -> Result<V::Value, Self::Error>
189    where
190        V: Visitor<'de>,
191    {
192        self.deserialize_any(visitor)
193    }
194    fn deserialize_map<V>(self, visitor: V) -> Result<V::Value, Self::Error>
195    where
196        V: Visitor<'de>,
197    {
198        self.deserialize_any(visitor)
199    }
200    fn deserialize_struct<V>(
201        self,
202        _name: &'static str,
203        _fields: &'static [&'static str],
204        visitor: V,
205    ) -> Result<V::Value, Self::Error>
206    where
207        V: Visitor<'de>,
208    {
209        self.deserialize_any(visitor)
210    }
211    fn deserialize_enum<V>(
212        self,
213        _name: &'static str,
214        _variants: &'static [&'static str],
215        visitor: V,
216    ) -> Result<V::Value, Self::Error>
217    where
218        V: Visitor<'de>,
219    {
220        self.deserialize_any(visitor)
221    }
222    fn deserialize_identifier<V>(self, visitor: V) -> Result<V::Value, Self::Error>
223    where
224        V: Visitor<'de>,
225    {
226        self.deserialize_any(visitor)
227    }
228    fn deserialize_ignored_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
229    where
230        V: Visitor<'de>,
231    {
232        self.deserialize_any(visitor)
233    }
234}
235
236impl<'de> IntoDeserializer<'de> for DictionaryDeserializer<'de> {
237    type Deserializer = Self;
238    fn into_deserializer(self) -> Self::Deserializer {
239        self
240    }
241}
242
243struct ValueDeserializer<'de>(&'de Option<Box<DictionaryValue>>);
244
245impl<'de> Deserializer<'de> for ValueDeserializer<'de> {
246    type Error = Error;
247
248    // Because a DictionaryValue is self-describing we don't need to use any type hints and all
249    // other deserialize_* methods can delegate to this.
250    fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
251    where
252        V: Visitor<'de>,
253    {
254        match self.0 {
255            Some(value) => match &**value {
256                DictionaryValue::Str(s) => visitor.visit_borrowed_str(s.as_str()),
257                DictionaryValue::StrVec(v) => {
258                    visitor.visit_seq(SeqDeserializer::new(v.iter().map(String::as_str)))
259                }
260                DictionaryValue::ObjVec(v) => visitor
261                    .visit_seq(SeqDeserializer::new(v.iter().map(DictionaryDeserializer::new))),
262                other => {
263                    Err(serde::de::value::Error::custom(format!("unknown dict value {other:?}")))
264                }
265            },
266            None => visitor.visit_none(),
267        }
268    }
269
270    fn deserialize_bool<V>(self, visitor: V) -> Result<V::Value, Self::Error>
271    where
272        V: Visitor<'de>,
273    {
274        self.deserialize_any(visitor)
275    }
276    fn deserialize_i8<V>(self, visitor: V) -> Result<V::Value, Self::Error>
277    where
278        V: Visitor<'de>,
279    {
280        self.deserialize_any(visitor)
281    }
282    fn deserialize_i16<V>(self, visitor: V) -> Result<V::Value, Self::Error>
283    where
284        V: Visitor<'de>,
285    {
286        self.deserialize_any(visitor)
287    }
288    fn deserialize_i32<V>(self, visitor: V) -> Result<V::Value, Self::Error>
289    where
290        V: Visitor<'de>,
291    {
292        self.deserialize_any(visitor)
293    }
294    fn deserialize_i64<V>(self, visitor: V) -> Result<V::Value, Self::Error>
295    where
296        V: Visitor<'de>,
297    {
298        self.deserialize_any(visitor)
299    }
300    fn deserialize_u8<V>(self, visitor: V) -> Result<V::Value, Self::Error>
301    where
302        V: Visitor<'de>,
303    {
304        self.deserialize_any(visitor)
305    }
306    fn deserialize_u16<V>(self, visitor: V) -> Result<V::Value, Self::Error>
307    where
308        V: Visitor<'de>,
309    {
310        self.deserialize_any(visitor)
311    }
312    fn deserialize_u32<V>(self, visitor: V) -> Result<V::Value, Self::Error>
313    where
314        V: Visitor<'de>,
315    {
316        self.deserialize_any(visitor)
317    }
318    fn deserialize_u64<V>(self, visitor: V) -> Result<V::Value, Self::Error>
319    where
320        V: Visitor<'de>,
321    {
322        self.deserialize_any(visitor)
323    }
324    fn deserialize_f32<V>(self, visitor: V) -> Result<V::Value, Self::Error>
325    where
326        V: Visitor<'de>,
327    {
328        self.deserialize_any(visitor)
329    }
330    fn deserialize_f64<V>(self, visitor: V) -> Result<V::Value, Self::Error>
331    where
332        V: Visitor<'de>,
333    {
334        self.deserialize_any(visitor)
335    }
336    fn deserialize_char<V>(self, visitor: V) -> Result<V::Value, Self::Error>
337    where
338        V: Visitor<'de>,
339    {
340        self.deserialize_any(visitor)
341    }
342    fn deserialize_str<V>(self, visitor: V) -> Result<V::Value, Self::Error>
343    where
344        V: Visitor<'de>,
345    {
346        self.deserialize_any(visitor)
347    }
348    fn deserialize_string<V>(self, visitor: V) -> Result<V::Value, Self::Error>
349    where
350        V: Visitor<'de>,
351    {
352        self.deserialize_any(visitor)
353    }
354    fn deserialize_bytes<V>(self, visitor: V) -> Result<V::Value, Self::Error>
355    where
356        V: Visitor<'de>,
357    {
358        self.deserialize_any(visitor)
359    }
360    fn deserialize_byte_buf<V>(self, visitor: V) -> Result<V::Value, Self::Error>
361    where
362        V: Visitor<'de>,
363    {
364        self.deserialize_any(visitor)
365    }
366    fn deserialize_option<V>(self, visitor: V) -> Result<V::Value, Self::Error>
367    where
368        V: Visitor<'de>,
369    {
370        // DictionaryValues cannot be null, the only way to express an absent value in Dictionary is
371        // to omit the key entirely.
372        visitor.visit_some(self)
373    }
374    fn deserialize_unit<V>(self, visitor: V) -> Result<V::Value, Self::Error>
375    where
376        V: Visitor<'de>,
377    {
378        self.deserialize_any(visitor)
379    }
380    fn deserialize_unit_struct<V>(
381        self,
382        _name: &'static str,
383        visitor: V,
384    ) -> Result<V::Value, Self::Error>
385    where
386        V: Visitor<'de>,
387    {
388        self.deserialize_any(visitor)
389    }
390    fn deserialize_newtype_struct<V>(
391        self,
392        _name: &'static str,
393        visitor: V,
394    ) -> Result<V::Value, Self::Error>
395    where
396        V: Visitor<'de>,
397    {
398        self.deserialize_any(visitor)
399    }
400    fn deserialize_seq<V>(self, visitor: V) -> Result<V::Value, Self::Error>
401    where
402        V: Visitor<'de>,
403    {
404        self.deserialize_any(visitor)
405    }
406    fn deserialize_tuple<V>(self, _len: usize, visitor: V) -> Result<V::Value, Self::Error>
407    where
408        V: Visitor<'de>,
409    {
410        self.deserialize_any(visitor)
411    }
412    fn deserialize_tuple_struct<V>(
413        self,
414        _name: &'static str,
415        _len: usize,
416        visitor: V,
417    ) -> Result<V::Value, Self::Error>
418    where
419        V: Visitor<'de>,
420    {
421        self.deserialize_any(visitor)
422    }
423    fn deserialize_map<V>(self, visitor: V) -> Result<V::Value, Self::Error>
424    where
425        V: Visitor<'de>,
426    {
427        self.deserialize_any(visitor)
428    }
429    fn deserialize_struct<V>(
430        self,
431        _name: &'static str,
432        _fields: &'static [&'static str],
433        visitor: V,
434    ) -> Result<V::Value, Self::Error>
435    where
436        V: Visitor<'de>,
437    {
438        self.deserialize_any(visitor)
439    }
440    fn deserialize_enum<V>(
441        self,
442        _name: &'static str,
443        _variants: &'static [&'static str],
444        visitor: V,
445    ) -> Result<V::Value, Self::Error>
446    where
447        V: Visitor<'de>,
448    {
449        self.deserialize_any(visitor)
450    }
451    fn deserialize_identifier<V>(self, visitor: V) -> Result<V::Value, Self::Error>
452    where
453        V: Visitor<'de>,
454    {
455        self.deserialize_any(visitor)
456    }
457    fn deserialize_ignored_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
458    where
459        V: Visitor<'de>,
460    {
461        self.deserialize_any(visitor)
462    }
463}
464
465impl<'de> IntoDeserializer<'de> for ValueDeserializer<'de> {
466    type Deserializer = Self;
467
468    fn into_deserializer(self) -> Self::Deserializer {
469        self
470    }
471}
472
473/// A wrapper type that allows runners to specify that they want a basic field to be stored as
474/// a string and deserialized with that type's `FromStr` impl.
475// TODO(https://fxbug.dev/397443131) remove once basic types supported by Dictionary
476#[derive(Clone, Eq, PartialEq)]
477pub struct StoreAsString<T>(pub T);
478
479impl<T> std::ops::Deref for StoreAsString<T> {
480    type Target = T;
481    fn deref(&self) -> &Self::Target {
482        &self.0
483    }
484}
485
486impl<T: ToString> Serialize for StoreAsString<T> {
487    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
488    where
489        S: Serializer,
490    {
491        self.0.to_string().serialize(serializer)
492    }
493}
494
495impl<'de, T> Deserialize<'de> for StoreAsString<T>
496where
497    T: FromStr,
498    <T as FromStr>::Err: Display,
499{
500    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
501    where
502        D: Deserializer<'de>,
503    {
504        use serde::de::Error;
505        let s = String::deserialize(deserializer)?;
506        let inner = s.parse().map_err(|e| {
507            D::Error::custom(format!(
508                "couldn't parse '{s}' as a {}: {e}",
509                std::any::type_name::<T>()
510            ))
511        })?;
512        Ok(Self(inner))
513    }
514}
515
516impl<T> Default for StoreAsString<T>
517where
518    T: Default,
519{
520    fn default() -> Self {
521        Self(T::default())
522    }
523}
524
525impl<T> Debug for StoreAsString<T>
526where
527    T: Debug,
528{
529    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
530        self.0.fmt(f)
531    }
532}
533
534#[cfg(test)]
535mod tests {
536    use super::*;
537    use anyhow::Context as _;
538    use serde::de::DeserializeOwned;
539    use serde::Serialize;
540    use serde_json::json;
541    use std::fmt::Debug;
542    use std::path::Path;
543
544    #[fuchsia::test]
545    fn string_round_trip() {
546        #[derive(Debug, Deserialize, PartialEq, Serialize)]
547        struct Foo {
548            is_a_str: String,
549        }
550        let original = Foo { is_a_str: String::from("hello, world!") };
551        let roundtripped = round_trip_as_program(&original).unwrap();
552        assert_eq!(original, roundtripped);
553    }
554
555    #[fuchsia::test]
556    fn optional_string_round_trip() {
557        #[derive(Debug, Deserialize, PartialEq, Serialize)]
558        struct Foo {
559            is_a_str: Option<String>,
560        }
561        let original = Foo { is_a_str: Some(String::from("hello, world!")) };
562        let roundtripped = round_trip_as_program(&original).unwrap();
563        assert_eq!(original, roundtripped);
564    }
565
566    #[fuchsia::test]
567    fn char_round_trip() {
568        #[derive(Debug, Deserialize, PartialEq, Serialize)]
569        struct Foo {
570            is_a_char: char,
571        }
572        let original = Foo { is_a_char: 'x' };
573        let roundtripped = round_trip_as_program(&original).unwrap();
574        assert_eq!(original, roundtripped);
575    }
576
577    #[fuchsia::test]
578    fn string_list_round_trip() {
579        #[derive(Debug, Deserialize, PartialEq, Serialize)]
580        struct Foo {
581            is_a_list_of_strings: Vec<String>,
582        }
583        let original = Foo { is_a_list_of_strings: vec![String::from("foo"), String::from("bar")] };
584        let roundtripped = round_trip_as_program(&original).unwrap();
585        assert_eq!(original, roundtripped);
586    }
587
588    #[fuchsia::test]
589    fn object_list_round_trip() {
590        #[derive(Debug, Deserialize, PartialEq, Serialize)]
591        struct Foo {
592            is_a_list_of_objects: Vec<Bar>,
593        }
594        #[derive(Debug, Deserialize, PartialEq, Serialize)]
595        struct Bar {
596            is_a_string: String,
597        }
598        let original = Foo {
599            is_a_list_of_objects: vec![
600                Bar { is_a_string: String::from("hello, world!") },
601                Bar { is_a_string: String::from("another string") },
602                Bar { is_a_string: String::from("yet another string") },
603            ],
604        };
605        let roundtripped = round_trip_as_program(&original).unwrap();
606        assert_eq!(original, roundtripped);
607    }
608
609    #[fuchsia::test]
610    fn store_as_string_round_trips() {
611        #[derive(Debug, Deserialize, PartialEq, Serialize)]
612        struct Foo {
613            is_an_int: StoreAsString<u32>,
614        }
615        let original = Foo { is_an_int: StoreAsString(5) };
616        let roundtripped = round_trip_as_program(&original).unwrap();
617        assert_eq!(original, roundtripped);
618    }
619
620    #[fuchsia::test]
621    fn optional_store_as_string_round_trips() {
622        #[derive(Debug, Deserialize, PartialEq, Serialize)]
623        struct Foo {
624            #[serde(skip_serializing_if = "Option::is_none")]
625            is_an_int: Option<StoreAsString<u32>>,
626        }
627        let original_some = Foo { is_an_int: Some(StoreAsString(5)) };
628        let roundtripped_some = round_trip_as_program(&original_some).unwrap();
629        assert_eq!(original_some, roundtripped_some);
630
631        let original_none = Foo { is_an_int: None };
632        let roundtripped_none = round_trip_as_program(&original_none).unwrap();
633        assert_eq!(original_none, roundtripped_none);
634    }
635
636    #[fuchsia::test]
637    fn list_of_store_as_string_round_trip() {
638        #[derive(Debug, Deserialize, PartialEq, Serialize)]
639        struct Foo {
640            is_a_list: Vec<StoreAsString<u32>>,
641        }
642        let original_some = Foo { is_a_list: vec![StoreAsString(5), StoreAsString(6)] };
643        let roundtripped_some = round_trip_as_program(&original_some).unwrap();
644        assert_eq!(original_some, roundtripped_some);
645
646        let original_empty = Foo { is_a_list: vec![] };
647        let roundtripped_empty = round_trip_as_program(&original_empty).unwrap();
648        assert_eq!(original_empty, roundtripped_empty);
649    }
650
651    #[fuchsia::test]
652    fn mixed_fields_round_trip() {
653        #[derive(Debug, Deserialize, PartialEq, Serialize)]
654        struct Foo {
655            is_a_string: String,
656            is_a_list_of_strings: Vec<String>,
657            is_a_list_of_objects: Vec<Bar>,
658        }
659        #[derive(Debug, Deserialize, PartialEq, Serialize)]
660        struct Bar {
661            is_a_string: String,
662        }
663        let original = Foo {
664            is_a_string: String::from("hello, world!"),
665            is_a_list_of_strings: vec![String::from("foo"), String::from("bar")],
666            is_a_list_of_objects: vec![
667                Bar { is_a_string: String::from("hello, world!") },
668                Bar { is_a_string: String::from("another string") },
669                Bar { is_a_string: String::from("yet another string") },
670            ],
671        };
672        let roundtripped = round_trip_as_program(&original).unwrap();
673        assert_eq!(original, roundtripped);
674    }
675
676    macro_rules! unsupported_type_test {
677        ($t:tt) => {
678            paste::paste! {
679                #[test]
680                fn [<$t _not_supported>]() {
681                    #[derive(Debug, Deserialize, Serialize)]
682                    struct Foo {
683                        is_a_value: $t,
684                    }
685                    round_trip_as_program(&Foo { is_a_value: Default::default() })
686                        .expect_err("unsupported types must not be able to roundtrip");
687                }
688            }
689        };
690    }
691
692    // TODO(https://fxbug.dev/397443131) support bools natively
693    unsupported_type_test!(bool);
694    // TODO(https://fxbug.dev/397443131) support i8 natively
695    unsupported_type_test!(i8);
696    // TODO(https://fxbug.dev/397443131) support i16 natively
697    unsupported_type_test!(i16);
698    // TODO(https://fxbug.dev/397443131) support i32 natively
699    unsupported_type_test!(i32);
700    // TODO(https://fxbug.dev/397443131) support i64 natively
701    unsupported_type_test!(i64);
702    // TODO(https://fxbug.dev/397443131) support u8 natively
703    unsupported_type_test!(u8);
704    // TODO(https://fxbug.dev/397443131) support u16 natively
705    unsupported_type_test!(u16);
706    // TODO(https://fxbug.dev/397443131) support u32 natively
707    unsupported_type_test!(u32);
708    // TODO(https://fxbug.dev/397443131) support u64 natively
709    unsupported_type_test!(u64);
710    // TODO(https://fxbug.dev/397443131) support f32 natively
711    unsupported_type_test!(f32);
712    // TODO(https://fxbug.dev/397443131) support f64 natively
713    unsupported_type_test!(f64);
714
715    #[track_caller]
716    fn round_trip_as_program<T: Debug + DeserializeOwned + Serialize>(t: &T) -> anyhow::Result<T> {
717        let file_path = Path::new("/fake/path/for/roundtrip/test");
718        let mut program_json = serde_json::to_value(&t).context("serializing T as json")?;
719        program_json
720            .as_object_mut()
721            .context("test structs must serialize as objects")?
722            .insert("runner".to_string(), json!("serde_roundtrip_test"));
723
724        let manifest_str = serde_json::to_string_pretty(&json!({
725            "program": program_json,
726        }))
727        .context("serializing generated document as JSON")?;
728
729        let document = cml::parse_one_document(&manifest_str, file_path)
730            .context("parsing generated document as CML")?;
731        let compiled = cml::compile(&document, cml::CompileOptions::new().file(file_path))
732            .context("compiling generated CML")?;
733
734        let program = compiled
735            .program
736            .as_ref()
737            .context("getting program block from compiled manifest")?
738            .info
739            .as_ref()
740            .context("getting compiled info from program block")?;
741
742        deserialize_program::<T>(&program).context("deserializing T from program block")
743    }
744}