sui_rpc/field/
field_mask_tree.rs

1use super::is_valid_path;
2use super::FieldMaskUtil;
3use super::FIELD_PATH_SEPARATOR;
4use super::FIELD_PATH_WILDCARD;
5use super::FIELD_SEPARATOR;
6
7use prost_types::FieldMask;
8use std::collections::BTreeMap;
9
10#[derive(Clone, Debug, Default)]
11pub struct FieldMaskTree {
12    wildcard: bool,
13    root: Node,
14}
15
16#[derive(Clone, Debug, Default)]
17struct Node {
18    children: BTreeMap<String, Node>,
19}
20
21impl FieldMaskTree {
22    pub fn new_wildcard() -> Self {
23        Self {
24            wildcard: true,
25            root: Default::default(),
26        }
27    }
28
29    pub fn add_field_path(&mut self, path: &str) -> &mut Self {
30        if self.wildcard || !is_valid_path(path) {
31            return self;
32        }
33
34        if path == FIELD_PATH_WILDCARD {
35            self.wildcard = true;
36            self.root.children.clear();
37            return self;
38        }
39
40        let root = std::ptr::from_ref(&self.root);
41        let mut node = &mut self.root;
42        let mut create_new_branch = false;
43        for component in path.split(FIELD_SEPARATOR) {
44            if !create_new_branch && !std::ptr::eq(root, node) && node.children.is_empty() {
45                return self;
46            }
47
48            node = node
49                .children
50                .entry(component.to_owned())
51                .or_insert_with(|| {
52                    create_new_branch = true;
53                    Node::default()
54                });
55        }
56
57        node.children.clear();
58        self
59    }
60
61    pub fn from_field_mask(mask: &FieldMask) -> Self {
62        let mut tree = Self::default();
63        for path in &mask.paths {
64            tree.add_field_path(path);
65        }
66        tree
67    }
68
69    pub fn to_field_mask(&self) -> FieldMask {
70        if self.root.children.is_empty() {
71            return FieldMask::default();
72        }
73
74        let mut paths = Vec::new();
75        Self::collect_field_paths(&self.root, &mut String::new(), &mut paths);
76        FieldMask { paths }
77    }
78
79    fn collect_field_paths(node: &Node, path: &mut String, paths: &mut Vec<String>) {
80        if node.children.is_empty() {
81            paths.push(path.clone());
82            return;
83        }
84
85        let parent_path_len = path.len();
86        for (part, child) in node.children.iter() {
87            if path.is_empty() {
88                path.push_str(part);
89            } else {
90                path.push(FIELD_SEPARATOR);
91                path.push_str(part);
92            };
93            Self::collect_field_paths(child, path, paths);
94            path.truncate(parent_path_len);
95        }
96    }
97
98    /// Checks if the provided path is contained in this FieldMaskTree.
99    ///
100    /// A path is considered a match and contained by this tree if it is a prefix for any contained
101    /// paths, including if it is an exact match.
102    ///
103    /// ```
104    /// # use sui_rpc::field::FieldMaskTree;
105    /// let mut tree = FieldMaskTree::default();
106    /// tree.add_field_path("foo.bar");
107    ///
108    /// assert!(tree.contains("foo"));
109    /// assert!(tree.contains("foo.bar"));
110    /// assert!(!tree.contains("foo.baz"));
111    /// ```
112    pub fn contains<P: AsRef<str>>(&self, path: P) -> bool {
113        let path = path.as_ref();
114
115        if path.is_empty() {
116            return false;
117        }
118
119        if self.wildcard {
120            return true;
121        }
122
123        let mut node = &self.root;
124        for component in path.split(FIELD_SEPARATOR) {
125            // If this isn't the root node, and there are no sub-paths, then this path has been
126            // matched and we can return a hit
127            if !std::ptr::eq(node, &self.root) && node.children.is_empty() {
128                return true;
129            }
130
131            if let Some(child) = node.children.get(component) {
132                node = child;
133            } else {
134                return false;
135            }
136        }
137
138        // We found a matching node for this path. This node may be empty or have leaf children. In
139        // either case the provided patch is a "match" and is contained by this tree.
140        true
141    }
142
143    pub fn subtree<P: AsRef<str>>(&self, path: P) -> Option<Self> {
144        let path = path.as_ref();
145
146        if path.is_empty() {
147            return None;
148        }
149
150        if self.wildcard {
151            return Some(self.clone());
152        }
153
154        let mut node = &self.root;
155        for component in path.split(FIELD_SEPARATOR) {
156            if let Some(child) = node.children.get(component) {
157                node = child;
158            } else {
159                return None;
160            }
161        }
162
163        if std::ptr::eq(node, &self.root) {
164            None
165        } else {
166            Some(Self {
167                wildcard: node.children.is_empty(),
168                root: node.clone(),
169            })
170        }
171    }
172}
173
174impl From<FieldMask> for FieldMaskTree {
175    fn from(mask: FieldMask) -> Self {
176        Self::from_field_mask(&mask)
177    }
178}
179
180impl From<FieldMaskTree> for FieldMask {
181    fn from(tree: FieldMaskTree) -> Self {
182        tree.to_field_mask()
183    }
184}
185
186impl std::fmt::Display for FieldMaskTree {
187    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
188        FieldMaskUtil::display(&self.to_field_mask()).fmt(f)
189    }
190}
191
192impl std::str::FromStr for FieldMaskTree {
193    type Err = std::convert::Infallible;
194
195    fn from_str(s: &str) -> Result<Self, Self::Err> {
196        let mut tree = Self::default();
197
198        for path in s.split(FIELD_PATH_SEPARATOR) {
199            tree.add_field_path(path);
200        }
201
202        Ok(tree)
203    }
204}
205
206#[cfg(test)]
207mod tests {
208    use super::*;
209
210    #[test]
211    fn test_add_field_path() {
212        let mut tree = FieldMaskTree::default();
213
214        assert!(tree.to_string().is_empty());
215        tree.add_field_path("");
216        assert!(tree.to_string().is_empty());
217
218        tree.add_field_path("foo");
219        assert_eq!(tree.to_string(), "foo");
220        // redundant path
221        tree.add_field_path("foo");
222        assert_eq!(tree.to_string(), "foo");
223
224        tree.add_field_path("bar.baz");
225        assert_eq!(tree.to_string(), "bar.baz,foo");
226
227        // redundant sub-path
228        tree.add_field_path("foo.bar");
229        assert_eq!(tree.to_string(), "bar.baz,foo");
230
231        // new sub-path
232        tree.add_field_path("bar.quz");
233        assert_eq!(tree.to_string(), "bar.baz,bar.quz,foo");
234
235        // path that matches several existing sub-paths
236        tree.add_field_path("bar");
237        assert_eq!(tree.to_string(), "bar,foo");
238    }
239
240    #[test]
241    fn test_contains() {
242        let mut tree = FieldMaskTree::default();
243
244        assert!(!tree.contains("foo"));
245        assert!(!tree.contains("foo.bar"));
246
247        tree.add_field_path("foo.bar");
248
249        assert!(tree.contains("foo"));
250        assert!(tree.contains("foo.bar"));
251        assert!(!tree.contains("foo.baz"));
252        assert!(!tree.contains("foobar"));
253    }
254}