opendev_context/compaction/
artifacts.rs1use std::collections::HashMap;
4
5use chrono::Local;
6use serde::{Deserialize, Serialize};
7
8#[derive(Debug, Clone, Default, Serialize, Deserialize)]
13pub struct ArtifactIndex {
14 pub entries: HashMap<String, ArtifactEntry>,
15}
16
17#[derive(Debug, Clone, Serialize, Deserialize)]
18pub struct ArtifactEntry {
19 pub file_path: String,
20 pub last_operation: String,
21 pub last_details: String,
22 pub created_at: String,
23 pub updated_at: String,
24 pub operation_count: u32,
25 pub operations_seen: Vec<String>,
26}
27
28impl ArtifactIndex {
29 pub fn new() -> Self {
30 Self::default()
31 }
32
33 pub fn record(&mut self, file_path: &str, operation: &str, details: &str) {
35 let now = Local::now().to_rfc3339();
36 if let Some(existing) = self.entries.get_mut(file_path) {
37 existing.last_operation.clear();
38 existing.last_operation.push_str(operation);
39 existing.last_details.clear();
40 existing.last_details.push_str(details);
41 existing.updated_at = now;
42 existing.operation_count += 1;
43 if !existing.operations_seen.iter().any(|s| s == operation) {
44 existing.operations_seen.push(operation.to_owned());
45 }
46 } else {
47 let op = operation.to_owned();
48 self.entries.insert(
49 file_path.to_owned(),
50 ArtifactEntry {
51 file_path: file_path.to_owned(),
52 last_operation: op.clone(),
53 last_details: details.to_owned(),
54 created_at: now.clone(),
55 updated_at: now,
56 operation_count: 1,
57 operations_seen: vec![op],
58 },
59 );
60 }
61 }
62
63 pub fn as_summary(&self) -> String {
65 if self.entries.is_empty() {
66 return String::new();
67 }
68 let mut lines = vec!["## Artifact Index (files touched this session)".to_string()];
69 for (path, entry) in &self.entries {
70 let ops = entry.operations_seen.join(", ");
71 let detail = if entry.last_details.is_empty() {
72 String::new()
73 } else {
74 format!(" — {}", entry.last_details)
75 };
76 lines.push(format!("- `{path}` [{ops}]{detail}"));
77 }
78 lines.join("\n")
79 }
80
81 pub fn len(&self) -> usize {
82 self.entries.len()
83 }
84
85 pub fn is_empty(&self) -> bool {
86 self.entries.is_empty()
87 }
88
89 pub fn to_json(&self) -> serde_json::Value {
91 serde_json::to_value(self).unwrap_or(serde_json::Value::Null)
92 }
93
94 pub fn from_json(value: &serde_json::Value) -> Option<Self> {
96 serde_json::from_value(value.clone()).ok()
97 }
98}
99
100#[cfg(test)]
101#[path = "artifacts_tests.rs"]
102mod tests;