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