Skip to main content

sys_shred/core/
report.rs

1//! # Forensic Audit Reporting
2//!
3//! Provides structures and logic for capturing and serializing the results
4//! of shredding operations for audit and compliance purposes.
5
6use crate::cli::args::{AuditFormat, ShredMethod};
7use chrono::{DateTime, Utc};
8use serde::Serialize;
9use std::fs::File;
10use std::io::Write;
11use std::path::{Path, PathBuf};
12
13/// Represents a single file destruction event.
14#[derive(Debug, Serialize, Clone)]
15pub struct ShredEvent {
16    /// The path to the file that was targeted.
17    pub path: PathBuf,
18    /// Timestamp of when the operation completed.
19    pub timestamp: DateTime<Utc>,
20    /// The algorithm used for destruction.
21    pub method: ShredMethod,
22    /// Whether the operation succeeded or failed.
23    pub success: bool,
24    /// Detailed error message if the operation failed.
25    pub error: Option<String>,
26}
27
28/// A comprehensive report of a shredding session.
29#[derive(Debug, Serialize, Clone)]
30pub struct ShredReport {
31    /// Timestamp of when the report was generated.
32    pub generated_at: DateTime<Utc>,
33    /// Total number of files targeted in this session.
34    pub total_files: usize,
35    /// Number of successful destructions.
36    pub success_count: usize,
37    /// Number of failed destructions.
38    pub failure_count: usize,
39    /// Individual event logs.
40    pub events: Vec<ShredEvent>,
41}
42
43impl ShredReport {
44    /// Creates a new `ShredReport` from a collection of events.
45    pub fn new(events: Vec<ShredEvent>) -> Self {
46        let total = events.len();
47        let success_count = events.iter().filter(|e| e.success).count();
48        let failure_count = total - success_count;
49
50        Self {
51            generated_at: Utc::now(),
52            total_files: total,
53            success_count,
54            failure_count,
55            events,
56        }
57    }
58
59    /// Saves the report to the specified path in the given format.
60    pub fn save(&self, path: &Path, format: AuditFormat) -> std::io::Result<()> {
61        let content = match format {
62            AuditFormat::Json => {
63                serde_json::to_string_pretty(self).map_err(std::io::Error::other)?
64            }
65            AuditFormat::Text => self.to_text(),
66        };
67
68        let mut file = File::create(path)?;
69        file.write_all(content.as_bytes())?;
70        Ok(())
71    }
72
73    fn to_text(&self) -> String {
74        let mut text = String::new();
75        text.push_str("=== sys-shred Forensic Audit Report ===\n");
76        text.push_str(&format!("Generated at: {}\n", self.generated_at));
77        text.push_str(&format!("Total Files:  {}\n", self.total_files));
78        text.push_str(&format!("Successes:    {}\n", self.success_count));
79        text.push_str(&format!("Failures:     {}\n", self.failure_count));
80        text.push_str("========================================\n\n");
81
82        for event in &self.events {
83            let status = if event.success { "SUCCESS" } else { "FAILURE" };
84            text.push_str(&format!(
85                "[{}] {:?} - {} ({:?})\n",
86                event.timestamp.format("%Y-%m-%d %H:%M:%S"),
87                event.path,
88                status,
89                event.method
90            ));
91            if let Some(ref err) = event.error {
92                text.push_str(&format!("  Error: {}\n", err));
93            }
94        }
95
96        text
97    }
98}