use crate::request::{RedactionRules, validate_redaction_body_path};
use super::Rule;
pub(super) fn apply_redaction_directive(
rules: &mut RedactionRules,
pair: pest::iterators::Pair<Rule>,
) -> Result<(), pest::error::Error<Rule>> {
let rule = pair.as_rule();
let span = pair.as_span();
let raw_value = pair
.into_inner()
.next()
.expect("redaction directives should include a value")
.as_str()
.trim()
.to_string();
let result = match rule {
Rule::redact_header_directive => rules.add_header_name(&raw_value),
Rule::redact_capture_directive => rules.add_capture_name(&raw_value),
Rule::redact_body_directive => {
if let Err(err) = validate_redaction_body_path(&raw_value) {
return Err(custom_error(err.to_string(), span));
}
rules.add_body_path(&raw_value)
}
_ => unreachable!("unexpected redaction directive: {:?}", rule),
};
result.map_err(|message| custom_error(message, span))
}
pub(super) fn reject_misplaced_redaction_directive(
raw: &str,
span: pest::Span<'_>,
) -> Result<(), pest::error::Error<Rule>> {
if looks_like_redaction_directive(raw) {
return Err(custom_error(
"redaction directives must appear before the first ---".to_string(),
span,
));
}
Ok(())
}
fn looks_like_redaction_directive(raw: &str) -> bool {
let trimmed = raw.trim_start();
if let Some(value) = redaction_directive_value(trimmed, "redact_header") {
return looks_like_redaction_name(value);
}
if let Some(value) = redaction_directive_value(trimmed, "redact_capture") {
return looks_like_redaction_name(value);
}
redaction_directive_value(trimmed, "redact_body")
.map(|value| validate_redaction_body_path(value.trim()).is_ok())
.unwrap_or(false)
}
fn redaction_directive_value<'a>(raw: &'a str, prefix: &str) -> Option<&'a str> {
raw.strip_prefix(prefix)
.and_then(|remainder| remainder.trim_start().strip_prefix('='))
}
fn looks_like_redaction_name(value: &str) -> bool {
let trimmed = value.trim();
!trimmed.is_empty() && !trimmed.chars().any(|ch| ch.is_whitespace())
}
fn custom_error(message: String, span: pest::Span<'_>) -> pest::error::Error<Rule> {
pest::error::Error::new_from_span(
pest::error::ErrorVariant::CustomError { message },
span,
)
}