1use crate::types::{Manifest, PolicyContext};
2
3pub trait PolicyRule {
4 fn evaluate(&self, manifest: &Manifest, context: &PolicyContext) -> Result<(), String>;
5}
6
7pub struct AllowedRegionRule;
8
9impl PolicyRule for AllowedRegionRule {
10 fn evaluate(&self, manifest: &Manifest, context: &PolicyContext) -> Result<(), String> {
11 let region = match context.requested_region.as_ref() {
12 Some(region) => region,
13 None => return Ok(()),
14 };
15
16 if let Some(permitted) = manifest.permitted_regions.as_ref() {
17 if !permitted
18 .iter()
19 .any(|item| item.eq_ignore_ascii_case(region))
20 {
21 return Err(format!("Region {} is not permitted", region));
22 }
23 }
24
25 if let Some(forbidden) = manifest.forbidden_regions.as_ref() {
26 if forbidden
27 .iter()
28 .any(|item| item.eq_ignore_ascii_case(region))
29 {
30 return Err(format!("Region {} is explicitly forbidden", region));
31 }
32 }
33
34 Ok(())
35 }
36}
37
38pub struct MaxTransactionValueRule;
39
40impl PolicyRule for MaxTransactionValueRule {
41 fn evaluate(&self, manifest: &Manifest, context: &PolicyContext) -> Result<(), String> {
42 let value = match context.transaction_value {
43 Some(value) => value,
44 None => return Ok(()),
45 };
46
47 if let Some(max) = manifest.max_transaction_value {
48 if value > max {
49 return Err(format!("Transaction value {} exceeds max {}", value, max));
50 }
51 }
52
53 Ok(())
54 }
55}
56
57pub fn apply_policy(
58 manifest: &Manifest,
59 context: &PolicyContext,
60 rules: &[Box<dyn PolicyRule>],
61) -> (bool, Vec<String>) {
62 let mut errors = Vec::new();
63 for rule in rules {
64 if let Err(err) = rule.evaluate(manifest, context) {
65 errors.push(err);
66 }
67 }
68
69 (errors.is_empty(), errors)
70}