1#![cfg_attr(docsrs, feature(doc_cfg))]
166
167use std::collections::HashMap;
168use std::fmt::Debug;
169use std::result::Result as StdResult;
170use std::sync::Arc;
171
172use ipnet::IpNet;
173use serde_json::{Map, Value as JsonValue, json};
174
175#[cfg(feature = "validation")]
177#[cfg_attr(docsrs, doc(cfg(feature = "validation")))]
178pub use jsonschema::ValidationError;
179
180pub use error::Error;
182pub use matcher::{
183 BoolMatcher, DefaultMatcher, IpMatcher, Matcher, NumberMatcher, Operator, RegexMatcher,
184 StringMatcher,
185};
186pub use types::{AsyncCheckFn, BoxFuture, CheckFn, MaybeSend, MaybeSync, ToOperator};
187pub use value::Value;
188
189use crate::types::{AsyncEvalFn, AsyncFetcherFn, DynError, EvalFn, FetcherFn};
190
191pub(crate) type Result<T> = StdResult<T, error::Error>;
192
193pub enum Rule<Ctx: ?Sized + 'static> {
201 Any(Vec<Self>),
202 All(Vec<Self>),
203 Not(Box<Self>),
204 Leaf(Condition<Ctx>),
205}
206
207impl<Ctx: ?Sized> Debug for Rule<Ctx> {
208 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
209 match self {
210 Rule::Any(rules) => f.debug_tuple("Any").field(rules).finish(),
211 Rule::All(rules) => f.debug_tuple("All").field(rules).finish(),
212 Rule::Not(rule) => f.debug_tuple("Not").field(rule).finish(),
213 Rule::Leaf(_) => f.debug_tuple("Leaf").finish(),
214 }
215 }
216}
217
218impl<Ctx: ?Sized> Clone for Rule<Ctx> {
219 fn clone(&self) -> Self {
220 match self {
221 Rule::Any(rules) => Rule::Any(rules.clone()),
222 Rule::All(rules) => Rule::All(rules.clone()),
223 Rule::Not(rule) => Rule::Not(rule.clone()),
224 Rule::Leaf(condition) => Rule::Leaf(condition.clone()),
225 }
226 }
227}
228
229#[doc(hidden)]
233pub struct Condition<Ctx: ?Sized>(AnyEvalFn<Ctx>);
234
235impl<Ctx: ?Sized> Clone for Condition<Ctx> {
236 fn clone(&self) -> Self {
237 Condition(self.0.clone())
238 }
239}
240
241impl<Ctx: ?Sized> Rule<Ctx> {
242 #[inline(always)]
243 fn any(mut rules: Vec<Rule<Ctx>>) -> Self {
244 if rules.len() == 1 {
245 return rules.pop().unwrap();
246 }
247 Rule::Any(rules)
248 }
249
250 #[inline(always)]
251 fn all(mut rules: Vec<Rule<Ctx>>) -> Self {
252 if rules.len() == 1 {
253 return rules.pop().unwrap();
254 }
255 Rule::All(rules)
256 }
257
258 #[inline(always)]
259 fn not(mut rules: Vec<Rule<Ctx>>) -> Self {
260 if rules.len() == 1 {
261 return Rule::Not(Box::new(rules.pop().unwrap()));
262 }
263 Rule::Not(Box::new(Rule::All(rules)))
264 }
265
266 #[inline(always)]
267 fn leaf(eval_fn: AnyEvalFn<Ctx>) -> Self {
268 Rule::Leaf(Condition(eval_fn))
269 }
270
271 #[inline(always)]
272 fn into_vec(self) -> Vec<Self> {
273 match self {
274 Rule::Any(rules) | Rule::All(rules) => rules,
275 Rule::Not(_) | Rule::Leaf(_) => vec![self],
276 }
277 }
278}
279
280#[derive(Debug)]
282pub(crate) struct FetcherKey {
283 name: String,
284 args: Vec<String>,
285}
286
287enum AnyFetcherFn<Ctx: ?Sized> {
288 Sync(Arc<FetcherFn<Ctx>>),
289 Async(Arc<AsyncFetcherFn<Ctx>>),
290}
291
292impl<Ctx: ?Sized> Clone for AnyFetcherFn<Ctx> {
293 fn clone(&self) -> Self {
294 match self {
295 AnyFetcherFn::Sync(func) => AnyFetcherFn::Sync(func.clone()),
296 AnyFetcherFn::Async(func) => AnyFetcherFn::Async(func.clone()),
297 }
298 }
299}
300
301enum AnyEvalFn<Ctx: ?Sized> {
302 Sync(EvalFn<Ctx>),
303 Async(AsyncEvalFn<Ctx>),
304}
305
306impl<Ctx: ?Sized> Clone for AnyEvalFn<Ctx> {
307 fn clone(&self) -> Self {
308 match self {
309 AnyEvalFn::Sync(func) => AnyEvalFn::Sync(func.clone()),
310 AnyEvalFn::Async(func) => AnyEvalFn::Async(func.clone()),
311 }
312 }
313}
314
315pub struct Fetcher<Ctx: ?Sized> {
322 matcher: Arc<dyn Matcher<Ctx>>,
323 func: AnyFetcherFn<Ctx>,
324 raw_args: bool,
325}
326
327impl<Ctx: ?Sized> Clone for Fetcher<Ctx> {
328 fn clone(&self) -> Self {
329 Fetcher {
330 matcher: self.matcher.clone(),
331 func: self.func.clone(),
332 raw_args: self.raw_args,
333 }
334 }
335}
336
337impl<Ctx: ?Sized> Fetcher<Ctx> {
338 pub fn with_matcher<M>(&mut self, matcher: M) -> &mut Self
340 where
341 M: Matcher<Ctx> + 'static,
342 {
343 self.matcher = Arc::new(matcher);
344 self
345 }
346
347 pub fn with_raw_args(&mut self, raw_args: bool) -> &mut Self {
349 self.raw_args = raw_args;
350 self
351 }
352}
353
354pub struct Engine<Ctx: MaybeSync + ?Sized + 'static> {
393 fetchers: HashMap<String, Fetcher<Ctx>>,
394 operators: HashMap<String, Arc<dyn ToOperator<Ctx>>>,
395}
396
397impl<Ctx: MaybeSync + ?Sized> Default for Engine<Ctx> {
398 fn default() -> Self {
399 Self::new()
400 }
401}
402
403impl<Ctx: MaybeSync + ?Sized> Clone for Engine<Ctx> {
404 fn clone(&self) -> Self {
405 Engine {
406 fetchers: self.fetchers.clone(),
407 operators: self.operators.clone(),
408 }
409 }
410}
411
412impl<Ctx: MaybeSync + ?Sized> Engine<Ctx> {
413 pub fn new() -> Self {
415 Engine {
416 fetchers: HashMap::new(),
417 operators: HashMap::new(),
418 }
419 }
420
421 pub fn register_fetcher<F>(&mut self, name: &str, func: F) -> &mut Fetcher<Ctx>
431 where
432 F: for<'a> Fn(&'a Ctx, &[String]) -> StdResult<Value<'a>, DynError>
433 + MaybeSend
434 + MaybeSync
435 + 'static,
436 {
437 let fetcher = Fetcher {
438 matcher: Arc::new(DefaultMatcher),
439 func: AnyFetcherFn::Sync(Arc::new(func)),
440 raw_args: false,
441 };
442 self.fetchers
443 .entry(name.to_string())
444 .insert_entry(fetcher)
445 .into_mut()
446 }
447
448 pub fn register_async_fetcher<F>(&mut self, name: &str, func: F) -> &mut Fetcher<Ctx>
452 where
453 F: for<'a> Fn(&'a Ctx, Arc<[String]>) -> BoxFuture<'a, StdResult<Value<'a>, DynError>>
454 + MaybeSend
455 + MaybeSync
456 + 'static,
457 {
458 let fetcher = Fetcher {
459 matcher: Arc::new(DefaultMatcher),
460 func: AnyFetcherFn::Async(Arc::new(func)),
461 raw_args: false,
462 };
463 self.fetchers
464 .entry(name.to_string())
465 .insert_entry(fetcher)
466 .into_mut()
467 }
468
469 pub fn register_operator<O>(&mut self, name: &str, op: O)
471 where
472 O: ToOperator<Ctx> + 'static,
473 {
474 self.operators.insert(name.to_string(), Arc::new(op));
475 }
476
477 pub fn compile_rule(&self, value: &JsonValue) -> Result<Rule<Ctx>> {
479 match value {
480 JsonValue::Object(map) => {
481 let mut subrules = Vec::with_capacity(map.len());
482 for (key, value) in map {
483 match key.as_str() {
484 "any" => subrules.push(Rule::any(self.compile_rule(value)?.into_vec())),
485 "all" => subrules.push(Rule::all(self.compile_rule(value)?.into_vec())),
486 "not" => subrules.push(Rule::not(self.compile_rule(value)?.into_vec())),
487 _ => {
488 let FetcherKey { name, args } = Self::parse_fetcher_key(key)?;
489 let fetcher = (self.fetchers.get(&name)).ok_or_else(|| {
490 Error::fetcher(&name, "fetcher is not registered")
491 })?;
492 let args = Self::parse_fetcher_args(args.clone(), fetcher.raw_args);
493
494 let mut operator = fetcher.matcher.compile(value);
495 if let Err(Error::UnknownOperator(ref op)) = operator
497 && let Some(op_builder) = self.operators.get(op)
498 {
499 operator = op_builder
500 .to_operator(&value[op])
501 .map_err(|err| Error::operator(op, err));
502 }
503 let operator = operator.map_err(|err| Error::matcher(&name, err))?;
504 let fetcher_fn = fetcher.func.clone();
505 let eval_fn =
506 Self::compile_condition(fetcher_fn, args.into(), operator);
507
508 subrules.push(Rule::leaf(eval_fn));
509 }
510 }
511 }
512 Ok(Rule::all(subrules))
513 }
514 JsonValue::Array(seq) => (seq.iter())
515 .try_fold(Vec::with_capacity(seq.len()), |mut subrules, v| {
516 subrules.push(self.compile_rule(v)?);
517 Result::Ok(subrules)
518 })
519 .map(Rule::all),
520 _ => Err(Error::json("rule must be a JSON object or array")),
521 }
522 }
523
524 #[cfg(feature = "validation")]
526 #[cfg_attr(docsrs, doc(cfg(feature = "validation")))]
527 #[allow(clippy::result_large_err)]
528 pub fn validate_rule<'a>(&self, value: &'a JsonValue) -> StdResult<(), ValidationError<'a>> {
529 let schema = self.json_schema();
531 let validator = jsonschema::options()
532 .with_pattern_options(jsonschema::PatternOptions::regex())
533 .build(&schema)?;
534 validator.validate(value)
535 }
536
537 pub fn json_schema(&self) -> JsonValue {
539 let mut pattern_props = Map::new();
540
541 let custom_ops: Vec<(&str, JsonValue)> = (self.operators.iter())
543 .map(|(k, v)| (k.as_str(), v.json_schema()))
544 .collect();
545
546 for (name, fetcher) in &self.fetchers {
548 let pattern = format!(r"^{}(:?\(([^)]*)\))?$", regex::escape(name));
549 let schema = fetcher.matcher.json_schema(&custom_ops);
550 pattern_props.insert(pattern, schema);
551 }
552
553 json!({
554 "$schema": "http://json-schema.org/draft-07/schema",
555 "$ref": "#/definitions/rule_object",
556 "definitions": {
557 "rule_object": {
558 "type": "object",
559 "properties": {
560 "any": { "$ref": "#/definitions/rule" },
561 "all": { "$ref": "#/definitions/rule" },
562 "not": { "$ref": "#/definitions/rule" }
563 },
564 "patternProperties": pattern_props,
565 "additionalProperties": false,
566 },
567 "rule_array": {
568 "type": "array",
569 "minItems": 1,
570 "items": { "$ref": "#/definitions/rule_object" },
571 },
572 "rule": {
573 "if": { "type": "array" },
574 "then": { "$ref": "#/definitions/rule_array" },
575 "else": { "$ref": "#/definitions/rule_object" }
576 },
577 }
578 })
579 }
580
581 fn compile_condition(
582 fetcher_fn: AnyFetcherFn<Ctx>,
583 fetcher_args: Arc<[String]>,
584 operator: Operator<Ctx>,
585 ) -> AnyEvalFn<Ctx> {
586 match (fetcher_fn, operator) {
587 (AnyFetcherFn::Sync(fetcher_fn), Operator::Equal(right)) => {
589 AnyEvalFn::Sync(Arc::new(move |ctx| {
590 Ok(fetcher_fn(ctx, &fetcher_args)? == right)
591 }))
592 }
593 (AnyFetcherFn::Async(fetcher_fn), Operator::Equal(right)) => {
594 let right = Arc::new(right);
595 AnyEvalFn::Async(Arc::new(move |ctx| {
596 let right = right.clone();
597 let value = fetcher_fn(ctx, fetcher_args.clone());
598 Box::pin(async move { Ok(value.await? == *right) })
599 }))
600 }
601
602 (AnyFetcherFn::Sync(fetcher_fn), Operator::LessThan(right)) => {
604 AnyEvalFn::Sync(Arc::new(move |ctx| {
605 Ok(fetcher_fn(ctx, &fetcher_args)? < right)
606 }))
607 }
608 (AnyFetcherFn::Async(fetcher_fn), Operator::LessThan(right)) => {
609 let right = Arc::new(right);
610 AnyEvalFn::Async(Arc::new(move |ctx| {
611 let right = right.clone();
612 let value = fetcher_fn(ctx, fetcher_args.clone());
613 Box::pin(async move { Ok(value.await? < *right) })
614 }))
615 }
616
617 (AnyFetcherFn::Sync(fetcher_fn), Operator::LessThanOrEqual(right)) => {
619 AnyEvalFn::Sync(Arc::new(move |ctx| {
620 Ok(fetcher_fn(ctx, &fetcher_args)? <= right)
621 }))
622 }
623 (AnyFetcherFn::Async(fetcher_fn), Operator::LessThanOrEqual(right)) => {
624 let right = Arc::new(right);
625 AnyEvalFn::Async(Arc::new(move |ctx| {
626 let right = right.clone();
627 let value = fetcher_fn(ctx, fetcher_args.clone());
628 Box::pin(async move { Ok(value.await? <= *right) })
629 }))
630 }
631
632 (AnyFetcherFn::Sync(fetcher_fn), Operator::GreaterThan(right)) => {
634 AnyEvalFn::Sync(Arc::new(move |ctx| {
635 Ok(fetcher_fn(ctx, &fetcher_args)? > right)
636 }))
637 }
638 (AnyFetcherFn::Async(fetcher_fn), Operator::GreaterThan(right)) => {
639 let right = Arc::new(right);
640 AnyEvalFn::Async(Arc::new(move |ctx| {
641 let right = right.clone();
642 let value = fetcher_fn(ctx, fetcher_args.clone());
643 Box::pin(async move { Ok(value.await? > *right) })
644 }))
645 }
646
647 (AnyFetcherFn::Sync(fetcher_fn), Operator::GreaterThanOrEqual(right)) => {
649 AnyEvalFn::Sync(Arc::new(move |ctx| {
650 Ok(fetcher_fn(ctx, &fetcher_args)? >= right)
651 }))
652 }
653 (AnyFetcherFn::Async(fetcher_fn), Operator::GreaterThanOrEqual(right)) => {
654 let right = Arc::new(right);
655 AnyEvalFn::Async(Arc::new(move |ctx| {
656 let right = right.clone();
657 let value = fetcher_fn(ctx, fetcher_args.clone());
658 Box::pin(async move { Ok(value.await? >= *right) })
659 }))
660 }
661
662 (AnyFetcherFn::Sync(fetcher_fn), Operator::InSet(set)) => {
664 AnyEvalFn::Sync(Arc::new(move |ctx| {
665 fetcher_fn(ctx, &fetcher_args).map(|val| set.contains(&val))
666 }))
667 }
668 (AnyFetcherFn::Async(fetcher_fn), Operator::InSet(set)) => {
669 let set = Arc::new(set);
670 AnyEvalFn::Async(Arc::new(move |ctx| {
671 let set = set.clone();
672 let value = fetcher_fn(ctx, fetcher_args.clone());
673 Box::pin(async move { value.await.map(|val| set.contains(&val)) })
674 }))
675 }
676
677 (AnyFetcherFn::Sync(fetcher_fn), Operator::Regex(regex)) => {
679 AnyEvalFn::Sync(Arc::new(move |ctx| {
680 fetcher_fn(ctx, &fetcher_args)
681 .map(|val| val.as_str().map(|s| regex.is_match(s)).unwrap_or(false))
682 }))
683 }
684 (AnyFetcherFn::Async(fetcher_fn), Operator::Regex(regex)) => {
685 let regex = Arc::new(regex);
686 AnyEvalFn::Async(Arc::new(move |ctx| {
687 let regex = regex.clone();
688 let value = fetcher_fn(ctx, fetcher_args.clone());
689 Box::pin(async move {
690 (value.await)
691 .map(|val| val.as_str().map(|s| regex.is_match(s)).unwrap_or(false))
692 })
693 }))
694 }
695
696 (AnyFetcherFn::Sync(fetcher_fn), Operator::RegexSet(regex_set)) => {
698 AnyEvalFn::Sync(Arc::new(move |ctx| {
699 fetcher_fn(ctx, &fetcher_args)
700 .map(|val| val.as_str().map(|s| regex_set.is_match(s)).unwrap_or(false))
701 }))
702 }
703 (AnyFetcherFn::Async(fetcher_fn), Operator::RegexSet(regex_set)) => {
704 let regex_set = Arc::new(regex_set);
705 AnyEvalFn::Async(Arc::new(move |ctx| {
706 let regex_set = regex_set.clone();
707 let value = fetcher_fn(ctx, fetcher_args.clone());
708 Box::pin(async move {
709 (value.await)
710 .map(|val| val.as_str().map(|s| regex_set.is_match(s)).unwrap_or(false))
711 })
712 }))
713 }
714
715 (AnyFetcherFn::Sync(fetcher_fn), Operator::IpSet(set)) => {
717 AnyEvalFn::Sync(Arc::new(move |ctx| {
718 Ok((fetcher_fn(ctx, &fetcher_args)?.to_ip())
719 .map(|ip| set.longest_match(&IpNet::from(ip)).is_some())
720 .unwrap_or(false))
721 }))
722 }
723 (AnyFetcherFn::Async(fetcher_fn), Operator::IpSet(set)) => {
724 let set = Arc::new(set);
725 AnyEvalFn::Async(Arc::new(move |ctx| {
726 let set = set.clone();
727 let value = fetcher_fn(ctx, fetcher_args.clone());
728 Box::pin(async move {
729 Ok((value.await?.to_ip())
730 .map(|ip| set.longest_match(&IpNet::from(ip)).is_some())
731 .unwrap_or(false))
732 })
733 }))
734 }
735
736 (AnyFetcherFn::Sync(fetcher_fn), Operator::Custom(op_fn)) => {
738 AnyEvalFn::Sync(Arc::new(move |ctx| {
739 let value = fetcher_fn(ctx, &fetcher_args)?;
740 op_fn(ctx, value)
741 }))
742 }
743 (AnyFetcherFn::Async(fetcher_fn), Operator::Custom(op_fn)) => {
744 let op_fn: Arc<CheckFn<Ctx>> = op_fn.into();
745 AnyEvalFn::Async(Arc::new(move |ctx| {
746 let op_fn = op_fn.clone();
747 let value = fetcher_fn(ctx, fetcher_args.clone());
748 Box::pin(async move { op_fn(ctx, value.await?) })
749 }))
750 }
751
752 (AnyFetcherFn::Sync(fetcher_fn), Operator::CustomAsync(op_fn)) => {
754 let op_fn: Arc<AsyncCheckFn<Ctx>> = op_fn.into();
755 AnyEvalFn::Async(Arc::new(move |ctx| {
756 let op_fn = op_fn.clone();
757 let value = fetcher_fn(ctx, &fetcher_args);
758 Box::pin(async move { op_fn(ctx, value?).await })
759 }))
760 }
761 (AnyFetcherFn::Async(fetcher_fn), Operator::CustomAsync(op_fn)) => {
762 let op_fn: Arc<AsyncCheckFn<Ctx>> = op_fn.into();
763 AnyEvalFn::Async(Arc::new(move |ctx| {
764 let op_fn = op_fn.clone();
765 let value = fetcher_fn(ctx, fetcher_args.clone());
766 Box::pin(async move { op_fn(ctx, value.await?).await })
767 }))
768 }
769 }
770 }
771
772 fn parse_fetcher_key(key: &str) -> Result<FetcherKey> {
774 if let Some((name, args_str)) = key.split_once('(') {
775 if !args_str.ends_with(')') {
776 return Err(Error::fetcher(name, "missing closing parenthesis"));
777 }
778 let args_str = &args_str[..args_str.len() - 1];
779 let args = if args_str.is_empty() {
780 vec![]
781 } else {
782 vec![args_str.to_string()]
783 };
784 Ok(FetcherKey {
785 name: name.to_string(),
786 args,
787 })
788 } else {
789 Ok(FetcherKey {
790 name: key.to_string(),
791 args: Vec::new(),
792 })
793 }
794 }
795
796 fn parse_fetcher_args(mut args: Vec<String>, raw: bool) -> Vec<String> {
798 if raw || args.is_empty() {
799 args
800 } else {
801 let arg = args.pop().unwrap_or_default();
802 arg.split(',').map(|s| s.trim().to_string()).collect()
803 }
804 }
805}
806
807impl<Ctx: ?Sized> Rule<Ctx> {
808 pub fn evaluate(&self, context: &Ctx) -> StdResult<bool, DynError> {
814 match self {
815 Rule::Leaf(Condition(AnyEvalFn::Sync(eval_fn))) => eval_fn(context),
816 Rule::Leaf(Condition(AnyEvalFn::Async(_))) => {
817 Err("async operations are not supported in sync context".into())
818 }
819 Rule::Any(subrules) => {
820 for rule in subrules {
821 if rule.evaluate(context)? {
822 return Ok(true);
823 }
824 }
825 Ok(false)
826 }
827 Rule::All(subrules) => {
828 for rule in subrules {
829 if !rule.evaluate(context)? {
830 return Ok(false);
831 }
832 }
833 Ok(true)
834 }
835 Rule::Not(subrule) => Ok(!subrule.evaluate(context)?),
836 }
837 }
838
839 pub async fn evaluate_async(&self, context: &Ctx) -> StdResult<bool, DynError> {
845 match self {
846 Rule::Leaf(Condition(AnyEvalFn::Sync(eval_fn))) => eval_fn(context),
847 Rule::Leaf(Condition(AnyEvalFn::Async(eval_fn))) => eval_fn(context).await,
848 Rule::Any(subrules) => {
849 for rule in subrules {
850 if Box::pin(rule.evaluate_async(context)).await? {
851 return Ok(true);
852 }
853 }
854 Ok(false)
855 }
856 Rule::All(subrules) => {
857 for rule in subrules {
858 if !Box::pin(rule.evaluate_async(context)).await? {
859 return Ok(false);
860 }
861 }
862 Ok(true)
863 }
864 Rule::Not(subrule) => Ok(!Box::pin(subrule.evaluate_async(context)).await?),
865 }
866 }
867}
868
869pub(crate) trait JsonValueExt {
870 fn type_name(&self) -> &'static str;
871}
872
873impl JsonValueExt for JsonValue {
874 fn type_name(&self) -> &'static str {
875 match self {
876 JsonValue::String(_) => "string",
877 JsonValue::Number(_) => "number",
878 JsonValue::Bool(_) => "boolean",
879 JsonValue::Array(_) => "array",
880 JsonValue::Object(_) => "object",
881 JsonValue::Null => "null",
882 }
883 }
884}
885
886mod error;
887mod matcher;
888mod types;
889mod value;
890
891#[cfg(test)]
892mod tests {
893 #[cfg(feature = "send")]
894 static_assertions::assert_impl_all!(super::Engine<()>: Send, Sync);
895 #[cfg(feature = "send")]
896 static_assertions::assert_impl_all!(super::Rule<()>: Send, Sync);
897}