saluki_config/
space_separated.rs1use std::fmt;
9
10use serde::de::{self, Deserializer, SeqAccess, Visitor};
11
12pub fn deserialize_space_separated_or_seq<'de, D>(deserializer: D) -> Result<Vec<String>, D::Error>
14where
15 D: Deserializer<'de>,
16{
17 struct SpaceSeparatedOrSeq;
18
19 impl<'de> Visitor<'de> for SpaceSeparatedOrSeq {
20 type Value = Vec<String>;
21
22 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
23 formatter.write_str("a sequence or a space-separated string")
24 }
25
26 fn visit_str<E: de::Error>(self, v: &str) -> Result<Vec<String>, E> {
27 Ok(v.split_whitespace().map(str::to_owned).collect())
28 }
29
30 fn visit_seq<A: SeqAccess<'de>>(self, mut seq: A) -> Result<Vec<String>, A::Error> {
31 let mut values = Vec::new();
32 while let Some(v) = seq.next_element()? {
33 values.push(v);
34 }
35 Ok(values)
36 }
37 }
38
39 deserializer.deserialize_any(SpaceSeparatedOrSeq)
40}
41
42pub fn deserialize_opt_space_separated_or_seq<'de, D>(deserializer: D) -> Result<Option<Vec<String>>, D::Error>
46where
47 D: Deserializer<'de>,
48{
49 deserialize_space_separated_or_seq(deserializer).map(Some)
50}
51
52#[cfg(test)]
53mod tests {
54 use serde::Deserialize;
55 use serde_json::json;
56
57 use super::*;
58
59 #[derive(Deserialize)]
60 struct Holder {
61 #[serde(deserialize_with = "deserialize_space_separated_or_seq")]
62 values: Vec<String>,
63 }
64
65 #[test]
66 fn deserializes_space_separated_strings_and_sequences() {
67 let cases: &[(&str, serde_json::Value, Vec<&str>)] = &[
70 (
71 "space-separated string",
72 json!("host1 host2 host3"),
73 vec!["host1", "host2", "host3"],
74 ),
75 (
76 "surrounding and repeated whitespace collapses",
77 json!(" host1 host2 "),
78 vec!["host1", "host2"],
79 ),
80 ("empty string yields an empty list", json!(""), vec![]),
81 (
82 "sequence form is preserved verbatim",
83 json!(["host1", "host2"]),
84 vec!["host1", "host2"],
85 ),
86 ];
87
88 for (description, input, expected) in cases {
89 let holder: Holder =
90 serde_json::from_value(json!({ "values": input })).unwrap_or_else(|e| panic!("{description}: {e}"));
91 assert_eq!(holder.values, *expected, "case: {description}");
92 }
93 }
94}