greentic_operator/gmap/
parse.rs1use std::path::Path;
2
3#[derive(Clone, Debug, Eq, PartialEq)]
4pub enum Policy {
5 Public,
6 Forbidden,
7}
8
9#[derive(Clone, Debug, Eq, PartialEq)]
10pub struct GmapPath {
11 pub pack: Option<String>,
12 pub flow: Option<String>,
13 pub node: Option<String>,
14}
15
16#[derive(Clone, Debug, Eq, PartialEq)]
17pub struct GmapRule {
18 pub path: GmapPath,
19 pub policy: Policy,
20 pub line: usize,
21}
22
23pub fn parse_file(path: &Path) -> anyhow::Result<Vec<GmapRule>> {
24 if !path.exists() {
25 return Ok(Vec::new());
26 }
27 let contents = std::fs::read_to_string(path)?;
28 parse_str(&contents)
29}
30
31pub fn parse_str(contents: &str) -> anyhow::Result<Vec<GmapRule>> {
32 let mut rules = Vec::new();
33 for (idx, line) in contents.lines().enumerate() {
34 let line = line.trim();
35 if line.is_empty() || line.starts_with('#') {
36 continue;
37 }
38 let rule = parse_rule_line(line, idx + 1)?;
39 rules.push(rule);
40 }
41 Ok(rules)
42}
43
44pub fn parse_rule_line(line: &str, line_number: usize) -> anyhow::Result<GmapRule> {
45 let mut parts = line.splitn(2, '=');
46 let raw_path = parts
47 .next()
48 .map(|part| part.trim())
49 .filter(|part| !part.is_empty())
50 .ok_or_else(|| anyhow::anyhow!("Invalid rule line {}: missing path", line_number))?;
51 let raw_policy = parts
52 .next()
53 .map(|part| part.trim())
54 .filter(|part| !part.is_empty())
55 .ok_or_else(|| anyhow::anyhow!("Invalid rule line {}: missing policy", line_number))?;
56
57 let path = parse_path(raw_path, line_number)?;
58 let policy = parse_policy(raw_policy, line_number)?;
59 Ok(GmapRule {
60 path,
61 policy,
62 line: line_number,
63 })
64}
65
66pub fn parse_path(raw: &str, line_number: usize) -> anyhow::Result<GmapPath> {
67 if raw == "_" {
68 return Ok(GmapPath {
69 pack: None,
70 flow: None,
71 node: None,
72 });
73 }
74 let segments: Vec<&str> = raw.split('/').filter(|seg| !seg.is_empty()).collect();
75 if segments.is_empty() {
76 return Err(anyhow::anyhow!(
77 "Invalid path on line {}: empty path",
78 line_number
79 ));
80 }
81 if segments.len() > 3 {
82 return Err(anyhow::anyhow!(
83 "Invalid path on line {}: too many segments",
84 line_number
85 ));
86 }
87 let pack = Some(segments[0].to_string());
88 let flow = segments.get(1).map(|seg| seg.to_string());
89 let node = segments.get(2).map(|seg| seg.to_string());
90 Ok(GmapPath { pack, flow, node })
91}
92
93pub fn parse_policy(raw: &str, line_number: usize) -> anyhow::Result<Policy> {
94 match raw {
95 "public" => Ok(Policy::Public),
96 "forbidden" => Ok(Policy::Forbidden),
97 other => Err(anyhow::anyhow!(
98 "Invalid policy on line {}: {}",
99 line_number,
100 other
101 )),
102 }
103}
104
105impl std::fmt::Display for GmapPath {
106 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
107 match (&self.pack, &self.flow, &self.node) {
108 (None, None, None) => write!(formatter, "_"),
109 (Some(pack), None, None) => write!(formatter, "{pack}"),
110 (Some(pack), Some(flow), None) => write!(formatter, "{pack}/{flow}"),
111 (Some(pack), Some(flow), Some(node)) => write!(formatter, "{pack}/{flow}/{node}"),
112 _ => write!(formatter, "_"),
113 }
114 }
115}