| 350 | } |
| 351 | |
| 352 | pub fn save(&self, path: &Path) -> std::io::Result<()> { |
| 353 | if let Some(parent) = path.parent() { |
| 354 | std::fs::create_dir_all(parent)?; |
| 355 | } |
| 356 | let mut sessions: Vec<PersistedSession> = self |
| 357 | .emitted |
| 358 | .iter() |
| 359 | .map(|(session_id, emitted)| PersistedSession { |
| 360 | session_id: session_id.clone(), |
| 361 | emitted: *emitted, |
| 362 | }) |
| 363 | .collect(); |
| 364 | sessions.sort_by(|a, b| a.session_id.cmp(&b.session_id)); |
| 365 | let mut categories: Vec<PersistedCategory> = self |
| 366 | .categories |
| 367 | .iter() |
| 368 | .map(|((session_id, category), state)| PersistedCategory { |
| 369 | session_id: session_id.clone(), |
| 370 | category: category.as_key().to_string(), |
| 371 | hinted: state.hinted, |
| 372 | triggers_after_hint: state.triggers_after_hint, |
| 373 | escalated: state.escalated, |
| 374 | }) |
| 375 | .collect(); |
| 376 | categories.sort_by(|a, b| { |
| 377 | a.session_id |
| 378 | .cmp(&b.session_id) |
| 379 | .then_with(|| a.category.cmp(&b.category)) |
| 380 | }); |
| 381 | let persisted = PersistedHints { |
| 382 | version: HINT_STORE_VERSION, |
| 383 | sessions, |
| 384 | categories, |
| 385 | }; |
| 386 | let json = serde_json::to_string_pretty(&persisted).map_err(std::io::Error::other)?; |
| 387 | std::fs::write(path, format!("{json}\n")) |
| 388 | } |
| 389 | } |
| 390 | |
| 391 | /// Schema version written by [`ToolHintDedupe::save`]. v1 was a bare entry array. |