1use serde::{Deserialize, Serialize};
7use std::fs;
8use std::path::Path;
9
10#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
12pub struct DevKitConfig {
13 #[serde(default)]
15 pub network: NetworkConfig,
16 #[serde(default)]
18 pub decode: DecodeConfig,
19 #[serde(default)]
21 pub storage: StorageConfig,
22 #[serde(default)]
24 pub contracts: std::collections::HashMap<String, ContractConfig>,
25}
26
27#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
29pub struct ContractConfig {
30 pub path: String,
32 #[serde(default, skip_serializing_if = "Vec::is_empty")]
34 pub deploy_after: Vec<String>,
35}
36
37#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
39pub struct NetworkConfig {
40 pub rpc_url: String,
42 pub passphrase: String,
44 pub timeout_secs: Option<u64>,
46 pub pool_max_idle_per_host: Option<usize>,
48}
49
50#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
52pub struct DecodeConfig {
53 pub max_depth: usize,
55 pub allow_fallback_hex: bool,
57}
58
59#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
61pub struct StorageConfig {
62 pub max_entries: usize,
64 pub ttl_warning_days: u32,
66}
67
68impl Default for StorageConfig {
69 fn default() -> Self {
70 Self {
71 max_entries: 200,
72 ttl_warning_days: 30,
73 }
74 }
75}
76
77impl Default for DecodeConfig {
78 fn default() -> Self {
79 Self {
80 max_depth: 32,
81 allow_fallback_hex: true,
82 }
83 }
84}
85
86impl Default for NetworkConfig {
87 fn default() -> Self {
88 Self {
89 rpc_url: "https://soroban-testnet.stellar.org".to_string(),
90 passphrase: "Test SDF Network ; September 2015".to_string(),
91 timeout_secs: Some(15),
92 pool_max_idle_per_host: Some(100),
93 }
94 }
95}
96
97impl DevKitConfig {
98 pub fn from_toml(content: &str) -> Result<Self, toml::de::Error> {
100 toml::from_str(content)
101 }
102
103 pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Self, Box<dyn std::error::Error>> {
107 let path = path.as_ref();
108 if path.exists() {
109 let content = fs::read_to_string(path)?;
110 let config = Self::from_toml(&content)?;
111 Ok(config)
112 } else {
113 Ok(Self::default())
114 }
115 }
116}
117
118#[cfg(test)]
119mod tests {
120 use super::*;
121
122 #[test]
123 fn test_default_config() {
124 let default_config = DevKitConfig::default();
125 assert_eq!(default_config.decode.max_depth, 32);
126 assert!(default_config.decode.allow_fallback_hex);
127 assert_eq!(default_config.storage.max_entries, 200);
128 assert_eq!(default_config.storage.ttl_warning_days, 30);
129 }
130
131 #[test]
132 fn test_parse_valid_toml() {
133 let toml_data = r#"
134 [network]
135 rpc_url = "http://localhost:8000"
136 passphrase = "Standalone Network"
137
138 [decode]
139 max_depth = 16
140 allow_fallback_hex = false
141
142 [storage]
143 max_entries = 100
144 ttl_warning_days = 7
145 "#;
146 let parsed = DevKitConfig::from_toml(toml_data).unwrap();
147 assert_eq!(parsed.network.rpc_url, "http://localhost:8000");
148 assert_eq!(parsed.decode.max_depth, 16);
149 assert!(!parsed.decode.allow_fallback_hex);
150 assert_eq!(parsed.storage.max_entries, 100);
151 assert_eq!(parsed.storage.ttl_warning_days, 7);
152 }
153
154 #[test]
155 fn test_old_toml_without_storage() {
156 let toml_data = r#"
157 [network]
158 rpc_url = "http://localhost:8000"
159 passphrase = "Standalone Network"
160
161 [decode]
162 max_depth = 16
163 allow_fallback_hex = false
164 "#;
165 let parsed = DevKitConfig::from_toml(toml_data).unwrap();
166 assert_eq!(parsed.storage.max_entries, 200);
167 }
168}