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 = self.redact_artifact_with_input_kind_and_source(
85 text,
86 InputKind::Text,
87 Some(source_path),
88 )?;
89 Ok(artifact.result)
90 }
91
92 pub fn redact_artifact(&self, text: &str) -> Result<RedactionArtifact, RedactorError> {
93 self.redact_artifact_with_input_kind(text, InputKind::Text)
94 }
95
96 pub fn redact_artifact_with_input_kind(
97 &self,
98 text: &str,
99 input_kind: InputKind,
100 ) -> Result<RedactionArtifact, RedactorError> {
101 self.redact_artifact_with_input_kind_and_source(text, input_kind, None)
102 }
103
104 pub fn redact_artifact_with_input_kind_and_source(
105 &self,
106 text: &str,
107 input_kind: InputKind,
108 source_path: Option<&str>,
109 ) -> Result<RedactionArtifact, RedactorError> {
110 self.redact_artifact_with_input_kind_source_and_prior_session(
111 text,
112 input_kind,
113 source_path,
114 None,
115 None,
116 )
117 }
118
119 pub fn redact_artifact_with_input_kind_source_and_prior_session(
120 &self,
121 text: &str,
122 input_kind: InputKind,
123 source_path: Option<&str>,
124 prior_session: Option<&RedactionSession>,
125 external_id: Option<&str>,
126 ) -> Result<RedactionArtifact, RedactorError> {
127 let outcome = detect_internal(self, text, input_kind, source_path);
128 let findings = outcome.findings;
129 let mut processor =
130 crate::replace::ReplacementProcessor::with_prior_session(prior_session, external_id)?;
131 let redacted_text = processor.redact_fragment(text, &findings);
132 let session = processor.build_session(text, &redacted_text, &self.policy);
133 let applied_replacements = processor.into_applied_replacements();
134 let stats = stats_for(self.llm.is_some(), &findings, outcome.stats);
135
136 Ok(RedactionArtifact {
137 result: RedactionResult {
138 redacted_text: redacted_text.clone(),
139 findings,
140 applied_replacements,
141 stats,
142 },
143 session,
144 })
145 }
146
147 pub fn redact_with_session(&self, text: &str) -> Result<RedactionSession, RedactorError> {
148 self.redact_with_session_input_kind(text, InputKind::Text)
149 }
150
151 pub fn redact_with_session_input_kind(
152 &self,
153 text: &str,
154 input_kind: InputKind,
155 ) -> Result<RedactionSession, RedactorError> {
156 Ok(self
157 .redact_artifact_with_input_kind(text, input_kind)?
158 .session)
159 }
160
161 pub fn detect(&self, text: &str) -> Result<Vec<Finding>, RedactorError> {
162 self.detect_with_input_kind(text, InputKind::Text)
163 }
164
165 pub fn detect_with_input_kind(
166 &self,
167 text: &str,
168 input_kind: InputKind,
169 ) -> Result<Vec<Finding>, RedactorError> {
170 Ok(detect_internal(self, text, input_kind, None).findings)
171 }
172
173 pub fn detect_with_source_path(
174 &self,
175 text: &str,
176 source_path: &str,
177 ) -> Result<Vec<Finding>, RedactorError> {
178 Ok(detect_internal(self, text, InputKind::Text, Some(source_path)).findings)
179 }
180
181 pub fn restore_text(&self, text: &str, session: &RedactionSession) -> RestoreResult {
182 restore_text_with_session(text, session)
183 }
184
185 pub fn restore_patch(&self, patch: &str, session: &RedactionSession) -> RestoreResult {
186 restore_patch_with_session(patch, session)
187 }
188
189 pub fn redact_artifact_with_prior_session(
190 &self,
191 text: &str,
192 input_kind: InputKind,
193 prior_session: Option<&RedactionSession>,
194 external_id: Option<&str>,
195 ) -> Result<RedactionArtifact, RedactorError> {
196 self.redact_artifact_with_input_kind_source_and_prior_session(
197 text,
198 input_kind,
199 None,
200 prior_session,
201 external_id,
202 )
203 }
204}
205
206impl SessionRedactor {
207 pub fn new() -> Self {
208 Self::default()
209 }
210
211 pub fn with_prior_session(
212 prior_session: Option<&RedactionSession>,
213 external_id: Option<&str>,
214 ) -> Result<Self, RedactorError> {
215 Ok(Self {
216 processor: crate::replace::ReplacementProcessor::with_prior_session(
217 prior_session,
218 external_id,
219 )?,
220 })
221 }
222
223 pub fn redact_fragment(
224 &mut self,
225 redactor: &Redactor,
226 text: &str,
227 ) -> Result<String, RedactorError> {
228 self.redact_text_fragment(redactor, text)
229 }
230
231 pub fn build_session(&self, original_text: &str, redacted_text: &str) -> RedactionSession {
232 self.build_redaction_session(
233 original_text,
234 redacted_text,
235 &crate::RedactionPolicy::default(),
236 )
237 }
238
239 pub fn max_token_len(&self) -> usize {
240 self.max_replacement_token_len()
241 }
242}