p47h_engine/
validation.rs1use crate::types::PolicyDiagnostic;
2use core_policy::Policy;
3use wasm_bindgen::prelude::*;
4
5#[wasm_bindgen]
10pub fn validate_policy(policy_toml: &str) -> Result<(), JsValue> {
11 let _: Policy = toml::from_str(policy_toml)
12 .map_err(|e| JsValue::from_str(&format!("Invalid policy TOML: {}", e)))?;
13 Ok(())
14}
15
16#[wasm_bindgen]
18pub fn validate_policy_detailed(policy_toml: &str) -> Result<JsValue, JsValue> {
19 let policy: Policy = match toml::from_str(policy_toml) {
21 Ok(p) => p,
22 Err(e) => {
23 let msg = e.to_string();
26 let (line, col) = parse_toml_error_position(&msg);
27
28 let diagnostic = PolicyDiagnostic {
29 valid: false,
30 message: Some(msg),
31 line,
32 column: col,
33 };
34 return Ok(serde_wasm_bindgen::to_value(&diagnostic)?);
35 }
36 };
37
38 if policy.rules().is_empty() {
40 let diagnostic = PolicyDiagnostic {
41 valid: false,
42 message: Some("Policy is empty: must contain at least one rule".to_string()),
43 line: Some(0), column: None,
45 };
46 return Ok(serde_wasm_bindgen::to_value(&diagnostic)?);
47 }
48
49 for (i, rule) in policy.rules().iter().enumerate() {
51 if rule.peer_id.is_empty() {
52 let diagnostic = PolicyDiagnostic {
53 valid: false,
54 message: Some(format!("Rule #{} has empty peer_id", i + 1)),
55 line: None, column: None,
57 };
58 return Ok(serde_wasm_bindgen::to_value(&diagnostic)?);
59 }
60 }
61
62 let diagnostic = PolicyDiagnostic {
64 valid: true,
65 message: None,
66 line: None,
67 column: None,
68 };
69 Ok(serde_wasm_bindgen::to_value(&diagnostic)?)
70}
71
72fn parse_toml_error_position(msg: &str) -> (Option<u32>, Option<u32>) {
74 if let Some(line_idx) = msg.find("line ") {
78 if let Some(col_idx) = msg.find("column ") {
79 let line_str = &msg[line_idx + 5..];
80 let line_end = line_str.find(',').unwrap_or(line_str.len());
81 let line = line_str[..line_end].trim().parse::<u32>().ok();
82
83 let col_str = &msg[col_idx + 7..];
84 let col_end = col_str
85 .find(|c: char| !c.is_numeric())
86 .unwrap_or(col_str.len());
87 let col = col_str[..col_end].trim().parse::<u32>().ok();
88
89 return (line, col);
90 }
91 }
92 (None, None)
93}