sui_rpc/field/
mod.rs

1mod field_mask_tree;
2mod field_mask_util;
3
4pub use field_mask_tree::FieldMaskTree;
5pub use field_mask_util::FieldMaskUtil;
6pub use prost_types::FieldMask;
7
8/// Separator between field paths when a FieldMask is encoded as a string
9pub const FIELD_PATH_SEPARATOR: char = ',';
10
11/// Separator between fields in a field path
12pub const FIELD_SEPARATOR: char = '.';
13
14pub const FIELD_PATH_WILDCARD: &str = "*";
15
16fn is_valid_path(path: &str) -> bool {
17    if path == FIELD_PATH_WILDCARD {
18        return true;
19    }
20
21    path.split(FIELD_SEPARATOR).all(is_valid_path_component)
22}
23
24// A valid path component needs to be a valid protobuf identifier which is defined by the
25// following:
26//
27// ```
28// letter        = "A" … "Z" | "a" … "z" | "_" .
29// decimal_digit = "0" … "9"
30// identifier = letter { letter | decimal_digit }
31// ```
32fn is_valid_path_component(component: &str) -> bool {
33    if component.is_empty() || component == "_" {
34        return false;
35    }
36
37    let component = component.as_bytes();
38
39    if !(component[0].is_ascii_alphabetic() || component[0] == b'_') {
40        return false;
41    }
42
43    for &byte in &component[1..] {
44        if !(byte.is_ascii_alphabetic() || byte.is_ascii_digit() || byte == b'_') {
45            return false;
46        }
47    }
48
49    true
50}
51
52pub trait MessageFields {
53    const FIELDS: &'static [&'static MessageField];
54}
55
56pub struct MessageField {
57    pub name: &'static str,
58    pub json_name: &'static str,
59    pub number: i32,
60    pub message_fields: Option<&'static [&'static MessageField]>,
61}
62
63impl AsRef<str> for MessageField {
64    fn as_ref(&self) -> &str {
65        self.name
66    }
67}
68
69#[doc(hidden)]
70impl MessageField {
71    pub const fn new(name: &'static str) -> Self {
72        Self {
73            name,
74            json_name: "",
75            number: 0,
76            message_fields: None,
77        }
78    }
79
80    pub const fn with_message_fields(
81        mut self,
82        message_fields: &'static [&'static MessageField],
83    ) -> Self {
84        self.message_fields = Some(message_fields);
85        self
86    }
87}
88
89#[cfg(test)]
90mod tests {
91    use super::*;
92
93    #[test]
94    fn test_valid_path_component() {
95        let cases = [
96            ("foo", true),
97            ("_", false),
98            ("", false),
99            ("_abc", true),
100            ("BAR", true),
101            ("foo.bar", false),
102        ];
103
104        for (case, expected) in cases {
105            assert_eq!(is_valid_path_component(case), expected);
106        }
107    }
108
109    #[test]
110    fn test_valid_path() {
111        let cases = [
112            ("*", true),
113            ("**", false),
114            ("foo.bar", true),
115            ("foo.bar.baz", true),
116            ("_", false),
117            (".", false),
118            ("", false),
119            ("_abc", true),
120            ("BAR", true),
121        ];
122
123        for (case, expected) in cases {
124            assert_eq!(is_valid_path(case), expected);
125        }
126    }
127}