cyfs_lib/prelude/
global_state_common.rs1use crate::base::*;
2use crate::GlobalStateCategory;
3use cyfs_base::*;
4
5use std::borrow::Cow;
6use std::{fmt, str::FromStr};
7
8#[derive(Clone, Debug, Eq, PartialEq)]
9pub enum RequestGlobalStateRoot {
10 GlobalRoot(ObjectId),
11 DecRoot(ObjectId),
12}
13
14impl fmt::Display for RequestGlobalStateRoot {
15 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
16 match &self {
17 Self::GlobalRoot(root) => {
18 write!(f, "root:{}", root)
19 }
20 Self::DecRoot(root) => {
21 write!(f, "dec-root:{}", root)
22 }
23 }
24 }
25}
26
27#[derive(Clone, Eq, PartialEq)]
28pub struct RequestGlobalStatePath {
29 pub global_state_category: Option<GlobalStateCategory>,
31
32 pub global_state_root: Option<RequestGlobalStateRoot>,
34
35 pub dec_id: Option<ObjectId>,
37
38 pub req_path: Option<String>,
40}
41
42impl fmt::Display for RequestGlobalStatePath {
43 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
44 write!(f, "{}", self.format_string())
45 }
46}
47impl fmt::Debug for RequestGlobalStatePath {
48 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
49 write!(f, "{}", self.format_string())
50 }
51}
52
53impl RequestGlobalStatePath {
54 pub fn new(dec_id: Option<ObjectId>, req_path: Option<impl Into<String>>) -> Self {
55 Self {
56 global_state_category: None,
57 global_state_root: None,
58 dec_id,
59 req_path: req_path.map(|v| v.into()),
60 }
61 }
62
63 pub fn new_system_dec(req_path: Option<impl Into<String>>) -> Self {
64 Self::new(Some(cyfs_core::get_system_dec_app().to_owned()), req_path)
65 }
66
67 pub fn set_root(&mut self, root: ObjectId) {
68 self.global_state_root = Some(RequestGlobalStateRoot::GlobalRoot(root));
69 }
70 pub fn set_dec_root(&mut self, dec_root: ObjectId) {
71 self.global_state_root = Some(RequestGlobalStateRoot::DecRoot(dec_root));
72 }
73
74 pub fn category(&self) -> GlobalStateCategory {
75 match &self.global_state_category {
76 Some(v) => *v,
77 None => GlobalStateCategory::RootState,
78 }
79 }
80
81 pub fn req_path(&self) -> Cow<str> {
82 match &self.req_path {
83 Some(v) => Cow::Borrowed(v.as_str()),
84 None => Cow::Borrowed("/"),
85 }
86 }
87
88 pub fn dec<'a>(&'a self, source: &'a RequestSourceInfo) -> &ObjectId {
90 match &self.dec_id {
91 Some(id) => id,
92 None => &source.dec,
93 }
94 }
95
96 pub fn parse(req_path: &str) -> BuckyResult<Self> {
103 let segs: Vec<&str> = req_path
104 .trim_start_matches('/')
105 .split('/')
106 .filter(|seg| !seg.is_empty())
107 .collect();
108
109 let mut index = 0;
110 let global_state_category = if index < segs.len() {
111 let seg = segs[index];
112 match seg {
113 "root-state" => {
114 index += 1;
115 Some(GlobalStateCategory::RootState)
116 }
117 "local-cache" => {
118 index += 1;
119 Some(GlobalStateCategory::LocalCache)
120 }
121
122 _ => None,
123 }
124 } else {
125 None
126 };
127
128 let global_state_root = if index < segs.len() {
129 let seg = segs[index];
130 match seg {
131 "current" => {
132 index += 1;
133 None
134 }
135 _ if seg.starts_with("root:") => {
136 index += 1;
137
138 let id = seg.strip_prefix("root:").unwrap();
139 let root = ObjectId::from_str(&id).map_err(|e| {
140 let msg = format!("invalid req_path's root id: {}, {}", seg, e);
141 error!("{msg}");
142 BuckyError::new(BuckyErrorCode::InvalidFormat, msg)
143 })?;
144
145 Some(RequestGlobalStateRoot::GlobalRoot(root))
146 }
147 _ if seg.starts_with("dec-root:") => {
148 index += 1;
149
150 let id = seg.strip_prefix("dec-root:").unwrap();
151 let root = ObjectId::from_str(&id).map_err(|e| {
152 let msg = format!("invalid req_path's dec root id: {}, {}", seg, e);
153 error!("{msg}");
154 BuckyError::new(BuckyErrorCode::InvalidFormat, msg)
155 })?;
156
157 Some(RequestGlobalStateRoot::DecRoot(root))
158 }
159 _ => None,
160 }
161 } else {
162 None
163 };
164
165 let dec_id = if index < segs.len() {
166 let seg = segs[index];
168 if OBJECT_ID_BASE58_RANGE.contains(&seg.len()) {
169 let dec_id = ObjectId::from_str(seg).map_err(|e| {
170 let msg = format!("invalid req_path's dec root id: {}, {}", seg, e);
171 error!("{msg}");
172 BuckyError::new(BuckyErrorCode::InvalidFormat, msg)
173 })?;
174 index += 1;
175 Some(dec_id)
176 } else {
177 None
178 }
179 } else {
180 None
181 };
182
183 let req_path = if index < segs.len() {
184 let path = segs[index..].join("/");
185 Some(format!("/{}/", path))
186 } else {
187 None
188 };
189
190 Ok(Self {
191 global_state_category,
192 global_state_root,
193 dec_id,
194 req_path,
195 })
196 }
197
198 pub fn format_string(&self) -> String {
199 let mut segs: Vec<Cow<str>> = vec![];
200 if let Some(v) = &self.global_state_category {
201 segs.push(Cow::Borrowed(v.as_str()));
202 }
203
204 if let Some(root) = &self.global_state_root {
205 segs.push(Cow::Owned(root.to_string()));
206 }
207
208 if let Some(id) = &self.dec_id {
209 let seg = Cow::Owned(id.to_string());
210 segs.push(seg);
211 };
212
213 if let Some(path) = &self.req_path {
214 segs.push(Cow::Borrowed(path.trim_start_matches('/')));
215 }
216
217 format!("/{}", segs.join("/"))
218 }
219
220 pub fn match_target(&self, target: &Self) -> bool {
221 if self.category() != target.category() {
222 return false;
223 }
224
225 if self.global_state_root != target.global_state_root {
226 return false;
227 }
228
229 if self.dec_id != target.dec_id {
230 return false;
231 }
232
233 target.req_path().starts_with(&*self.req_path())
234 }
235}
236
237impl FromStr for RequestGlobalStatePath {
238 type Err = BuckyError;
239
240 fn from_str(s: &str) -> Result<Self, Self::Err> {
241 Self::parse(s)
242 }
243}
244
245#[cfg(test)]
246mod test {
247 use super::*;
248
249 #[test]
250 fn test() {
251 let mut root = RequestGlobalStatePath {
252 global_state_category: None,
253 global_state_root: None,
254 dec_id: None,
255 req_path: Some("/a/b/".to_owned()),
256 };
257
258 let s = root.format_string();
259 println!("{}", s);
260 let r = RequestGlobalStatePath::parse(&s).unwrap();
261 assert_eq!(root, r);
262
263 root.dec_id = Some(ObjectId::default());
264 let s = root.format_string();
265 println!("{}", s);
266
267 root.global_state_category = Some(GlobalStateCategory::RootState);
268 let s = root.format_string();
269 println!("{}", s);
270 let r = RequestGlobalStatePath::parse(&s).unwrap();
271 assert_eq!(root, r);
272
273 root.global_state_root = Some(RequestGlobalStateRoot::DecRoot(ObjectId::default()));
274 let s = root.format_string();
275 println!("{}", s);
276 let r = RequestGlobalStatePath::parse(&s).unwrap();
277 assert_eq!(root, r);
278
279 root.req_path = None;
280 let s = root.format_string();
281 println!("{}", s);
282 let r = RequestGlobalStatePath::parse(&s).unwrap();
283 assert_eq!(root, r);
284
285 root.req_path = Some("/a/".to_owned());
286 let s = root.format_string();
287 println!("{}", s);
288 let r = RequestGlobalStatePath::parse(&s).unwrap();
289 assert_eq!(root, r);
290
291 root.dec_id = Some(cyfs_core::get_system_dec_app().to_owned());
292 let s = root.format_string();
293 println!("{}", s);
294 let r = RequestGlobalStatePath::parse(&s).unwrap();
295 assert_eq!(r.dec_id, Some(cyfs_core::get_system_dec_app().to_owned()));
296
297 let root = RequestGlobalStatePath {
298 global_state_category: None,
299 global_state_root: None,
300 dec_id: None,
301 req_path: None,
302 };
303
304 let s = root.format_string();
305 println!("{}", s);
306 let r = RequestGlobalStatePath::parse(&s).unwrap();
307 assert_eq!(root, r);
308 }
309}