baobao_ir/
resource.rs

1//! Resource configuration types.
2//!
3//! These types represent the unified configuration for database pools,
4//! SQLite options, and other resources. They serve as the single source
5//! of truth for code generation, eliminating duplication between crates.
6
7use std::time::Duration;
8
9use serde::Serialize;
10
11use crate::serde_helpers::serialize_option_duration;
12
13/// Connection pool configuration.
14///
15/// This is the unified type for pool configuration, replacing the duplicate
16/// `PoolConfigInfo` (bao-codegen) and `PoolConfig` (bao-codegen adapters).
17#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize)]
18pub struct PoolConfig {
19    /// Maximum number of connections in the pool.
20    pub max_connections: Option<u32>,
21    /// Minimum number of connections to maintain.
22    pub min_connections: Option<u32>,
23    /// Timeout for acquiring a connection from the pool (milliseconds).
24    #[serde(serialize_with = "serialize_option_duration")]
25    pub acquire_timeout: Option<Duration>,
26    /// Maximum time a connection can remain idle before being closed (milliseconds).
27    #[serde(serialize_with = "serialize_option_duration")]
28    pub idle_timeout: Option<Duration>,
29    /// Maximum lifetime of a connection (milliseconds).
30    #[serde(serialize_with = "serialize_option_duration")]
31    pub max_lifetime: Option<Duration>,
32}
33
34impl PoolConfig {
35    /// Returns true if any pool option is configured.
36    pub fn has_config(&self) -> bool {
37        self.max_connections.is_some()
38            || self.min_connections.is_some()
39            || self.acquire_timeout.is_some()
40            || self.idle_timeout.is_some()
41            || self.max_lifetime.is_some()
42    }
43}
44
45/// SQLite-specific configuration options.
46///
47/// This is the unified type for SQLite configuration, replacing the duplicate
48/// `SqliteConfigInfo` (bao-codegen) and `SqliteConfig` (bao-codegen adapters).
49#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize)]
50pub struct SqliteOptions {
51    /// Direct file path to the SQLite database.
52    pub path: Option<String>,
53    /// Create the database file if it doesn't exist.
54    pub create_if_missing: Option<bool>,
55    /// Open the database in read-only mode.
56    pub read_only: Option<bool>,
57    /// Journal mode.
58    pub journal_mode: Option<JournalMode>,
59    /// Synchronous mode.
60    pub synchronous: Option<SynchronousMode>,
61    /// Busy timeout (milliseconds).
62    #[serde(serialize_with = "serialize_option_duration")]
63    pub busy_timeout: Option<Duration>,
64    /// Enable foreign key constraints.
65    pub foreign_keys: Option<bool>,
66}
67
68impl SqliteOptions {
69    /// Returns true if any SQLite-specific option is configured.
70    pub fn has_config(&self) -> bool {
71        self.create_if_missing.is_some()
72            || self.read_only.is_some()
73            || self.journal_mode.is_some()
74            || self.synchronous.is_some()
75            || self.busy_timeout.is_some()
76            || self.foreign_keys.is_some()
77    }
78}
79
80/// SQLite journal mode.
81#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, Serialize)]
82pub enum JournalMode {
83    #[default]
84    Wal,
85    Delete,
86    Truncate,
87    Persist,
88    Memory,
89    Off,
90}
91
92impl JournalMode {
93    /// Get the PascalCase string representation for code generation.
94    pub fn as_str(&self) -> &'static str {
95        match self {
96            JournalMode::Wal => "Wal",
97            JournalMode::Delete => "Delete",
98            JournalMode::Truncate => "Truncate",
99            JournalMode::Persist => "Persist",
100            JournalMode::Memory => "Memory",
101            JournalMode::Off => "Off",
102        }
103    }
104}
105
106/// SQLite synchronous mode.
107#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, Serialize)]
108pub enum SynchronousMode {
109    Off,
110    Normal,
111    #[default]
112    Full,
113    Extra,
114}
115
116impl SynchronousMode {
117    /// Get the PascalCase string representation for code generation.
118    pub fn as_str(&self) -> &'static str {
119        match self {
120            SynchronousMode::Off => "Off",
121            SynchronousMode::Normal => "Normal",
122            SynchronousMode::Full => "Full",
123            SynchronousMode::Extra => "Extra",
124        }
125    }
126}
127
128#[cfg(test)]
129mod tests {
130    use super::*;
131
132    #[test]
133    fn test_pool_config_has_config() {
134        let empty = PoolConfig::default();
135        assert!(!empty.has_config());
136
137        let with_max = PoolConfig {
138            max_connections: Some(10),
139            ..Default::default()
140        };
141        assert!(with_max.has_config());
142
143        let with_timeout = PoolConfig {
144            acquire_timeout: Some(Duration::from_secs(30)),
145            ..Default::default()
146        };
147        assert!(with_timeout.has_config());
148    }
149
150    #[test]
151    fn test_sqlite_options_has_config() {
152        let empty = SqliteOptions::default();
153        assert!(!empty.has_config());
154
155        let with_journal = SqliteOptions {
156            journal_mode: Some(JournalMode::Wal),
157            ..Default::default()
158        };
159        assert!(with_journal.has_config());
160
161        // path alone doesn't count as "config" (it's required for connection)
162        let with_path_only = SqliteOptions {
163            path: Some("db.sqlite".to_string()),
164            ..Default::default()
165        };
166        assert!(!with_path_only.has_config());
167    }
168
169    #[test]
170    fn test_journal_mode_as_str() {
171        assert_eq!(JournalMode::Wal.as_str(), "Wal");
172        assert_eq!(JournalMode::Delete.as_str(), "Delete");
173        assert_eq!(JournalMode::Truncate.as_str(), "Truncate");
174        assert_eq!(JournalMode::Persist.as_str(), "Persist");
175        assert_eq!(JournalMode::Memory.as_str(), "Memory");
176        assert_eq!(JournalMode::Off.as_str(), "Off");
177    }
178
179    #[test]
180    fn test_synchronous_mode_as_str() {
181        assert_eq!(SynchronousMode::Off.as_str(), "Off");
182        assert_eq!(SynchronousMode::Normal.as_str(), "Normal");
183        assert_eq!(SynchronousMode::Full.as_str(), "Full");
184        assert_eq!(SynchronousMode::Extra.as_str(), "Extra");
185    }
186
187    #[test]
188    fn test_default_values() {
189        assert_eq!(JournalMode::default(), JournalMode::Wal);
190        assert_eq!(SynchronousMode::default(), SynchronousMode::Full);
191    }
192}