1#[cfg(all(feature = "runtime-async-std", feature = "ip"))]
2use async_std::net::IpAddr;
3
4#[cfg(all(feature = "runtime-tokio", feature = "ip"))]
5use std::net::IpAddr;
6
7#[cfg(feature = "glob")]
8use globset::GlobBuilder;
9#[cfg(feature = "ip")]
10use ip_network::IpNetwork;
11use once_cell::sync::Lazy;
12use regex::Regex;
13use rhai::{Dynamic, ImmutableString};
14
15static MAT_B: Lazy<Regex> = Lazy::new(|| Regex::new(r":[^/]*").unwrap());
16static MAT_P: Lazy<Regex> = Lazy::new(|| Regex::new(r"\{[^/]*\}").unwrap());
17
18use std::{borrow::Cow, collections::HashMap};
19
20#[derive(Clone, Copy)]
21pub enum OperatorFunction {
22 Arg0(fn() -> Dynamic),
23 Arg1(fn(ImmutableString) -> Dynamic),
24 Arg2(fn(ImmutableString, ImmutableString) -> Dynamic),
25 Arg3(fn(ImmutableString, ImmutableString, ImmutableString) -> Dynamic),
26 Arg4(
27 fn(
28 ImmutableString,
29 ImmutableString,
30 ImmutableString,
31 ImmutableString,
32 ) -> Dynamic,
33 ),
34 Arg5(
35 fn(
36 ImmutableString,
37 ImmutableString,
38 ImmutableString,
39 ImmutableString,
40 ImmutableString,
41 ) -> Dynamic,
42 ),
43 Arg6(
44 fn(
45 ImmutableString,
46 ImmutableString,
47 ImmutableString,
48 ImmutableString,
49 ImmutableString,
50 ImmutableString,
51 ) -> Dynamic,
52 ),
53}
54
55pub struct FunctionMap {
56 pub(crate) fm: HashMap<String, OperatorFunction>,
57}
58
59impl Default for FunctionMap {
60 fn default() -> FunctionMap {
61 let mut fm: HashMap<String, OperatorFunction> = HashMap::new();
62 fm.insert(
63 "keyMatch".to_owned(),
64 OperatorFunction::Arg2(
65 |s1: ImmutableString, s2: ImmutableString| {
66 key_match(&s1, &s2).into()
67 },
68 ),
69 );
70 fm.insert(
71 "keyGet".to_owned(),
72 OperatorFunction::Arg2(
73 |s1: ImmutableString, s2: ImmutableString| {
74 key_get(&s1, &s2).into()
75 },
76 ),
77 );
78 fm.insert(
79 "keyMatch2".to_owned(),
80 OperatorFunction::Arg2(
81 |s1: ImmutableString, s2: ImmutableString| {
82 key_match2(&s1, &s2).into()
83 },
84 ),
85 );
86 fm.insert(
87 "keyGet2".to_owned(),
88 OperatorFunction::Arg3(
89 |s1: ImmutableString,
90 s2: ImmutableString,
91 s3: ImmutableString| {
92 key_get2(&s1, &s2, &s3).into()
93 },
94 ),
95 );
96 fm.insert(
97 "keyMatch3".to_owned(),
98 OperatorFunction::Arg2(
99 |s1: ImmutableString, s2: ImmutableString| {
100 key_match3(&s1, &s2).into()
101 },
102 ),
103 );
104 fm.insert(
105 "keyGet3".to_owned(),
106 OperatorFunction::Arg3(
107 |s1: ImmutableString,
108 s2: ImmutableString,
109 s3: ImmutableString| {
110 key_get3(&s1, &s2, &s3).into()
111 },
112 ),
113 );
114 fm.insert(
115 "keyMatch4".to_owned(),
116 OperatorFunction::Arg2(
117 |s1: ImmutableString, s2: ImmutableString| {
118 key_match4(&s1, &s2).into()
119 },
120 ),
121 );
122 fm.insert(
123 "keyMatch5".to_owned(),
124 OperatorFunction::Arg2(
125 |s1: ImmutableString, s2: ImmutableString| {
126 key_match5(&s1, &s2).into()
127 },
128 ),
129 );
130 fm.insert(
131 "regexMatch".to_owned(),
132 OperatorFunction::Arg2(
133 |s1: ImmutableString, s2: ImmutableString| {
134 regex_match(&s1, &s2).into()
135 },
136 ),
137 );
138
139 #[cfg(feature = "glob")]
140 fm.insert(
141 "globMatch".to_owned(),
142 OperatorFunction::Arg2(
143 |s1: ImmutableString, s2: ImmutableString| {
144 glob_match(&s1, &s2).into()
145 },
146 ),
147 );
148
149 #[cfg(feature = "ip")]
150 fm.insert(
151 "ipMatch".to_owned(),
152 OperatorFunction::Arg2(
153 |s1: ImmutableString, s2: ImmutableString| {
154 ip_match(&s1, &s2).into()
155 },
156 ),
157 );
158
159 FunctionMap { fm }
160 }
161}
162
163impl FunctionMap {
164 #[inline]
165 pub fn add_function(&mut self, fname: &str, f: OperatorFunction) {
166 self.fm.insert(fname.to_owned(), f);
167 }
168
169 #[inline]
170 pub fn get_functions(
171 &self,
172 ) -> impl Iterator<Item = (&String, &OperatorFunction)> {
173 self.fm.iter()
174 }
175}
176
177pub fn key_match(key1: &str, key2: &str) -> bool {
180 if let Some(i) = key2.find('*') {
181 if key1.len() > i {
182 return key1[..i] == key2[..i];
183 }
184 key1[..] == key2[..i]
185 } else {
186 key1 == key2
187 }
188}
189
190pub fn key_get(key1: &str, key2: &str) -> String {
194 if let Some(i) = key2.find('*') {
195 if key1.len() > i && key1[..i] == key2[..i] {
196 return key1[i..].to_string();
197 }
198 }
199 "".to_string()
200}
201
202pub fn key_match2(key1: &str, key2: &str) -> bool {
205 let mut key2: Cow<str> = if key2.contains("/*") {
206 key2.replace("/*", "/.*").into()
207 } else {
208 key2.into()
209 };
210
211 key2 = MAT_B.replace_all(&key2, "[^/]+").to_string().into();
212
213 regex_match(key1, &format!("^{}$", key2))
214}
215
216pub fn key_get2(key1: &str, key2: &str, path_var: &str) -> String {
220 let key2: Cow<str> = if key2.contains("/*") {
221 key2.replace("/*", "/.*").into()
222 } else {
223 key2.into()
224 };
225
226 let re = Regex::new(r":[^/]+").unwrap();
227 let keys: Vec<_> = re.find_iter(&key2).collect();
228 let key2 = re.replace_all(&key2, "([^/]+)").to_string();
229 let key2 = format!("^{}$", key2);
230
231 if let Ok(re2) = Regex::new(&key2) {
232 if let Some(caps) = re2.captures(key1) {
233 for (i, key) in keys.iter().enumerate() {
234 if path_var == &key.as_str()[1..] {
235 return caps
236 .get(i + 1)
237 .map_or("".to_string(), |m| m.as_str().to_string());
238 }
239 }
240 }
241 }
242 "".to_string()
243}
244
245pub fn key_match3(key1: &str, key2: &str) -> bool {
248 let mut key2: Cow<str> = if key2.contains("/*") {
249 key2.replace("/*", "/.*").into()
250 } else {
251 key2.into()
252 };
253
254 key2 = MAT_P.replace_all(&key2, "[^/]+").to_string().into();
255
256 regex_match(key1, &format!("^{}$", key2))
257}
258
259pub fn key_get3(key1: &str, key2: &str, path_var: &str) -> String {
263 let key2: Cow<str> = if key2.contains("/*") {
264 key2.replace("/*", "/.*").into()
265 } else {
266 key2.into()
267 };
268
269 let re = Regex::new(r"\{[^/]+?\}").unwrap();
270 let keys: Vec<_> = re.find_iter(&key2).collect();
271 let key2 = re.replace_all(&key2, "([^/]+?)").to_string();
272 let key2 = Regex::new(r"\{")
273 .unwrap()
274 .replace_all(&key2, "\\{")
275 .to_string();
276 let key2 = format!("^{}$", key2);
277
278 let re2 = Regex::new(&key2).unwrap();
279 if let Some(caps) = re2.captures(key1) {
280 for (i, key) in keys.iter().enumerate() {
281 if path_var == &key.as_str()[1..key.as_str().len() - 1] {
282 return caps
283 .get(i + 1)
284 .map_or("".to_string(), |m| m.as_str().to_string());
285 }
286 }
287 }
288 "".to_string()
289}
290
291pub fn key_match4(key1: &str, key2: &str) -> bool {
298 let mut key2 = key2.replace("/*", "/.*");
299 let mut tokens = Vec::new();
300
301 let re = Regex::new(r"\{[^/]+?\}").unwrap();
302 key2 = re
303 .replace_all(&key2, |caps: ®ex::Captures| {
304 tokens.push(caps[0][1..caps[0].len() - 1].to_string());
305 "([^/]+)".to_string()
306 })
307 .to_string();
308
309 let re = match Regex::new(&format!("^{}$", key2)) {
310 Ok(re) => re,
311 Err(_) => return false,
312 };
313 if let Some(caps) = re.captures(key1) {
314 let matches: Vec<_> =
315 caps.iter().skip(1).map(|m| m.unwrap().as_str()).collect();
316 if tokens.len() != matches.len() {
317 panic!(
318 "KeyMatch4: number of tokens is not equal to number of values"
319 );
320 }
321
322 let mut values = HashMap::new();
323 for (token, value) in tokens.iter().zip(matches.iter()) {
324 if let Some(existing_value) = values.get(token) {
325 if *existing_value != value {
326 return false;
327 }
328 } else {
329 values.insert(token, value);
330 }
331 }
332 true
333 } else {
334 false
335 }
336}
337
338pub fn key_match5(key1: &str, key2: &str) -> bool {
344 let key1 = if let Some(i) = key1.find('?') {
345 &key1[..i]
346 } else {
347 key1
348 };
349
350 let key2 = key2.replace("/*", "/.*");
351 let key2 = Regex::new(r"(\{[^/]+?\})")
352 .unwrap()
353 .replace_all(&key2, "[^/]+");
354
355 regex_match(key1, &format!("^{}$", key2))
356}
357
358pub fn regex_match(key1: &str, key2: &str) -> bool {
360 Regex::new(key2).unwrap().is_match(key1)
361}
362
363#[cfg(feature = "ip")]
366pub fn ip_match(key1: &str, key2: &str) -> bool {
367 let key2_split = key2.splitn(2, '/').collect::<Vec<&str>>();
368 let ip_addr2 = key2_split[0];
369
370 if let (Ok(ip_addr1), Ok(ip_addr2)) =
371 (key1.parse::<IpAddr>(), ip_addr2.parse::<IpAddr>())
372 {
373 if key2_split.len() == 2 {
374 match key2_split[1].parse::<u8>() {
375 Ok(ip_netmask) => {
376 match IpNetwork::new_truncate(ip_addr2, ip_netmask) {
377 Ok(ip_network) => ip_network.contains(ip_addr1),
378 Err(err) => panic!("invalid ip network {}", err),
379 }
380 }
381 _ => panic!("invalid netmask {}", key2_split[1]),
382 }
383 } else {
384 if let (IpAddr::V4(ip_addr1_new), IpAddr::V6(ip_addr2_new)) =
385 (ip_addr1, ip_addr2)
386 {
387 if let Some(ip_addr2_new) = ip_addr2_new.to_ipv4() {
388 return ip_addr2_new == ip_addr1_new;
389 }
390 }
391
392 ip_addr1 == ip_addr2
393 }
394 } else {
395 panic!("invalid argument {} {}", key1, key2)
396 }
397}
398
399#[cfg(feature = "glob")]
401pub fn glob_match(key1: &str, key2: &str) -> bool {
402 GlobBuilder::new(key2)
403 .literal_separator(true)
404 .build()
405 .unwrap()
406 .compile_matcher()
407 .is_match(key1)
408}
409
410#[cfg(test)]
411mod tests {
412 use super::*;
413
414 #[test]
415 fn test_key_match() {
416 assert!(key_match("/foo/bar", "/foo/*"));
417 assert!(!key_match("/bar/foo", "/foo/*"));
418 assert!(key_match("/bar", "/ba*"));
419 }
420
421 #[test]
422 fn test_key_get() {
423 assert_eq!(key_get("/foo", "/foo"), "");
424 assert_eq!(key_get("/foo", "/foo*"), "");
425 assert_eq!(key_get("/foo", "/foo/*"), "");
426 assert_eq!(key_get("/foo/bar", "/foo"), "");
427 assert_eq!(key_get("/foo/bar", "/foo*"), "/bar");
428 assert_eq!(key_get("/foo/bar", "/foo/*"), "bar");
429 assert_eq!(key_get("/foobar", "/foo"), "");
430 assert_eq!(key_get("/foobar", "/foo*"), "bar");
431 assert_eq!(key_get("/foobar", "/foo/*"), "");
432 }
433
434 #[test]
435 fn test_key_match2() {
436 assert!(key_match2("/foo/bar", "/foo/*"));
437 assert!(key_match2("/foo/bar/baz", "/foo/*"));
438 assert!(key_match2("/foo/baz", "/foo/:bar"));
439 assert!(key_match2("/foo/baz", "/:foo/:bar"));
440 assert!(key_match2("/foo/baz/foo", "/foo/:bar/foo"));
441 assert!(!key_match2("/baz", "/foo"));
442
443 assert!(key_match2("/foo/bar", "/foo/:"));
445 assert!(!key_match2("/foo/bar/baz", "/foo/:"));
446 assert!(key_match2("/foo/bar/baz", "/foo/:/baz"));
447 assert!(!key_match2("/foo/bar", "/foo/:/baz"));
448 }
449
450 #[test]
451 fn test_key_get2() {
452 assert_eq!(key_get2("/foo", "/foo", "id"), "");
453 assert_eq!(key_get2("/foo", "/foo*", "id"), "");
454 assert_eq!(key_get2("/foo", "/foo/*", "id"), "");
455 assert_eq!(key_get2("/foo/bar", "/foo", "id"), "");
456 assert_eq!(key_get2("/foo/bar", "/foo*", "id"), "");
457 assert_eq!(key_get2("/foo/bar", "/foo/*", "id"), "");
458 assert_eq!(key_get2("/foobar", "/foo", "id"), "");
459 assert_eq!(key_get2("/foobar", "/foo*", "id"), "");
460 assert_eq!(key_get2("/foobar", "/foo/*", "id"), "");
461
462 assert_eq!(key_get2("/", "/:resource", "resource"), "");
463 assert_eq!(
464 key_get2("/resource1", "/:resource", "resource"),
465 "resource1"
466 );
467 assert_eq!(key_get2("/myid", "/:id/using/:resId", "id"), "");
468 assert_eq!(
469 key_get2("/myid/using/myresid", "/:id/using/:resId", "id"),
470 "myid"
471 );
472 assert_eq!(
473 key_get2("/myid/using/myresid", "/:id/using/:resId", "resId"),
474 "myresid"
475 );
476
477 assert_eq!(key_get2("/proxy/myid", "/proxy/:id/*", "id"), "");
478 assert_eq!(key_get2("/proxy/myid/", "/proxy/:id/*", "id"), "myid");
479 assert_eq!(key_get2("/proxy/myid/res", "/proxy/:id/*", "id"), "myid");
480 assert_eq!(
481 key_get2("/proxy/myid/res/res2", "/proxy/:id/*", "id"),
482 "myid"
483 );
484 assert_eq!(
485 key_get2("/proxy/myid/res/res2/res3", "/proxy/:id/*", "id"),
486 "myid"
487 );
488 assert_eq!(
489 key_get2("/proxy/myid/res/res2/res3", "/proxy/:id/res/*", "id"),
490 "myid"
491 );
492 assert_eq!(key_get2("/proxy/", "/proxy/:id/*", "id"), "");
493
494 assert_eq!(key_get2("/alice", "/:id", "id"), "alice");
495 assert_eq!(key_get2("/alice/all", "/:id/all", "id"), "alice");
496 assert_eq!(key_get2("/alice", "/:id/all", "id"), "");
497 assert_eq!(key_get2("/alice/all", "/:id", "id"), "");
498
499 assert_eq!(key_get2("/alice/all", "/:/all", ""), "");
500 }
501
502 #[test]
503 fn test_regex_match() {
504 assert!(regex_match("foobar", "^foo*"));
505 assert!(!regex_match("barfoo", "^foo*"));
506 }
507
508 #[test]
509 fn test_key_match3() {
510 assert!(key_match3("/foo/bar", "/foo/*"));
511 assert!(key_match3("/foo/bar/baz", "/foo/*"));
512 assert!(key_match3("/foo/baz", "/foo/{bar}"));
513 assert!(key_match3("/foo/baz/foo", "/foo/{bar}/foo"));
514 assert!(!key_match3("/baz", "/foo"));
515
516 assert!(key_match3("/foo/bar", "/foo/{}"));
518 assert!(key_match3("/foo/{}", "/foo/{}"));
519 assert!(!key_match3("/foo/bar/baz", "/foo/{}"));
520 assert!(!key_match3("/foo/bar", "/foo/{}/baz"));
521 assert!(key_match3("/foo/bar/baz", "/foo/{}/baz"));
522 }
523
524 #[test]
525 fn test_key_get3() {
526 assert_eq!(key_get3("/foo", "/foo", "id"), "");
527 assert_eq!(key_get3("/foo", "/foo*", "id"), "");
528 assert_eq!(key_get3("/foo", "/foo/*", "id"), "");
529 assert_eq!(key_get3("/foo/bar", "/foo", "id"), "");
530 assert_eq!(key_get3("/foo/bar", "/foo*", "id"), "");
531 assert_eq!(key_get3("/foo/bar", "/foo/*", "id"), "");
532 assert_eq!(key_get3("/foobar", "/foo", "id"), "");
533 assert_eq!(key_get3("/foobar", "/foo*", "id"), "");
534 assert_eq!(key_get3("/foobar", "/foo/*", "id"), "");
535
536 assert_eq!(key_get3("/", "/{resource}", "resource"), "");
537 assert_eq!(
538 key_get3("/resource1", "/{resource}", "resource"),
539 "resource1"
540 );
541 assert_eq!(key_get3("/myid", "/{id}/using/{resId}", "id"), "");
542 assert_eq!(
543 key_get3("/myid/using/myresid", "/{id}/using/{resId}", "id"),
544 "myid"
545 );
546 assert_eq!(
547 key_get3("/myid/using/myresid", "/{id}/using/{resId}", "resId"),
548 "myresid"
549 );
550
551 assert_eq!(key_get3("/proxy/myid", "/proxy/{id}/*", "id"), "");
552 assert_eq!(key_get3("/proxy/myid/", "/proxy/{id}/*", "id"), "myid");
553 assert_eq!(key_get3("/proxy/myid/res", "/proxy/{id}/*", "id"), "myid");
554 assert_eq!(
555 key_get3("/proxy/myid/res/res2", "/proxy/{id}/*", "id"),
556 "myid"
557 );
558 assert_eq!(
559 key_get3("/proxy/myid/res/res2/res3", "/proxy/{id}/*", "id"),
560 "myid"
561 );
562 assert_eq!(
563 key_get3("/proxy/myid/res/res2/res3", "/proxy/{id}/res/*", "id"),
564 "myid"
565 );
566 assert_eq!(key_get3("/proxy/", "/proxy/{id}/*", "id"), "");
567
568 assert_eq!(
569 key_get3(
570 "/api/group1_group_name/project1_admin/info",
571 "/api/{proj}_admin/info",
572 "proj"
573 ),
574 ""
575 );
576 assert_eq!(
577 key_get3("/{id/using/myresid", "/{id/using/{resId}", "resId"),
578 "myresid"
579 );
580 assert_eq!(
581 key_get3(
582 "/{id/using/myresid/status}",
583 "/{id/using/{resId}/status}",
584 "resId"
585 ),
586 "myresid"
587 );
588
589 assert_eq!(
590 key_get3("/proxy/myid/res/res2/res3", "/proxy/{id}/*/{res}", "res"),
591 "res3"
592 );
593 assert_eq!(
594 key_get3(
595 "/api/project1_admin/info",
596 "/api/{proj}_admin/info",
597 "proj"
598 ),
599 "project1"
600 );
601 assert_eq!(
602 key_get3(
603 "/api/group1_group_name/project1_admin/info",
604 "/api/{g}_{gn}/{proj}_admin/info",
605 "g"
606 ),
607 "group1"
608 );
609 assert_eq!(
610 key_get3(
611 "/api/group1_group_name/project1_admin/info",
612 "/api/{g}_{gn}/{proj}_admin/info",
613 "gn"
614 ),
615 "group_name"
616 );
617 assert_eq!(
618 key_get3(
619 "/api/group1_group_name/project1_admin/info",
620 "/api/{g}_{gn}/{proj}_admin/info",
621 "proj"
622 ),
623 "project1"
624 );
625 }
626
627 #[test]
628 fn test_key_match4() {
629 assert!(key_match4(
630 "/parent/123/child/123",
631 "/parent/{id}/child/{id}"
632 ));
633 assert!(!key_match4(
634 "/parent/123/child/456",
635 "/parent/{id}/child/{id}"
636 ));
637 assert!(key_match4(
638 "/parent/123/child/123",
639 "/parent/{id}/child/{another_id}"
640 ));
641 assert!(key_match4(
642 "/parent/123/child/456",
643 "/parent/{id}/child/{another_id}"
644 ));
645 assert!(key_match4(
646 "/parent/123/child/123",
647 "/parent/{id}/child/{id}"
648 ));
649 assert!(!key_match4(
650 "/parent/123/child/456",
651 "/parent/{id}/child/{id}"
652 ));
653 assert!(key_match4(
654 "/parent/123/child/123",
655 "/parent/{id}/child/{another_id}"
656 ));
657 assert!(key_match4(
658 "/parent/123/child/123/book/123",
659 "/parent/{id}/child/{id}/book/{id}"
660 ));
661 assert!(!key_match4(
662 "/parent/123/child/123/book/456",
663 "/parent/{id}/child/{id}/book/{id}"
664 ));
665 assert!(!key_match4(
666 "/parent/123/child/456/book/123",
667 "/parent/{id}/child/{id}/book/{id}"
668 ));
669 assert!(!key_match4(
670 "/parent/123/child/456/book/",
671 "/parent/{id}/child/{id}/book/{id}"
672 ));
673 assert!(!key_match4(
674 "/parent/123/child/456",
675 "/parent/{id}/child/{id}/book/{id}"
676 ));
677 assert!(!key_match4(
678 "/parent/123/child/123",
679 "/parent/{i/d}/child/{i/d}"
680 ));
681 }
682
683 #[test]
684 fn test_key_match5() {
685 assert!(key_match5("/foo/bar?status=1&type=2", "/foo/bar"));
686 assert!(key_match5("/parent/child1", "/parent/*"));
687 assert!(key_match5("/parent/child1?status=1", "/parent/*"));
688 assert!(key_match5("/parent/child1?status=1", "/parent/child1"));
689 }
690
691 #[cfg(feature = "ip")]
692 #[test]
693 fn test_ip_match() {
694 assert!(ip_match("::1", "::0:1"));
695 assert!(ip_match("192.168.1.1", "192.168.1.1"));
696 assert!(ip_match("127.0.0.1", "::ffff:127.0.0.1"));
697 assert!(ip_match("192.168.2.123", "192.168.2.0/24"));
698 assert!(!ip_match("::1", "127.0.0.2"));
699 assert!(!ip_match("192.168.2.189", "192.168.1.134/26"));
700 }
701
702 #[cfg(feature = "ip")]
703 #[test]
704 #[should_panic]
705 fn test_ip_match_panic_1() {
706 assert!(ip_match("I am alice", "127.0.0.1"));
707 }
708
709 #[cfg(feature = "ip")]
710 #[test]
711 #[should_panic]
712 fn test_ip_match_panic_2() {
713 assert!(ip_match("127.0.0.1", "I am alice"));
714 }
715
716 #[cfg(feature = "glob")]
717 #[test]
718 fn test_glob_match() {
719 assert!(glob_match("/abc/123", "/abc/*"));
720 assert!(!glob_match("/abc/123/456", "/abc/*"));
721 assert!(glob_match("/abc/123/456", "/abc/**"));
722 }
723}