google_apis_common/
field_mask.rs1use std::fmt::{Display, Formatter};
2use std::str::FromStr;
3
4use serde::{Deserialize, Deserializer, Serialize, Serializer};
5
6fn titlecase(source: &str, dest: &mut String) {
7 let mut underscore = false;
8 for c in source.chars() {
9 if c == '_' {
10 underscore = true;
11 } else if underscore {
12 dest.push(c.to_ascii_uppercase());
13 underscore = false;
14 } else {
15 dest.push(c);
16 }
17 }
18}
19
20fn snakecase(source: &str) -> String {
21 let mut dest = String::with_capacity(source.len() + 5);
22 for c in source.chars() {
23 if c.is_ascii_uppercase() {
24 dest.push('_');
25 dest.push(c.to_ascii_lowercase());
26 } else {
27 dest.push(c);
28 }
29 }
30 dest
31}
32
33#[derive(Clone, Debug, Default, PartialEq, Eq)]
35pub struct FieldMask(Vec<String>);
36
37impl FieldMask {
38 pub fn new<S: AsRef<str>>(values: &[S]) -> Self {
41 Self(values.iter().map(|s| snakecase(s.as_ref())).collect())
42 }
43}
44
45impl Serialize for FieldMask {
46 fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
47 where
48 S: Serializer,
49 {
50 s.serialize_str(self.to_string().as_str())
51 }
52}
53
54impl<'de> Deserialize<'de> for FieldMask {
55 fn deserialize<D>(deserializer: D) -> Result<FieldMask, D::Error>
56 where
57 D: Deserializer<'de>,
58 {
59 let s: &str = Deserialize::deserialize(deserializer)?;
60 Ok(FieldMask::from_str(s).unwrap())
61 }
62}
63
64impl FromStr for FieldMask {
65 type Err = std::convert::Infallible;
66 fn from_str(s: &str) -> Result<Self, Self::Err> {
67 let mut in_quotes = false;
68 let mut prev_ind = 0;
69 let mut paths = Vec::new();
70 for (i, c) in s.char_indices() {
71 if c == '`' {
72 in_quotes = !in_quotes;
73 } else if in_quotes {
74 continue;
75 } else if c == ',' {
76 paths.push(snakecase(&s[prev_ind..i]));
77 prev_ind = i + 1;
78 }
79 }
80 paths.push(snakecase(&s[prev_ind..]));
81 Ok(FieldMask(paths))
82 }
83}
84
85impl Display for FieldMask {
86 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
87 let mut repr = String::new();
88 for path in &self.0 {
89 titlecase(path, &mut repr);
90 repr.push(',');
91 }
92 repr.pop();
93 f.write_str(&repr)
94 }
95}
96
97#[cfg(test)]
98mod test {
99 use crate::field_mask::FieldMask;
100 use serde::{Deserialize, Serialize};
101
102 #[derive(Serialize, Deserialize, Debug, PartialEq)]
103 struct FieldMaskWrapper {
104 fields: Option<FieldMask>,
105 }
106
107 #[test]
108 fn field_mask_roundtrip() {
109 let wrapper = FieldMaskWrapper {
110 fields: Some(FieldMask(vec![
111 "user.display_name".to_string(),
112 "photo".to_string(),
113 ])),
114 };
115 let json_repr = &serde_json::to_string(&wrapper);
116 assert!(json_repr.is_ok(), "serialization should succeed");
117 assert_eq!(
118 wrapper,
119 serde_json::from_str(r#"{"fields": "user.displayName,photo"}"#).unwrap()
120 );
121 assert_eq!(
122 wrapper,
123 serde_json::from_str(json_repr.as_ref().unwrap()).unwrap(),
124 "round trip should succeed"
125 );
126 }
127
128 #[test]
129 fn test_empty_wrapper() {
130 assert_eq!(
131 FieldMaskWrapper { fields: None },
132 serde_json::from_str("{}").unwrap()
133 );
134 }
135}