171 lines
5.2 KiB
Plaintext
171 lines
5.2 KiB
Plaintext
use chrono::{Datelike, Duration, NaiveDate, Utc};
|
|
use indicatif::{ProgressBar, ProgressStyle};
|
|
use serde::{Deserialize, Serialize};
|
|
use serde_json::{json, Value};
|
|
use std::collections::HashMap;
|
|
use std::fs::{self, File};
|
|
use std::io::{Read, Write};
|
|
use std::path::Path;
|
|
|
|
// Example API response type
|
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
|
struct HistoryItem {
|
|
checkpoint: Option<String>,
|
|
datetime: String,
|
|
sha256: Option<String>,
|
|
filename: Option<String>,
|
|
hostname: Option<String>,
|
|
// other fields...
|
|
}
|
|
|
|
// JSON file structure
|
|
#[derive(Debug, Serialize, Deserialize)]
|
|
struct JsonFile {
|
|
error: String,
|
|
response: ResponseData,
|
|
}
|
|
|
|
#[derive(Debug, Serialize, Deserialize)]
|
|
struct ResponseData {
|
|
exechistories: Vec<HistoryItem>,
|
|
}
|
|
|
|
// Mock API function
|
|
fn history_logging(_type: &str, checkpoint: &str, _policy: &[&str]) -> Vec<HistoryItem> {
|
|
// Replace with actual API call
|
|
vec![]
|
|
}
|
|
|
|
fn main() {
|
|
let file_path = Path::new("data/example.json");
|
|
let mut checkpoint = "initial_checkpoint".to_string();
|
|
let policy_name = "policy1".to_string();
|
|
let days = 7;
|
|
|
|
// Initialize JSON output
|
|
let mut json_output = JsonFile {
|
|
error: "Success".to_string(),
|
|
response: ResponseData {
|
|
exechistories: vec![],
|
|
},
|
|
};
|
|
|
|
// Outer progress bar (filebar)
|
|
let filebar = ProgressBar::new(10_000);
|
|
filebar.set_style(
|
|
ProgressStyle::default_bar()
|
|
.template("Checkpoint Progress: {msg} [{bar:40.cyan/blue}] {pos}/{len}")
|
|
.unwrap(),
|
|
);
|
|
filebar.set_message(&checkpoint);
|
|
|
|
// Inner progress bar (total progress)
|
|
let pbar = ProgressBar::new(100);
|
|
pbar.set_style(
|
|
ProgressStyle::default_bar()
|
|
.template("Total of {msg} Complete: [{bar:40.cyan/blue}] {pos}/{len}")
|
|
.unwrap(),
|
|
);
|
|
pbar.set_message(&policy_name);
|
|
|
|
loop {
|
|
let histories = history_logging("type", &checkpoint, &[&policy_name]);
|
|
|
|
if !histories.iter().all(|h| h.datetime.len() > 0) {
|
|
eprintln!("Unexpected response format from API.");
|
|
break;
|
|
}
|
|
|
|
filebar.set_length(histories.len() as u64);
|
|
|
|
if histories.is_empty() {
|
|
break;
|
|
}
|
|
|
|
for (index, history_item) in histories.iter().enumerate() {
|
|
if history_item.checkpoint.is_none() || history_item.datetime.is_empty() {
|
|
continue;
|
|
}
|
|
|
|
if index == histories.len() - 1 {
|
|
checkpoint = history_item.checkpoint.clone().unwrap();
|
|
filebar.set_message(&checkpoint);
|
|
break;
|
|
}
|
|
|
|
// Parse date
|
|
let history_date = match NaiveDate::parse_from_str(
|
|
&history_item.datetime.replace(" +0000 UTC", ""),
|
|
"%Y-%m-%dT%H:%M:%SZ",
|
|
) {
|
|
Ok(date) => date,
|
|
Err(_) => continue,
|
|
};
|
|
|
|
let cutoff = Utc::today().naive_utc() - Duration::days(days);
|
|
if history_date >= cutoff {
|
|
json_output.response.exechistories.push(history_item.clone());
|
|
}
|
|
|
|
filebar.inc(1);
|
|
filebar.tick();
|
|
}
|
|
|
|
// Deduplicate
|
|
let mut seen: HashMap<(Option<String>, Option<String>, Option<String>), HistoryItem> =
|
|
HashMap::new();
|
|
|
|
let combined = if file_path.exists() {
|
|
let mut f = File::open(file_path).unwrap();
|
|
let mut contents = String::new();
|
|
f.read_to_string(&mut contents).unwrap();
|
|
let existing_data: JsonFile = serde_json::from_str(&contents).unwrap_or(JsonFile {
|
|
error: "Success".to_string(),
|
|
response: ResponseData {
|
|
exechistories: vec![],
|
|
},
|
|
});
|
|
[existing_data.response.exechistories, json_output.response.exechistories.clone()]
|
|
.concat()
|
|
} else {
|
|
json_output.response.exechistories.clone()
|
|
};
|
|
|
|
for entry in combined {
|
|
let key = (entry.sha256.clone(), entry.filename.clone(), entry.hostname.clone());
|
|
seen.insert(key, entry);
|
|
}
|
|
|
|
let deduplicated: Vec<HistoryItem> = seen.into_values().collect();
|
|
|
|
// Write to file
|
|
let output_file = File::create(file_path).unwrap();
|
|
serde_json::to_writer_pretty(&output_file, &json!({
|
|
"error": "Success",
|
|
"response": { "exechistories": deduplicated }
|
|
}))
|
|
.unwrap();
|
|
|
|
json_output.response.exechistories.clear();
|
|
|
|
// Update inner progress bar (percentage based on last valid item)
|
|
if let Some(last_item) = histories.last() {
|
|
if let Ok(last_date) = NaiveDate::parse_from_str(
|
|
&last_item.datetime.replace(" +0000 UTC", ""),
|
|
"%Y-%m-%dT%H:%M:%SZ",
|
|
) {
|
|
let date_diff = Utc::today().naive_utc() - last_date;
|
|
let percentage_diff = ((days + 10) - date_diff.num_days()) as f64 / (days + 10) as f64 * 100.0;
|
|
pbar.set_position(percentage_diff.round() as u64);
|
|
pbar.set_message(&policy_name);
|
|
pbar.tick();
|
|
}
|
|
}
|
|
|
|
filebar.set_position(1);
|
|
}
|
|
|
|
filebar.finish();
|
|
pbar.finish();
|
|
}
|