Skip to main content

selectors_ext/
inspect.rs

1// Copyright 2026 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
5//! A serde module for Inspect selectors, to be used in the "with" field
6//! attribute.
7//!
8//! ```
9//! #[derive(Serialize, Deserialize)]
10//! struct Config {
11//!     #[serde(with="selectors_ext::inspect")]
12//!     selectors: Vec<fidl_fuchsia_diagnostics::Selector>,
13//! }
14//! ```
15
16use serde::de::{Error, SeqAccess, Visitor};
17use serde::ser::SerializeSeq;
18use serde::{Deserializer, Serializer};
19
20const PREFIX: &str = "INSPECT:";
21
22pub fn serialize<S>(
23    selectors: &Vec<fidl_fuchsia_diagnostics::Selector>,
24    serializer: S,
25) -> Result<S::Ok, S::Error>
26where
27    S: Serializer,
28{
29    let mut seq = serializer.serialize_seq(Some(selectors.len()))?;
30
31    for selector in selectors {
32        seq.serialize_element(&format!(
33            "{PREFIX}{}",
34            &selectors::selector_to_string(selector, selectors::SelectorDisplayOptions::default())
35                .map_err(serde::ser::Error::custom)?
36        ))?;
37    }
38
39    seq.end()
40}
41
42pub fn deserialize<'de, D>(
43    deserializer: D,
44) -> Result<Vec<fidl_fuchsia_diagnostics::Selector>, D::Error>
45where
46    D: Deserializer<'de>,
47{
48    deserializer.deserialize_seq(SelectorsVisitor)
49}
50
51struct SelectorsVisitor;
52
53impl<'de> Visitor<'de> for SelectorsVisitor {
54    type Value = Vec<fidl_fuchsia_diagnostics::Selector>;
55
56    fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
57        write!(formatter, "should be a list of string selectors starting with {}", PREFIX)
58    }
59
60    fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
61    where
62        A: SeqAccess<'de>,
63    {
64        let mut selectors = Vec::new();
65        while let Some(v) = seq.next_element::<String>()? {
66            if v.len() < PREFIX.len() || &v[..PREFIX.len()] != PREFIX {
67                return Err(A::Error::custom(format!(
68                    "Expected selector with prefix \"{}\", got \"{v}\"",
69                    PREFIX
70                )));
71            } else {
72                selectors.push(
73                    selectors::parse_selector::<selectors::VerboseError>(&v[PREFIX.len()..])
74                        .map_err(|e| A::Error::custom(e))?,
75                );
76            }
77        }
78        Ok(selectors)
79    }
80}
81
82#[cfg(test)]
83mod tests {
84    use super::*;
85    use fidl_fuchsia_diagnostics::Selector;
86    use pretty_assertions::assert_eq;
87    use serde::{Deserialize, Serialize};
88    use test_case::test_case;
89
90    #[derive(Debug, Serialize, Deserialize, PartialEq)]
91    #[serde(transparent)]
92    struct TestConfig {
93        #[serde(with = "crate::inspect")]
94        selectors: Vec<Selector>,
95    }
96
97    #[test_case("a/b:c:d" ; "with_basic_full_selector")]
98    #[test_case("a/b:c" ; "with_basic_partial_selector")]
99    #[test_case(r"a/b:c/d\/e:f" ; "with_escaped_forward_slash")]
100    #[test_case(r"a/b:[name=cd-e]f:g" ; "with_non_default_name")]
101    #[test_case(r#"a:[name="bc-d"]e:f"# ; "with_unneeded_name_quotes")]
102    #[test_case(r#"a:[name="b[]c"]d:e"# ; "with_needed_name_quotes")]
103    #[test_case("a/b:[...]c:d" ; "with_all_names")]
104    #[test_case(r#"a/b:[name=c, name="d", name="f[]g"]h:i"# ; "with_name_list")]
105    #[test_case(r"a\:b/c:d:e" ; "with_collection")]
106    #[test_case(r"a/b/c*d:e:f" ; "with_wildcard_component")]
107    #[test_case(r"a/b:c*/d:e" ; "with_wildcard_tree")]
108    #[test_case(r"a/b:c\*/d:e" ; "with_escaped_wildcard_tree")]
109    #[test_case(r"a/b/c/d:e/f:g*" ; "with_wildcard_property")]
110    #[test_case("a/b/c/d:e/f/g/h:k" ; "with_deep_nesting")]
111    #[fuchsia::test]
112    fn test_serialization(selector: &str) {
113        // Escape double quotes and forward slashes then wrap in double quotes.
114        let selector_escaped = serde_json::to_string(&format!("{PREFIX}{selector}")).unwrap();
115
116        let deserialized: TestConfig =
117            serde_json::from_str(&format!("[{selector_escaped}]")).unwrap();
118        let serialized = serde_json::to_string(&deserialized).unwrap();
119        let deserialized_again: TestConfig = serde_json::from_str(&serialized).unwrap();
120        assert_eq!(deserialized, deserialized_again);
121        assert_eq!(deserialized.selectors, vec![selectors::parse_verbose(selector).unwrap()]);
122    }
123}