qubit_redact/policy/
redaction_session.rs1use std::cell::RefCell;
11
12use super::{
13 InputOutputLimit,
14 RedactionPolicy,
15};
16
17mod budget {
18 use super::InputOutputLimit;
19
20 #[must_use]
26 #[derive(Debug)]
27 pub(crate) struct DiagnosticBudget {
28 remaining_input_bytes: usize,
29 remaining_output_bytes: usize,
30 input_exhausted: bool,
31 output_exhausted: bool,
32 }
33
34 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
36 pub(crate) enum OutputCharge {
37 Complete,
39 Fallback,
42 Exhausted,
45 }
46
47 impl DiagnosticBudget {
48 #[must_use = "retain the runtime budget for accounting"]
50 #[inline]
51 pub(crate) const fn new(limit: InputOutputLimit) -> Self {
52 Self {
53 remaining_input_bytes: limit.max_input_bytes(),
54 remaining_output_bytes: limit.max_output_bytes(),
55 input_exhausted: false,
56 output_exhausted: false,
57 }
58 }
59
60 #[inline]
62 pub(crate) fn consume_input(&mut self, bytes: usize) -> bool {
63 if self.input_exhausted || bytes > self.remaining_input_bytes {
64 self.input_exhausted = true;
65 self.remaining_input_bytes = 0;
66 return false;
67 }
68 self.remaining_input_bytes -= bytes;
69 if self.remaining_input_bytes == 0 {
70 self.input_exhausted = true;
71 }
72 true
73 }
74
75 pub(crate) fn charge_output_or_fallback(
78 &mut self,
79 bytes: usize,
80 fallback_bytes: usize,
81 ) -> OutputCharge {
82 if !self.output_exhausted && bytes <= self.remaining_output_bytes {
83 self.remaining_output_bytes -= bytes;
84 self.output_exhausted = self.remaining_output_bytes == 0;
85 return OutputCharge::Complete;
86 }
87 if !self.output_exhausted
88 && fallback_bytes <= self.remaining_output_bytes
89 {
90 self.remaining_output_bytes = 0;
91 self.output_exhausted = true;
92 return OutputCharge::Fallback;
93 }
94 self.remaining_output_bytes = 0;
95 self.output_exhausted = true;
96 OutputCharge::Exhausted
97 }
98
99 #[must_use]
101 #[inline]
102 pub(crate) const fn remaining_input_bytes(&self) -> usize {
103 self.remaining_input_bytes
104 }
105
106 #[must_use]
108 #[inline]
109 pub(crate) const fn remaining_output_bytes(&self) -> usize {
110 self.remaining_output_bytes
111 }
112
113 #[must_use]
115 #[inline]
116 pub(crate) const fn is_exhausted(&self) -> bool {
117 self.input_exhausted || self.output_exhausted
118 }
119 }
120}
121
122pub(crate) use budget::{
123 DiagnosticBudget,
124 OutputCharge,
125};
126
127mod session_kind {
128 #[must_use]
131 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
132 pub enum RedactionSessionKind {
133 Operation,
135 Diagnostic,
137 }
138}
139
140pub use session_kind::RedactionSessionKind;
141
142#[must_use]
145#[derive(Debug)]
146pub struct RedactionSession<'policy> {
147 policy: &'policy RedactionPolicy,
148 budget: RefCell<DiagnosticBudget>,
149 kind: RedactionSessionKind,
150}
151
152impl<'policy> RedactionSession<'policy> {
153 #[must_use = "retain the operation session for redaction"]
155 #[inline]
156 pub fn operation(policy: &'policy RedactionPolicy) -> Self {
157 Self {
158 policy,
159 budget: RefCell::new(DiagnosticBudget::new(
160 policy.limits().ordinary_operation(),
161 )),
162 kind: RedactionSessionKind::Operation,
163 }
164 }
165
166 #[must_use = "retain the diagnostic session for redaction"]
168 #[inline]
169 pub fn diagnostic(policy: &'policy RedactionPolicy) -> Self {
170 Self {
171 policy,
172 budget: RefCell::new(DiagnosticBudget::new(
173 policy.limits().diagnostic_event(),
174 )),
175 kind: RedactionSessionKind::Diagnostic,
176 }
177 }
178
179 #[must_use = "use the policy snapshot for redaction"]
181 #[inline]
182 pub const fn policy(&self) -> &'policy RedactionPolicy {
183 self.policy
184 }
185
186 #[must_use = "use the session kind when selecting operation behavior"]
188 #[inline]
189 pub const fn kind(&self) -> RedactionSessionKind {
190 self.kind
191 }
192
193 #[inline]
195 pub fn consume_input(&self, bytes: usize) -> bool {
196 self.budget.borrow_mut().consume_input(bytes)
197 }
198
199 pub(crate) fn charge_output_or_fallback(
201 &self,
202 bytes: usize,
203 fallback_bytes: usize,
204 ) -> OutputCharge {
205 self.budget
206 .borrow_mut()
207 .charge_output_or_fallback(bytes, fallback_bytes)
208 }
209
210 #[must_use]
212 #[inline]
213 pub fn remaining_input_bytes(&self) -> usize {
214 self.budget.borrow().remaining_input_bytes()
215 }
216
217 #[must_use]
219 #[inline]
220 pub fn remaining_output_bytes(&self) -> usize {
221 self.budget.borrow().remaining_output_bytes()
222 }
223
224 #[must_use]
226 #[inline]
227 pub fn is_exhausted(&self) -> bool {
228 self.budget.borrow().is_exhausted()
229 }
230}