1use crate::proof::{Proof, ProofNode, ProofNodeId, ProofStep};
7use rustc_hash::FxHashMap;
8use std::io::{self, BufWriter, Write};
9
10#[derive(Debug, Clone)]
12pub struct StreamConfig {
13 pub chunk_size: usize,
15 pub buffer_size: usize,
17 pub compress: bool,
19}
20
21impl Default for StreamConfig {
22 fn default() -> Self {
23 Self {
24 chunk_size: 1000,
25 buffer_size: 8192,
26 compress: false,
27 }
28 }
29}
30
31impl StreamConfig {
32 pub fn new() -> Self {
34 Self::default()
35 }
36
37 pub fn with_chunk_size(mut self, size: usize) -> Self {
39 self.chunk_size = size;
40 self
41 }
42
43 pub fn with_buffer_size(mut self, size: usize) -> Self {
45 self.buffer_size = size;
46 self
47 }
48
49 pub fn with_compression(mut self, enabled: bool) -> Self {
51 self.compress = enabled;
52 self
53 }
54}
55
56#[derive(Debug, Clone)]
58pub struct ProofChunk {
59 pub start_id: ProofNodeId,
61 pub nodes: Vec<ProofNode>,
63 pub index: usize,
65 pub total_chunks: usize,
67}
68
69impl ProofChunk {
70 pub fn new(
72 start_id: ProofNodeId,
73 nodes: Vec<ProofNode>,
74 index: usize,
75 total_chunks: usize,
76 ) -> Self {
77 Self {
78 start_id,
79 nodes,
80 index,
81 total_chunks,
82 }
83 }
84
85 pub fn len(&self) -> usize {
87 self.nodes.len()
88 }
89
90 pub fn is_empty(&self) -> bool {
92 self.nodes.is_empty()
93 }
94
95 pub fn is_last(&self) -> bool {
97 self.index + 1 == self.total_chunks
98 }
99}
100
101pub struct ProofStreamer {
103 config: StreamConfig,
104}
105
106impl Default for ProofStreamer {
107 fn default() -> Self {
108 Self::new()
109 }
110}
111
112impl ProofStreamer {
113 pub fn new() -> Self {
115 Self {
116 config: StreamConfig::default(),
117 }
118 }
119
120 pub fn with_config(config: StreamConfig) -> Self {
122 Self { config }
123 }
124
125 pub fn stream_chunks<'a>(&self, proof: &'a Proof) -> ProofChunkIterator<'a> {
127 ProofChunkIterator::new(proof, self.config.chunk_size)
128 }
129
130 pub fn process_streaming<F>(&self, proof: &Proof, mut callback: F) -> Result<(), String>
132 where
133 F: FnMut(&ProofChunk) -> Result<(), String>,
134 {
135 for chunk in self.stream_chunks(proof) {
136 callback(&chunk)?;
137 }
138 Ok(())
139 }
140
141 pub fn write_streaming<W: Write>(&self, proof: &Proof, writer: W) -> io::Result<()> {
143 let mut buf_writer = BufWriter::with_capacity(self.config.buffer_size, writer);
144
145 for chunk in self.stream_chunks(proof) {
146 for node in &chunk.nodes {
147 writeln!(buf_writer, "{}", self.format_node(node))?;
148 }
149 }
150
151 buf_writer.flush()
152 }
153
154 fn format_node(&self, node: &ProofNode) -> String {
156 match &node.step {
157 ProofStep::Axiom { conclusion } => {
158 format!("axiom {} : {}", node.id, conclusion)
159 }
160 ProofStep::Inference {
161 rule,
162 premises,
163 conclusion,
164 ..
165 } => {
166 let premise_str = premises
167 .iter()
168 .map(|p| p.to_string())
169 .collect::<Vec<_>>()
170 .join(", ");
171 format!(
172 "infer {} : {} from [{}] => {}",
173 node.id, rule, premise_str, conclusion
174 )
175 }
176 }
177 }
178}
179
180pub struct ProofChunkIterator<'a> {
182 proof: &'a Proof,
183 chunk_size: usize,
184 current_index: usize,
185 total_nodes: usize,
186}
187
188impl<'a> ProofChunkIterator<'a> {
189 pub fn new(proof: &'a Proof, chunk_size: usize) -> Self {
191 Self {
192 proof,
193 chunk_size,
194 current_index: 0,
195 total_nodes: proof.len(),
196 }
197 }
198}
199
200impl<'a> Iterator for ProofChunkIterator<'a> {
201 type Item = ProofChunk;
202
203 fn next(&mut self) -> Option<Self::Item> {
204 if self.current_index >= self.total_nodes {
205 return None;
206 }
207
208 let start_idx = self.current_index;
209 let end_idx = (start_idx + self.chunk_size).min(self.total_nodes);
210
211 let nodes: Vec<ProofNode> = self.proof.nodes()[start_idx..end_idx].to_vec();
212
213 let total_chunks = self.total_nodes.div_ceil(self.chunk_size);
214 let chunk_index = start_idx / self.chunk_size;
215
216 let start_id = if !nodes.is_empty() {
217 nodes[0].id
218 } else {
219 ProofNodeId(0)
220 };
221
222 self.current_index = end_idx;
223
224 Some(ProofChunk::new(start_id, nodes, chunk_index, total_chunks))
225 }
226}
227
228pub struct StreamingProofBuilder {
230 nodes: Vec<ProofNode>,
232 node_map: FxHashMap<ProofNodeId, usize>,
234 proof: Proof,
236 #[allow(dead_code)]
238 config: StreamConfig,
239}
240
241impl Default for StreamingProofBuilder {
242 fn default() -> Self {
243 Self::new()
244 }
245}
246
247impl StreamingProofBuilder {
248 pub fn new() -> Self {
250 Self {
251 nodes: Vec::new(),
252 node_map: FxHashMap::default(),
253 proof: Proof::new(),
254 config: StreamConfig::default(),
255 }
256 }
257
258 pub fn with_config(config: StreamConfig) -> Self {
260 Self {
261 nodes: Vec::new(),
262 node_map: FxHashMap::default(),
263 proof: Proof::new(),
264 config,
265 }
266 }
267
268 pub fn add_axiom(&mut self, conclusion: &str) -> ProofNodeId {
270 self.proof.add_axiom(conclusion)
271 }
272
273 pub fn add_inference(
275 &mut self,
276 rule: &str,
277 premises: Vec<ProofNodeId>,
278 conclusion: &str,
279 ) -> ProofNodeId {
280 self.proof.add_inference(rule, premises, conclusion)
281 }
282
283 pub fn flush(&mut self) -> Proof {
285 let proof = std::mem::take(&mut self.proof);
286 self.nodes.clear();
287 self.node_map.clear();
288 proof
289 }
290
291 pub fn len(&self) -> usize {
293 self.proof.len()
294 }
295
296 pub fn is_empty(&self) -> bool {
298 self.proof.is_empty()
299 }
300}
301
302#[cfg(test)]
303mod tests {
304 use super::*;
305
306 #[test]
307 fn test_stream_config_new() {
308 let config = StreamConfig::new();
309 assert_eq!(config.chunk_size, 1000);
310 assert_eq!(config.buffer_size, 8192);
311 assert!(!config.compress);
312 }
313
314 #[test]
315 fn test_stream_config_with_settings() {
316 let config = StreamConfig::new()
317 .with_chunk_size(500)
318 .with_buffer_size(4096)
319 .with_compression(true);
320 assert_eq!(config.chunk_size, 500);
321 assert_eq!(config.buffer_size, 4096);
322 assert!(config.compress);
323 }
324
325 #[test]
326 fn test_proof_chunk_new() {
327 let chunk = ProofChunk::new(ProofNodeId(0), Vec::new(), 0, 1);
328 assert_eq!(chunk.index, 0);
329 assert_eq!(chunk.total_chunks, 1);
330 assert!(chunk.is_empty());
331 assert!(chunk.is_last());
332 }
333
334 #[test]
335 fn test_proof_streamer_new() {
336 let streamer = ProofStreamer::new();
337 assert_eq!(streamer.config.chunk_size, 1000);
338 }
339
340 #[test]
341 fn test_stream_chunks() {
342 let mut proof = Proof::new();
343 for i in 0..5 {
344 proof.add_axiom(format!("axiom_{}", i));
345 }
346
347 let streamer = ProofStreamer::with_config(StreamConfig::new().with_chunk_size(2));
348 let chunks: Vec<ProofChunk> = streamer.stream_chunks(&proof).collect();
349
350 assert_eq!(chunks.len(), 3); assert_eq!(chunks[0].len(), 2);
352 assert_eq!(chunks[1].len(), 2);
353 assert_eq!(chunks[2].len(), 1);
354 }
355
356 #[test]
357 fn test_chunk_iterator() {
358 let mut proof = Proof::new();
359 proof.add_axiom("x = x");
360 proof.add_axiom("y = y");
361
362 let mut iter = ProofChunkIterator::new(&proof, 1);
363 let chunk1 = iter.next();
364 assert!(chunk1.is_some());
365 assert_eq!(chunk1.expect("test operation should succeed").len(), 1);
366
367 let chunk2 = iter.next();
368 assert!(chunk2.is_some());
369 assert_eq!(chunk2.expect("test operation should succeed").len(), 1);
370
371 let chunk3 = iter.next();
372 assert!(chunk3.is_none());
373 }
374
375 #[test]
376 fn test_process_streaming() {
377 let mut proof = Proof::new();
378 proof.add_axiom("x = x");
379 proof.add_axiom("y = y");
380
381 let streamer = ProofStreamer::new();
382 let mut count = 0;
383 let result = streamer.process_streaming(&proof, |chunk| {
384 count += chunk.len();
385 Ok(())
386 });
387
388 assert!(result.is_ok());
389 assert_eq!(count, 2);
390 }
391
392 #[test]
393 fn test_streaming_builder() {
394 let mut builder = StreamingProofBuilder::new();
395 builder.add_axiom("x = x");
396 builder.add_axiom("y = y");
397
398 assert_eq!(builder.len(), 2);
399 assert!(!builder.is_empty());
400
401 let proof = builder.flush();
402 assert_eq!(proof.len(), 2);
403 assert_eq!(builder.len(), 0);
404 }
405
406 #[test]
407 fn test_write_streaming() {
408 let mut proof = Proof::new();
409 proof.add_axiom("x = x");
410 proof.add_axiom("y = y");
411
412 let streamer = ProofStreamer::new();
413 let mut output = Vec::new();
414 let result = streamer.write_streaming(&proof, &mut output);
415
416 assert!(result.is_ok());
417 let output_str = String::from_utf8(output).expect("test operation should succeed");
418 assert!(output_str.contains("axiom"));
419 assert!(output_str.contains("x = x"));
420 }
421}