cloudiful_redactor/redactor/
mod.rs1use crate::{
2 Finding, InputKind, LlmConfig, RedactionArtifact, RedactionPolicy, RedactionResult,
3 RedactionRules, RedactionSession, RedactorError, RestoreResult, restore_patch_with_session,
4 restore_text_with_session,
5};
6
7mod detection;
8mod session;
9mod stats;
10
11use detection::detect_internal;
12use session::SessionRedactorExt;
13use stats::stats_for;
14
15#[derive(Debug, Clone, Default)]
16pub struct RedactorBuilder {
17 llm: Option<LlmConfig>,
18 policy: RedactionPolicy,
19}
20
21impl RedactorBuilder {
22 pub fn new() -> Self {
23 Self::default()
24 }
25
26 pub fn with_llm(mut self, config: LlmConfig) -> Self {
27 self.llm = Some(config);
28 self
29 }
30
31 pub fn with_person_detection(mut self, enabled: bool) -> Self {
32 self.policy.rules.person = enabled;
33 self
34 }
35
36 pub fn with_redaction_rules(mut self, rules: RedactionRules) -> Self {
37 self.policy.rules = rules;
38 self
39 }
40
41 pub fn with_redaction_policy(mut self, policy: RedactionPolicy) -> Self {
42 self.policy = policy;
43 self
44 }
45
46 pub fn build(self) -> Redactor {
47 Redactor {
48 llm: self.llm,
49 policy: self.policy,
50 }
51 }
52}
53
54#[derive(Debug, Clone)]
55pub struct Redactor {
56 pub(super) llm: Option<LlmConfig>,
57 pub(super) policy: RedactionPolicy,
58}
59
60#[derive(Debug, Default)]
61pub struct SessionRedactor {
62 pub(super) processor: crate::replace::ReplacementProcessor,
63}
64
65impl Redactor {
66 pub fn redact(&self, text: &str) -> Result<RedactionResult, RedactorError> {
67 self.redact_with_input_kind(text, InputKind::Text)
68 }
69
70 pub fn redact_with_input_kind(
71 &self,
72 text: &str,
73 input_kind: InputKind,
74 ) -> Result<RedactionResult, RedactorError> {
75 let artifact = self.redact_artifact_with_input_kind(text, input_kind)?;
76 Ok(artifact.result)
77 }
78
79 pub fn redact_with_source_path(
80 &self,
81 text: &str,
82 source_path: &str,
83 ) -> Result<RedactionResult, RedactorError> {
84 let artifact =
85 self.redact_artifact_with_input_kind_and_source(text, InputKind::Text, Some(source_path))?;
86 Ok(artifact.result)
87 }
88
89 pub fn redact_artifact(&self, text: &str) -> Result<RedactionArtifact, RedactorError> {
90 self.redact_artifact_with_input_kind(text, InputKind::Text)
91 }
92
93 pub fn redact_artifact_with_input_kind(
94 &self,
95 text: &str,
96 input_kind: InputKind,
97 ) -> Result<RedactionArtifact, RedactorError> {
98 self.redact_artifact_with_input_kind_and_source(text, input_kind, None)
99 }
100
101 pub fn redact_artifact_with_input_kind_and_source(
102 &self,
103 text: &str,
104 input_kind: InputKind,
105 source_path: Option<&str>,
106 ) -> Result<RedactionArtifact, RedactorError> {
107 let outcome = detect_internal(self, text, input_kind, source_path);
108 let findings = outcome.findings;
109 let output = crate::replace::apply_replacements(text, &findings, &self.policy);
110 let stats = stats_for(self.llm.is_some(), &findings, outcome.stats);
111
112 Ok(RedactionArtifact {
113 result: RedactionResult {
114 redacted_text: output.redacted_text,
115 findings,
116 applied_replacements: output.applied_replacements,
117 stats,
118 },
119 session: output.session,
120 })
121 }
122
123 pub fn redact_with_session(&self, text: &str) -> Result<RedactionSession, RedactorError> {
124 self.redact_with_session_input_kind(text, InputKind::Text)
125 }
126
127 pub fn redact_with_session_input_kind(
128 &self,
129 text: &str,
130 input_kind: InputKind,
131 ) -> Result<RedactionSession, RedactorError> {
132 Ok(self
133 .redact_artifact_with_input_kind(text, input_kind)?
134 .session)
135 }
136
137 pub fn detect(&self, text: &str) -> Result<Vec<Finding>, RedactorError> {
138 self.detect_with_input_kind(text, InputKind::Text)
139 }
140
141 pub fn detect_with_input_kind(
142 &self,
143 text: &str,
144 input_kind: InputKind,
145 ) -> Result<Vec<Finding>, RedactorError> {
146 Ok(detect_internal(self, text, input_kind, None).findings)
147 }
148
149 pub fn detect_with_source_path(
150 &self,
151 text: &str,
152 source_path: &str,
153 ) -> Result<Vec<Finding>, RedactorError> {
154 Ok(detect_internal(self, text, InputKind::Text, Some(source_path))
155 .findings)
156 }
157
158 pub fn restore_text(&self, text: &str, session: &RedactionSession) -> RestoreResult {
159 restore_text_with_session(text, session)
160 }
161
162 pub fn restore_patch(&self, patch: &str, session: &RedactionSession) -> RestoreResult {
163 restore_patch_with_session(patch, session)
164 }
165}
166
167impl SessionRedactor {
168 pub fn new() -> Self {
169 Self::default()
170 }
171
172 pub fn redact_fragment(
173 &mut self,
174 redactor: &Redactor,
175 text: &str,
176 ) -> Result<String, RedactorError> {
177 self.redact_text_fragment(redactor, text)
178 }
179
180 pub fn build_session(&self, original_text: &str, redacted_text: &str) -> RedactionSession {
181 self.build_redaction_session(
182 original_text,
183 redacted_text,
184 &crate::RedactionPolicy::default(),
185 )
186 }
187
188 pub fn max_token_len(&self) -> usize {
189 self.max_replacement_token_len()
190 }
191}