byteor_redaction/
parse.rs

1//! Stable parsing helpers for redaction policy configuration.
2
3use crate::{format, string::ToString, String};
4
5use crate::types::{Action, EnforcementMode, HashAlgo, Value};
6
7/// Parse a stable enforcement mode string.
8pub fn parse_mode(s: &str) -> Result<EnforcementMode, String> {
9    match s {
10        "block" => Ok(EnforcementMode::Block),
11        "transform" => Ok(EnforcementMode::Transform),
12        _ => Err(format!("invalid enforcement mode: {s}")),
13    }
14}
15
16/// Parse a stable action string.
17///
18/// Supported:
19/// - `drop`
20/// - `hash`
21/// - `hash=sha256`
22/// - `mask=<visible_prefix>:<visible_suffix>:<mask_char>`
23/// - `truncate=<max_bytes>`
24/// - `replace_with=<string>`
25pub fn parse_action(s: &str) -> Result<Action, String> {
26    if s == "drop" {
27        return Ok(Action::Drop);
28    }
29    if matches!(s, "hash" | "hash=sha256") {
30        return Ok(Action::Hash(HashAlgo::Sha256));
31    }
32    if let Some(spec) = s.strip_prefix("mask=") {
33        let mut parts = spec.splitn(3, ':');
34        let visible_prefix = parts
35            .next()
36            .ok_or_else(|| format!("invalid action: {s}"))?
37            .parse::<usize>()
38            .map_err(|_| format!("invalid mask visible_prefix: {s}"))?;
39        let visible_suffix = parts
40            .next()
41            .ok_or_else(|| format!("invalid action: {s}"))?
42            .parse::<usize>()
43            .map_err(|_| format!("invalid mask visible_suffix: {s}"))?;
44        let mask_char_s = parts.next().ok_or_else(|| format!("invalid action: {s}"))?;
45        let mut chars = mask_char_s.chars();
46        let mask_char = chars
47            .next()
48            .ok_or_else(|| format!("invalid mask char: {s}"))?;
49        if chars.next().is_some() {
50            return Err(format!("invalid mask char: {s}"));
51        }
52        return Ok(Action::Mask {
53            visible_prefix,
54            visible_suffix,
55            mask_char,
56        });
57    }
58    if let Some(spec) = s.strip_prefix("truncate=") {
59        let max_bytes = spec
60            .parse::<usize>()
61            .map_err(|_| format!("invalid truncate max_bytes: {s}"))?;
62        return Ok(Action::Truncate { max_bytes });
63    }
64    let Some(v) = s.strip_prefix("replace_with=") else {
65        return Err(format!("invalid action: {s}"));
66    };
67    Ok(Action::ReplaceWith(Value::String(v.to_string())))
68}