1use crate::{
2 core::{
3 bexter::{rawify, tables as bexter, Bext},
4 matter::{tables as matter, Matter},
5 sadder::Sadder,
6 saider::Saider,
7 serder::Serder,
8 util::REB64_STRING,
9 },
10 data::Value,
11 error::{err, Error, Result},
12};
13
14use lazy_static::lazy_static;
15use regex::Regex;
16
17#[derive(Debug, Clone, PartialEq)]
18pub struct Pather {
19 code: String,
20 raw: Vec<u8>,
21 size: u32,
22}
23
24impl Default for Pather {
25 fn default() -> Self {
26 Pather { code: matter::Codex::StrB64_L0.to_string(), raw: vec![], size: 0 }
27 }
28}
29
30fn validate_code(code: &str) -> Result<()> {
31 if !bexter::Codex::has_code(code) {
32 return err!(Error::UnexpectedCode(code.to_string()));
33 }
34
35 Ok(())
36}
37
38fn pather_from_bext(bext: &str, code: &str) -> Result<Pather> {
39 lazy_static! {
40 static ref REB64: Regex = Regex::new(REB64_STRING).unwrap();
41 }
42
43 if !REB64.is_match(bext) {
44 return err!(Error::Value("invalid base64".to_string()));
45 }
46
47 let raw = rawify(bext)?;
48
49 Matter::new(Some(code), Some(&raw), None, None, None)
50}
51
52impl Pather {
53 pub fn new(
54 path: Option<&Value>,
55 bext: Option<&str>,
56 code: Option<&str>,
57 raw: Option<&[u8]>,
58 qb64b: Option<&[u8]>,
59 qb64: Option<&str>,
60 qb2: Option<&[u8]>,
61 ) -> Result<Self> {
62 let code = code.unwrap_or(matter::Codex::StrB64_L0);
63
64 let pather: Pather = if bext.is_none()
65 && raw.is_none()
66 && qb64b.is_none()
67 && qb64.is_none()
68 && qb2.is_none()
69 {
70 if let Some(path) = path {
71 pather_from_bext(&Self::bextify(path)?, code)?
72 } else {
73 return err!(Error::EmptyMaterial("missing bext string".to_string()));
74 }
75 } else if let Some(bext) = bext {
76 pather_from_bext(bext, code)?
77 } else {
78 Matter::new(Some(code), raw, qb64b, qb64, qb2)?
79 };
80
81 validate_code(&pather.code())?;
82
83 Ok(pather)
84 }
85
86 pub fn new_with_path(path: &Value) -> Result<Self> {
87 Self::new(Some(path), None, None, None, None, None, None)
88 }
89
90 pub fn new_with_bext(bext: &str) -> Result<Self> {
91 Self::new(None, Some(bext), None, None, None, None, None)
92 }
93
94 pub fn new_with_raw(raw: &[u8], code: Option<&str>) -> Result<Self> {
95 Self::new(None, None, code, Some(raw), None, None, None)
96 }
97
98 pub fn new_with_qb64b(qb64b: &[u8]) -> Result<Self> {
99 Self::new(None, None, None, None, Some(qb64b), None, None)
100 }
101
102 pub fn new_with_qb64(qb64: &str) -> Result<Self> {
103 Self::new(None, None, None, None, None, Some(qb64), None)
104 }
105
106 pub fn new_with_qb2(qb2: &[u8]) -> Result<Self> {
107 Self::new(None, None, None, None, None, None, Some(qb2))
108 }
109
110 pub fn path(&self) -> Result<Value> {
111 let bext = self.bext()?;
112
113 if !bext.starts_with('-') {
114 return err!(Error::Value("invalid sad pointer".to_string()));
115 }
116
117 let result: Vec<Value> = bext[1..].split('-').map(|p| dat!(p)).collect();
118
119 if result[0].to_string()?.is_empty() {
120 Ok(dat!([]))
121 } else {
122 Ok(dat!(result.as_slice()))
123 }
124 }
125
126 pub fn root(&self, root: &Self) -> Result<Self> {
127 let mut path = root.path()?.to_vec()?;
128 let mut to_append = self.path()?.to_vec()?;
129
130 path.append(&mut to_append);
131 let path = dat!(path.as_slice());
132
133 Pather::new_with_path(&path)
134 }
135
136 pub fn strip(&self, root: &Self) -> Result<Self> {
137 let mut root_path = root.path()?.to_vec()?;
138 let mut path = self.path()?.to_vec()?;
139
140 let hashmap: std::collections::HashMap<String, usize> =
141 path.iter().enumerate().map(|(x, y)| (y.to_string().unwrap(), x)).collect();
142
143 if root_path.len() > path.len() {
144 return Ok(self.clone());
145 }
146
147 root_path.reverse();
148 for p in root_path {
149 path.remove(hashmap[&p.to_string()?]);
150 }
151
152 Pather::new_with_path(&dat!(path.as_slice()))
153 }
154
155 pub fn starts_with(&self, path: &Self) -> Result<bool> {
156 Ok(self.bext()?.starts_with(&path.bext()?))
157 }
158
159 pub fn resolve(&self, sad: &Value) -> Result<Value> {
160 Self::_resolve(sad, &self.path()?)
161 }
162
163 pub fn tail(&self, serder: &Serder) -> Result<String> {
164 let val = self.resolve(&serder.ked())?;
165
166 if val.to_string().is_ok() {
167 let result = val.to_string()?;
168 Saider::new(None, None, None, None, None, None, None, Some(&result), None)?;
170 Ok(result)
171 } else if val.to_map().is_ok() || val.to_vec().is_ok() {
172 val.to_json()
173 } else {
174 return err!(Error::Value("bad tail value".to_string()));
175 }
176 }
177
178 fn bextify(path: &Value) -> Result<String> {
179 lazy_static! {
180 static ref REB64: Regex = Regex::new(REB64_STRING).unwrap();
181 }
182
183 let mut vath = vec![];
184 let path = path.to_vec()?;
185 for e in &path {
186 let p = e.to_string();
187 let p = if let Ok(p) = p { p } else { e.to_i64()?.to_string() };
188
189 if !REB64.is_match(&p) {
190 return err!(Error::Value("invalid base64".to_string()));
191 }
192
193 vath.push(p);
194 }
195
196 Ok("-".to_string() + &vath.join("-"))
197 }
198
199 fn _resolve(val: &Value, ptr: &Value) -> Result<Value> {
200 let mut ptr = ptr.to_vec()?;
201 if ptr.is_empty() {
202 return Ok(val.clone());
203 }
204
205 let idx = ptr.remove(0).to_string()?;
206
207 let cur = if val.to_map().is_ok() {
208 let val = val.to_map()?;
209 let result = idx.parse::<usize>();
210 if result.is_ok() {
211 let i = result?;
212 if i >= val.len() {
213 return err!(Error::Value(format!("invalid map index {i}, larger than size")));
214 }
215 val[i].clone()
216 } else if idx.is_empty() {
217 return Ok(dat!(&val));
218 } else {
219 if !val.contains_key(&idx) {
220 return err!(Error::Value(format!("invalid index {idx} for map")));
221 }
222 val[&idx].clone()
223 }
224 } else if val.to_vec().is_ok() {
225 let val = val.to_vec()?;
226 let i = idx.parse::<usize>()?;
227 if i >= val.len() {
228 return err!(Error::Value(format!("invalid array index {i}, larger than size")));
229 }
230 val[i].clone()
231 } else {
232 return err!(Error::Value("invalid traversal type".to_string()));
233 };
234
235 Self::_resolve(&cur, &dat!(ptr.as_slice()))
236 }
237}
238
239impl Bext for Pather {}
240
241impl Matter for Pather {
242 fn code(&self) -> String {
243 self.code.clone()
244 }
245
246 fn raw(&self) -> Vec<u8> {
247 self.raw.clone()
248 }
249
250 fn size(&self) -> u32 {
251 self.size
252 }
253
254 fn set_code(&mut self, code: &str) {
255 self.code = code.to_string();
256 }
257
258 fn set_raw(&mut self, raw: &[u8]) {
259 self.raw = raw.to_vec();
260 }
261
262 fn set_size(&mut self, size: u32) {
263 self.size = size;
264 }
265}
266
267#[cfg(test)]
268mod test {
269 use super::Pather;
270 use crate::{
271 core::{
272 bexter::Bext,
273 matter::{tables as matter, Matter},
274 saider::Saider,
275 serder::Serder,
276 },
277 data::dat,
278 };
279
280 #[test]
281 fn convenience() {
282 let pather = Pather::new_with_bext("-a").unwrap();
283
284 assert!(Pather::new_with_bext(&pather.bext().unwrap()).is_ok());
285 assert!(Pather::new_with_path(&pather.path().unwrap()).is_ok());
286 assert!(Pather::new_with_qb2(&pather.qb2().unwrap()).is_ok());
287 assert!(Pather::new_with_qb64(&pather.qb64().unwrap()).is_ok());
288 assert!(Pather::new_with_qb64b(&pather.qb64b().unwrap()).is_ok());
289 assert!(Pather::new_with_raw(&pather.raw(), None).is_ok());
290 }
291
292 #[test]
293 fn unhappy() {
294 assert!(Pather::new(
295 None,
296 None,
297 Some(matter::Codex::Blake3_256),
298 Some(b"00000000000000000000000000000000"),
299 None,
300 None,
301 None
302 )
303 .is_err());
304 assert!(Pather::new(None, Some("@!"), None, None, None, None, None).is_err());
305 assert!(Pather::new(None, None, None, None, None, None, None).is_err());
306
307 let pather =
308 Pather::new(None, None, Some(matter::Codex::StrB64_L0), Some(b"00"), None, None, None)
309 .unwrap();
310 assert!(pather.path().is_err());
311
312 let sad = dat!({
313 "a": [1]
314 });
315
316 let pather = Pather::new(None, Some("-1"), None, None, None, None, None).unwrap();
317 assert!(pather.resolve(&sad).is_err());
318
319 let pather = Pather::new(None, Some("-b"), None, None, None, None, None).unwrap();
320 assert!(pather.resolve(&sad).is_err());
321
322 let pather = Pather::new(None, Some("-a-1"), None, None, None, None, None).unwrap();
323 assert!(pather.resolve(&sad).is_err());
324
325 let sad = dat!(2);
327 let pather = Pather::new(None, Some("-0"), None, None, None, None, None).unwrap();
328 assert!(pather.resolve(&sad).is_err());
329 }
330
331 #[test]
332 fn resolve() {
333 let sad = dat!({
334 "a": [2]
335 });
336
337 let pather = Pather::new(None, Some("-a-0"), None, None, None, None, None).unwrap();
338 assert_eq!(pather.resolve(&sad).unwrap(), dat!(2));
339
340 assert_eq!(Pather::_resolve(&sad, &dat!([""])).unwrap(), sad);
341 }
342
343 #[test]
344 fn root() {
345 let pather = Pather::new_with_bext("-a").unwrap();
346 let root = Pather::new_with_bext("-r").unwrap();
347 assert_eq!(pather.root(&root).unwrap().bext().unwrap(), "-r-a");
348 }
349
350 #[test]
351 fn tail() {
352 let _vs = "KERI10JSON000000_";
353 let e1 = dat!({
354 "v": _vs,
355 "d": "",
356 "i": "ABCDEFG",
357 "s": {},
358 "t": "rot",
359 "x": 1
360 });
361 let (_, e1) = Saider::saidify(&e1, None, None, None, None).unwrap();
362 let serder = Serder::new(None, None, None, Some(&e1), None).unwrap();
363
364 let pather = Pather::new_with_bext("-d").unwrap();
365 assert_eq!(pather.tail(&serder).unwrap(), e1["d"].to_string().unwrap());
366
367 let pather = Pather::new_with_bext("-s").unwrap();
368 assert_eq!(pather.tail(&serder).unwrap(), "{}");
369
370 let pather = Pather::new_with_bext("-x").unwrap();
371 assert!(pather.tail(&serder).is_err());
372 }
373
374 #[test]
375 fn python_interop() {
376 let sad = dat!({
377 "a": {
378 "z": "value",
379 "b": {
380 "x": 1,
381 "y": 2,
382 "c": "test"
383 }
384 }
385 });
386
387 let path = dat!([]);
388 let pather = Pather::new(Some(&path), None, None, None, None, None, None).unwrap();
389 assert_eq!(pather.bext().unwrap(), "-");
390 assert_eq!(pather.qb64().unwrap(), "6AABAAA-");
391 assert_eq!(pather.raw(), b">");
392 assert_eq!(pather.resolve(&sad).unwrap(), sad);
393 assert_eq!(pather.path().unwrap(), path);
394
395 let path = dat!(["a", "b", "c"]);
396 let pather = Pather::new(Some(&path), None, None, None, None, None, None).unwrap();
397 assert_eq!(pather.bext().unwrap(), "-a-b-c");
398 assert_eq!(pather.qb64().unwrap(), "5AACAA-a-b-c");
399 assert_eq!(pather.raw(), b"\x0f\x9a\xf9\xbf\x9c");
400 assert_eq!(pather.resolve(&sad).unwrap().to_string().unwrap(), "test");
401 assert_eq!(pather.path().unwrap(), path);
402
403 let path = dat!(["0", "1", "2"]);
404 let pather = Pather::new(Some(&path), None, None, None, None, None, None).unwrap();
405 assert_eq!(pather.bext().unwrap(), "-0-1-2");
406 assert_eq!(pather.qb64().unwrap(), "5AACAA-0-1-2");
407 assert_eq!(pather.raw(), b"\x0f\xb4\xfb_\xb6");
408 assert_eq!(pather.resolve(&sad).unwrap().to_string().unwrap(), "test");
409 assert_eq!(pather.path().unwrap(), path);
410
411 let sad = dat!({
412 "field0": {
413 "z": "value",
414 "field1": {
415 "field2": 1,
416 "field3": 2,
417 "c": "test"
418 }
419 }
420 });
421
422 let path = dat!(["field0"]);
423 let pather = Pather::new(Some(&path), None, None, None, None, None, None).unwrap();
424 assert_eq!(pather.bext().unwrap(), "-field0");
425 assert_eq!(pather.qb64().unwrap(), "4AACA-field0");
426 assert_eq!(pather.raw(), b"\x03\xe7\xe2zWt");
427 assert_eq!(
428 pather.resolve(&sad).unwrap(),
429 dat!({
430 "z": "value",
431 "field1": {
432 "field2": 1,
433 "field3": 2,
434 "c": "test"
435 }
436 })
437 );
438 assert_eq!(pather.path().unwrap(), path);
439
440 let path = dat!(["field0", "field1", "field3"]);
441 let pather = Pather::new(Some(&path), None, None, None, None, None, None).unwrap();
442 assert_eq!(pather.bext().unwrap(), "-field0-field1-field3");
443 assert_eq!(pather.qb64().unwrap(), "6AAGAAA-field0-field1-field3");
444 assert_eq!(pather.raw(), b">~'\xa5wO\x9f\x89\xe9]\xd7\xe7\xe2zWw");
445 assert_eq!(pather.resolve(&sad).unwrap().to_i64().unwrap(), 2);
446 assert_eq!(pather.path().unwrap(), path);
447
448 let path = dat!(["field0", "1", "0"]);
449 let pather = Pather::new(Some(&path), None, None, None, None, None, None).unwrap();
450 assert_eq!(pather.bext().unwrap(), "-field0-1-0");
451 assert_eq!(pather.qb64().unwrap(), "4AADA-field0-1-0");
452 assert_eq!(pather.raw(), b"\x03\xe7\xe2zWt\xfb_\xb4");
453 assert_eq!(pather.resolve(&sad).unwrap().to_i64().unwrap(), 1);
454 assert_eq!(pather.path().unwrap(), path);
455
456 let sad = dat!({
457 "field0": {
458 "z": {
459 "field2": 1,
460 "field3": 2,
461 "c": "test"
462 },
463 "field1": "value"
464 }
465 });
466
467 let text = "-0-z-2";
468 let pather = Pather::new(None, Some(text), None, None, None, None, None).unwrap();
469 assert_eq!(pather.bext().unwrap(), text);
470 assert_eq!(pather.qb64().unwrap(), "5AACAA-0-z-2");
471 assert_eq!(pather.raw(), b"\x0f\xb4\xfb?\xb6");
472 assert_eq!(pather.resolve(&sad).unwrap().to_string().unwrap(), "test");
473 assert_eq!(pather.path().unwrap(), dat!(["0", "z", "2"]));
474
475 let text = "-0-a";
476 let pather = Pather::new(None, Some(text), None, None, None, None, None).unwrap();
477 assert_eq!(pather.bext().unwrap(), text);
478 assert_eq!(pather.qb64().unwrap(), "4AAB-0-a");
479 assert_eq!(pather.raw(), b"\xfbO\x9a");
480 assert!(pather.resolve(&sad).is_err());
481 assert_eq!(pather.path().unwrap(), dat!(["0", "a"]));
482
483 let text = "-0-field1-0";
484 let pather = Pather::new(None, Some(text), None, None, None, None, None).unwrap();
485 assert_eq!(pather.bext().unwrap(), text);
486 assert_eq!(pather.qb64().unwrap(), "4AADA-0-field1-0");
487 assert_eq!(pather.raw(), b"\x03\xed>~'\xa5w_\xb4");
488 assert!(pather.resolve(&sad).is_err());
489 assert_eq!(pather.path().unwrap(), dat!(["0", "field1", "0"]));
490
491 let path = dat!(["Not$Base64", "@moreso", "*again"]);
492 assert!(Pather::new(Some(&path), None, None, None, None, None, None).is_err());
493
494 let text = "-a";
495 let a = Pather::new(None, Some(text), None, None, None, None, None).unwrap();
496 let b = Pather::new(None, Some("-a-b"), None, None, None, None, None).unwrap();
497
498 let pather = Pather::new(None, Some(text), None, None, None, None, None).unwrap();
499 assert!(pather.starts_with(&a).unwrap());
500 assert!(!pather.starts_with(&b).unwrap());
501
502 let pnew = pather.strip(&a).unwrap();
503 assert_eq!(pnew.path().unwrap(), dat!([]));
504
505 let pnew = pather.strip(&b).unwrap();
506 assert_eq!(pnew.path().unwrap(), pather.path().unwrap());
507
508 let pather = Pather::new(None, Some("-a-b-c-d-e-f"), None, None, None, None, None).unwrap();
509 assert!(pather.starts_with(&a).unwrap());
510 assert!(pather.starts_with(&b).unwrap());
511
512 let pnew = pather.strip(&a).unwrap();
513 assert_eq!(pnew.path().unwrap(), dat!(["b", "c", "d", "e", "f"]));
514
515 let pnew = pather.strip(&b).unwrap();
516 assert_eq!(pnew.path().unwrap(), dat!(["c", "d", "e", "f"]));
517 }
518}