casting_cli/
tool_config.rs

1use serde::{Deserialize, Serialize};
2use version_migrate::migrator;
3use version_migrate::DeriveQueryable as Queryable;
4use version_migrate::{IntoDomain, MigratesTo, Migrator, Versioned};
5
6#[derive(Debug, Clone, Serialize, Deserialize)]
7#[serde(tag = "type", rename_all = "lowercase")]
8pub enum ToolType {
9    Cli { bin_path: String },
10    Api { spec_url: String, base_url: String },
11    Expert,
12}
13
14// Version 1.0.0 - Old format with direct bin_path
15#[derive(Debug, Serialize, Deserialize, Versioned)]
16#[versioned(version = "1.0.0")]
17pub struct ToolConfigV1 {
18    pub tool_name: String,
19    pub bin_path: String,
20    pub persona: Option<String>,
21    pub created_at: String,
22}
23
24// Version 2.0.0 - New format with ToolType
25#[derive(Debug, Serialize, Deserialize, Versioned)]
26#[versioned(version = "2.0.0")]
27pub struct ToolConfigV2 {
28    pub tool_name: String,
29    #[serde(flatten)]
30    pub tool_type: ToolType,
31    pub persona: Option<String>,
32    pub created_at: String,
33}
34
35// Version 3.0.0 - Added aliases support
36#[derive(Debug, Serialize, Deserialize, Versioned)]
37#[versioned(version = "3.0.0")]
38pub struct ToolConfigV3 {
39    pub tool_name: String,
40    #[serde(flatten)]
41    pub tool_type: ToolType,
42    pub persona: Option<String>,
43    #[serde(default)]
44    pub aliases: Vec<String>,
45    pub created_at: String,
46}
47
48// Domain model (current ToolConfig)
49#[derive(Debug, Serialize, Deserialize, Queryable)]
50#[queryable(entity = "tool_config")]
51pub struct ToolConfig {
52    pub tool_name: String,
53    #[serde(flatten)]
54    pub tool_type: ToolType,
55    pub persona: Option<String>,
56    #[serde(default)]
57    pub aliases: Vec<String>,
58    pub created_at: String,
59}
60
61// Migration: V1 -> V2
62impl MigratesTo<ToolConfigV2> for ToolConfigV1 {
63    fn migrate(self) -> ToolConfigV2 {
64        ToolConfigV2 {
65            tool_name: self.tool_name,
66            tool_type: ToolType::Cli {
67                bin_path: self.bin_path,
68            },
69            persona: self.persona,
70            created_at: self.created_at,
71        }
72    }
73}
74
75// Migration: V2 -> V3
76impl MigratesTo<ToolConfigV3> for ToolConfigV2 {
77    fn migrate(self) -> ToolConfigV3 {
78        ToolConfigV3 {
79            tool_name: self.tool_name,
80            tool_type: self.tool_type,
81            persona: self.persona,
82            aliases: Vec::new(), // Existing configs have no aliases
83            created_at: self.created_at,
84        }
85    }
86}
87
88// Conversion: V3 -> Domain
89impl IntoDomain<ToolConfig> for ToolConfigV3 {
90    fn into_domain(self) -> ToolConfig {
91        ToolConfig {
92            tool_name: self.tool_name,
93            tool_type: self.tool_type,
94            persona: self.persona,
95            aliases: self.aliases,
96            created_at: self.created_at,
97        }
98    }
99}
100
101impl version_migrate::FromDomain<ToolConfig> for ToolConfigV3 {
102    fn from_domain(config: ToolConfig) -> Self {
103        ToolConfigV3 {
104            tool_name: config.tool_name,
105            tool_type: config.tool_type,
106            persona: config.persona,
107            aliases: config.aliases,
108            created_at: config.created_at,
109        }
110    }
111}
112
113/// Setup migrator for ToolConfig versioning
114pub fn setup_config_migrator() -> anyhow::Result<Migrator> {
115    Ok(
116        migrator!("tool_config" => [ToolConfigV1, ToolConfigV2, ToolConfigV3, ToolConfig], save = true)?,
117    )
118}