1use serde::{Deserialize, Serialize};
2
3use crate::core::framework::Framework;
4use crate::core::governance::PolicyPackStore;
5use crate::core::job::{Job, JobId, JobManager, JobStatus};
6use crate::core::module::ModuleResult;
7use crate::core::safety::{Charter, MemoryEntry, PolicySet, RiskLevel, ScopeManager};
8use crate::core::session::{Session, SessionId, SessionKind, SessionManager};
9
10#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct WorkspaceSnapshot {
12 pub charter: Charter,
13 pub scope_allow: Vec<String>,
14 pub max_risk: RiskLevel,
15 pub policy_rules: PolicySet,
16 pub policy_packs: PolicyPackStore,
17 pub memories: Vec<MemoryEntry>,
18 pub jobs: Vec<JobSnapshot>,
19 pub sessions: Vec<SessionSnapshot>,
20}
21
22#[derive(Debug, Clone, Serialize, Deserialize)]
23pub struct JobSnapshot {
24 pub id: u64,
25 pub module_name: String,
26 pub target: String,
27 pub status: JobStatus,
28 pub elapsed_secs: u64,
29 pub result: Option<ModuleResult>,
30}
31
32#[derive(Debug, Clone, Serialize, Deserialize)]
33pub struct SessionSnapshot {
34 pub id: u64,
35 pub kind: SessionKind,
36 pub target: String,
37 pub module_name: String,
38 pub elapsed_secs: u64,
39 pub closed: bool,
40}
41
42impl WorkspaceSnapshot {
43 pub fn from_framework(fw: &Framework) -> Self {
44 let jobs: Vec<JobSnapshot> = fw
45 .jobs
46 .list_recent(usize::MAX)
47 .iter()
48 .map(|j| JobSnapshot {
49 id: j.id.as_u64(),
50 module_name: j.module_name.clone(),
51 target: j.target.clone(),
52 status: j.status,
53 elapsed_secs: j.elapsed().as_secs(),
54 result: j.result.clone(),
55 })
56 .collect();
57 let sessions: Vec<SessionSnapshot> = fw
58 .sessions
59 .list_all()
60 .iter()
61 .map(|s| SessionSnapshot {
62 id: s.id.as_u64(),
63 kind: s.kind,
64 target: s.target.clone(),
65 module_name: s.module_name.clone(),
66 elapsed_secs: s.elapsed().as_secs(),
67 closed: s.closed,
68 })
69 .collect();
70 WorkspaceSnapshot {
71 charter: fw.executor.charter.clone(),
72 scope_allow: fw.executor.scope.allow.clone(),
73 max_risk: fw.executor.max_risk,
74 policy_rules: fw.executor.policy_set.clone(),
75 policy_packs: fw.policy_packs.clone(),
76 memories: fw.executor.memories.clone(),
77 jobs,
78 sessions,
79 }
80 }
81
82 pub fn save_to_file(&self, path: &str) -> Result<(), crate::core::WorkspaceError> {
83 let json = serde_json::to_string_pretty(self).map_err(crate::core::WorkspaceError::Json)?;
84 std::fs::write(path, &json).map_err(crate::core::WorkspaceError::Io)?;
85 Ok(())
86 }
87
88 pub fn load_from_file(path: &str) -> Result<Self, crate::core::WorkspaceError> {
89 let json = std::fs::read_to_string(path).map_err(crate::core::WorkspaceError::Io)?;
90 let snap: WorkspaceSnapshot =
91 serde_json::from_str(&json).map_err(crate::core::WorkspaceError::Json)?;
92 Ok(snap)
93 }
94
95 pub fn apply_to_framework(&self, fw: &mut Framework) {
96 fw.executor.charter = self.charter.clone();
97 fw.executor.scope = ScopeManager::new(self.scope_allow.clone());
98 fw.executor.max_risk = self.max_risk;
99 fw.executor.policy_set = self.policy_rules.clone();
100 fw.policy_packs = self.policy_packs.clone();
101 fw.executor.memories = self.memories.clone();
102 let max_job_id = self.jobs.iter().map(|j| j.id).max().unwrap_or(0);
103 for js in &self.jobs {
104 let mut j = Job::new(&js.module_name, &js.target);
105 j.id = JobId(js.id);
106 j.status = js.status;
107 j.result = js.result.clone();
108 fw.jobs.register(j);
109 }
110 JobManager::advance_counter(max_job_id);
111 let max_sesh_id = self.sessions.iter().map(|s| s.id).max().unwrap_or(0);
112 for ss in &self.sessions {
113 let mut s = Session::new(ss.kind, &ss.target, &ss.module_name);
114 s.id = SessionId(ss.id);
115 s.closed = ss.closed;
116 fw.sessions.register(s);
117 }
118 SessionManager::advance_counter(max_sesh_id);
119 }
120}
121
122impl Default for WorkspaceSnapshot {
123 fn default() -> Self {
124 WorkspaceSnapshot {
125 charter: Charter::default(),
126 scope_allow: Vec::new(),
127 max_risk: RiskLevel::Critical,
128 policy_rules: PolicySet::default(),
129 policy_packs: PolicyPackStore::new(),
130 memories: Vec::new(),
131 jobs: Vec::new(),
132 sessions: Vec::new(),
133 }
134 }
135}