Skip to main content

sui_rpc/field/
field_mask_tree.rs

1use super::FIELD_PATH_SEPARATOR;
2use super::FIELD_PATH_WILDCARD;
3use super::FIELD_SEPARATOR;
4use super::FieldMaskUtil;
5use super::is_valid_path;
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            node = node.children.get(component)?;
157        }
158
159        if std::ptr::eq(node, &self.root) {
160            None
161        } else {
162            Some(Self {
163                wildcard: node.children.is_empty(),
164                root: node.clone(),
165            })
166        }
167    }
168}
169
170impl From<FieldMask> for FieldMaskTree {
171    fn from(mask: FieldMask) -> Self {
172        Self::from_field_mask(&mask)
173    }
174}
175
176impl From<FieldMaskTree> for FieldMask {
177    fn from(tree: FieldMaskTree) -> Self {
178        tree.to_field_mask()
179    }
180}
181
182impl std::fmt::Display for FieldMaskTree {
183    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
184        FieldMaskUtil::display(&self.to_field_mask()).fmt(f)
185    }
186}
187
188impl std::str::FromStr for FieldMaskTree {
189    type Err = std::convert::Infallible;
190
191    fn from_str(s: &str) -> Result<Self, Self::Err> {
192        let mut tree = Self::default();
193
194        for path in s.split(FIELD_PATH_SEPARATOR) {
195            tree.add_field_path(path);
196        }
197
198        Ok(tree)
199    }
200}
201
202#[cfg(test)]
203mod tests {
204    use super::*;
205
206    #[test]
207    fn test_add_field_path() {
208        let mut tree = FieldMaskTree::default();
209
210        assert!(tree.to_string().is_empty());
211        tree.add_field_path("");
212        assert!(tree.to_string().is_empty());
213
214        tree.add_field_path("foo");
215        assert_eq!(tree.to_string(), "foo");
216        // redundant path
217        tree.add_field_path("foo");
218        assert_eq!(tree.to_string(), "foo");
219
220        tree.add_field_path("bar.baz");
221        assert_eq!(tree.to_string(), "bar.baz,foo");
222
223        // redundant sub-path
224        tree.add_field_path("foo.bar");
225        assert_eq!(tree.to_string(), "bar.baz,foo");
226
227        // new sub-path
228        tree.add_field_path("bar.quz");
229        assert_eq!(tree.to_string(), "bar.baz,bar.quz,foo");
230
231        // path that matches several existing sub-paths
232        tree.add_field_path("bar");
233        assert_eq!(tree.to_string(), "bar,foo");
234    }
235
236    #[test]
237    fn test_contains() {
238        let mut tree = FieldMaskTree::default();
239
240        assert!(!tree.contains("foo"));
241        assert!(!tree.contains("foo.bar"));
242
243        tree.add_field_path("foo.bar");
244
245        assert!(tree.contains("foo"));
246        assert!(tree.contains("foo.bar"));
247        assert!(!tree.contains("foo.baz"));
248        assert!(!tree.contains("foobar"));
249    }
250}