Skip to main content

oxibrain_cli/cmd/
redact.rs

1use oxibrain::{Brain, BrainConfig, RedactTarget};
2use std::path::Path;
3
4pub async fn run(
5    dir: &Path,
6    target: &str,
7    space: &str,
8    dry_run: bool,
9    reason: &str,
10) -> anyhow::Result<()> {
11    let brain = Brain::open(BrainConfig::at(dir)).await?;
12    let space_id = brain.ensure_space(space).await?;
13    let parsed = parse_target(target, &space_id)?;
14    if dry_run {
15        let closure = brain.redact_dry_run(&parsed).await?;
16        print_closure("dry-run", &closure);
17        Ok(())
18    } else {
19        let result = brain.redact(&parsed, reason, "cli").await?;
20        print_closure("redacted", &result.closure);
21        println!("beliefs refolded: {}", result.beliefs_refolded);
22        Ok(())
23    }
24}
25
26fn parse_target(target: &str, space_id: &str) -> anyhow::Result<RedactTarget> {
27    let (kind, id) = target
28        .split_once(':')
29        .ok_or_else(|| anyhow::anyhow!("target must be `kind:id`, got `{target}`"))?;
30    let id = id.trim();
31    if id.is_empty() {
32        anyhow::bail!("target id is empty in `{target}`");
33    }
34    match kind {
35        "episode" => Ok(RedactTarget::Episode { id: id.to_string() }),
36        "entity" => Ok(RedactTarget::Entity {
37            space: space_id.to_string(),
38            entity_id: id.to_string(),
39        }),
40        "predicate" => {
41            let (entity_id, predicate) = id
42                .split_once('/')
43                .ok_or_else(|| anyhow::anyhow!("predicate target must be `entity/predicate`"))?;
44            Ok(RedactTarget::PredicateScoped {
45                space: space_id.to_string(),
46                entity_id: entity_id.to_string(),
47                predicate: predicate.to_string(),
48            })
49        }
50        other => anyhow::bail!("unknown target kind `{other}` (expected episode|entity|predicate)"),
51    }
52}
53
54fn print_closure(label: &str, c: &oxibrain::RedactionClosure) {
55    println!("{label}:");
56    println!("  episodes: {}", c.episodes.len());
57    for id in &c.episodes {
58        println!("    episode:{id}");
59    }
60    println!("  assertions: {}", c.assertions.len());
61    println!("  statements: {}", c.statements.len());
62    for id in &c.statements {
63        println!("    statement:{id}");
64    }
65    println!("  mentions: {}", c.mentions.len());
66    println!("  extractions: {}", c.extractions.len());
67    println!("  summaries: {}", c.summaries.len());
68}