pawan/config/
migration.rs1use chrono::Utc;
7use tracing;
8use std::path::PathBuf;
9use super::{PawanConfig, default_tool_idle_timeout};
10
11const LATEST_CONFIG_VERSION: u32 = 1;
13
14#[derive(Debug)]
16pub struct MigrationResult {
17 pub migrated: bool,
19 pub from_version: u32,
21 pub to_version: u32,
23 pub backup_path: Option<PathBuf>,
25}
26
27impl MigrationResult {
28 pub fn new(from_version: u32, to_version: u32, backup_path: Option<PathBuf>) -> Self {
29 Self {
30 migrated: from_version != to_version,
31 from_version,
32 to_version,
33 backup_path,
34 }
35 }
36
37 pub fn no_migration(version: u32) -> Self {
38 Self {
39 migrated: false,
40 from_version: version,
41 to_version: version,
42 backup_path: None,
43 }
44 }
45}
46
47pub fn migrate_to_latest(config: &mut PawanConfig, config_path: Option<&PathBuf>) -> MigrationResult {
54 let current_version = config.config_version;
55
56 if current_version >= LATEST_CONFIG_VERSION {
57 return MigrationResult::no_migration(current_version);
58 }
59
60 let backup_path = config_path.and_then(|path| create_backup(path).ok());
61
62 let mut version = current_version;
63 while version < LATEST_CONFIG_VERSION {
64 version = match apply_migration(config, version + 1) {
65 Ok(v) => v,
66 Err(e) => {
67 tracing::error!(
68 from_version = version,
69 to_version = LATEST_CONFIG_VERSION,
70 error = %e,
71 "Config migration failed"
72 );
73 return MigrationResult::new(current_version, version, backup_path);
74 }
75 };
76 }
77
78 config.config_version = LATEST_CONFIG_VERSION;
79 MigrationResult::new(current_version, LATEST_CONFIG_VERSION, backup_path)
80}
81
82pub fn save_config(config: &PawanConfig, path: &PathBuf) -> Result<(), String> {
84 let toml_string = toml::to_string_pretty(config)
85 .map_err(|e| format!("Failed to serialize config to TOML: {}", e))?;
86
87 std::fs::write(path, toml_string)
88 .map_err(|e| format!("Failed to write config to {}: {}", path.display(), e))?;
89
90 tracing::info!(path = %path.display(), "Config saved");
91 Ok(())
92}
93
94fn apply_migration(config: &mut PawanConfig, target_version: u32) -> Result<u32, String> {
99 match target_version {
100 1 => migrate_to_v1(config),
101 _ => Err(format!("Unknown target version: {}", target_version)),
102 }
103}
104
105pub(super) fn migrate_to_v1(config: &mut PawanConfig) -> Result<u32, String> {
108 config.config_version = 1;
109 if config.tool_call_idle_timeout_secs == 0 {
110 config.tool_call_idle_timeout_secs = default_tool_idle_timeout();
111 }
112 tracing::info!("Config migrated to version 1");
113 Ok(1)
114}
115
116fn create_backup(config_path: &PathBuf) -> Result<PathBuf, String> {
117 let timestamp = Utc::now().format("%Y%m%d_%H%M%S");
118 let backup_path = config_path.with_extension(format!("toml.backup.{}", timestamp));
119
120 std::fs::copy(config_path, &backup_path)
121 .map_err(|e| format!("Failed to create backup at {}: {}", backup_path.display(), e))?;
122
123 tracing::info!(backup = %backup_path.display(), "Config backup created");
124 Ok(backup_path)
125}