1use serde::{Deserialize, Serialize};
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
10#[serde(rename_all = "kebab-case")]
11pub enum Precedence {
12 #[default]
14 AcpFirst,
15 NativeFirst,
17 Merge,
19}
20
21impl std::fmt::Display for Precedence {
22 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
23 match self {
24 Precedence::AcpFirst => write!(f, "acp-first"),
25 Precedence::NativeFirst => write!(f, "native-first"),
26 Precedence::Merge => write!(f, "merge"),
27 }
28 }
29}
30
31#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
33#[serde(rename_all = "lowercase")]
34pub enum Strictness {
35 #[default]
37 Permissive,
38 Strict,
40}
41
42#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
44#[serde(rename_all = "lowercase")]
45pub enum DocstringStyle {
46 #[default]
48 Auto,
49 Google,
51 Numpy,
53 Sphinx,
55}
56
57#[derive(Debug, Clone, Serialize, Deserialize)]
59#[serde(rename_all = "camelCase")]
60pub struct JsDocConfig {
61 #[serde(default = "default_true")]
63 pub enabled: bool,
64 #[serde(default = "default_true")]
66 pub extract_types: bool,
67 #[serde(default = "default_jsdoc_tags")]
69 pub convert_tags: Vec<String>,
70}
71
72impl Default for JsDocConfig {
73 fn default() -> Self {
74 Self {
75 enabled: true,
76 extract_types: true,
77 convert_tags: default_jsdoc_tags(),
78 }
79 }
80}
81
82fn default_jsdoc_tags() -> Vec<String> {
83 vec![
84 "param".to_string(),
85 "returns".to_string(),
86 "throws".to_string(),
87 "deprecated".to_string(),
88 "example".to_string(),
89 "see".to_string(),
90 ]
91}
92
93#[derive(Debug, Clone, Serialize, Deserialize)]
95#[serde(rename_all = "camelCase")]
96pub struct PythonConfig {
97 #[serde(default = "default_true")]
99 pub enabled: bool,
100 #[serde(default)]
102 pub docstring_style: DocstringStyle,
103 #[serde(default = "default_true")]
105 pub extract_type_hints: bool,
106 #[serde(default = "default_python_sections")]
108 pub convert_sections: Vec<String>,
109}
110
111impl Default for PythonConfig {
112 fn default() -> Self {
113 Self {
114 enabled: true,
115 docstring_style: DocstringStyle::Auto,
116 extract_type_hints: true,
117 convert_sections: default_python_sections(),
118 }
119 }
120}
121
122fn default_python_sections() -> Vec<String> {
123 vec![
124 "Args".to_string(),
125 "Parameters".to_string(),
126 "Returns".to_string(),
127 "Raises".to_string(),
128 "Example".to_string(),
129 "Yields".to_string(),
130 ]
131}
132
133#[derive(Debug, Clone, Serialize, Deserialize)]
135#[serde(rename_all = "camelCase")]
136pub struct RustConfig {
137 #[serde(default = "default_true")]
139 pub enabled: bool,
140 #[serde(default = "default_rust_sections")]
142 pub convert_sections: Vec<String>,
143}
144
145impl Default for RustConfig {
146 fn default() -> Self {
147 Self {
148 enabled: true,
149 convert_sections: default_rust_sections(),
150 }
151 }
152}
153
154fn default_rust_sections() -> Vec<String> {
155 vec![
156 "Arguments".to_string(),
157 "Returns".to_string(),
158 "Panics".to_string(),
159 "Errors".to_string(),
160 "Examples".to_string(),
161 "Safety".to_string(),
162 ]
163}
164
165#[derive(Debug, Clone, Serialize, Deserialize)]
167#[serde(rename_all = "camelCase")]
168pub struct ProvenanceConfig {
169 #[serde(default = "default_true")]
171 pub mark_converted: bool,
172 #[serde(default = "default_true")]
174 pub include_source_format: bool,
175}
176
177impl Default for ProvenanceConfig {
178 fn default() -> Self {
179 Self {
180 mark_converted: true,
181 include_source_format: true,
182 }
183 }
184}
185
186fn default_true() -> bool {
187 true
188}
189
190#[derive(Debug, Clone, Default, Serialize, Deserialize)]
192#[serde(rename_all = "camelCase")]
193pub struct BridgeConfig {
194 #[serde(default)]
196 pub enabled: bool,
197 #[serde(default)]
199 pub precedence: Precedence,
200 #[serde(default)]
202 pub strictness: Strictness,
203 #[serde(default)]
205 pub jsdoc: JsDocConfig,
206 #[serde(default)]
208 pub python: PythonConfig,
209 #[serde(default)]
211 pub rust: RustConfig,
212 #[serde(default)]
214 pub provenance: ProvenanceConfig,
215}
216
217impl BridgeConfig {
218 pub fn new() -> Self {
220 Self::default()
221 }
222
223 pub fn enabled() -> Self {
225 Self {
226 enabled: true,
227 ..Default::default()
228 }
229 }
230
231 pub fn is_enabled_for(&self, language: &str) -> bool {
233 if !self.enabled {
234 return false;
235 }
236 match language.to_lowercase().as_str() {
237 "javascript" | "typescript" | "js" | "ts" => self.jsdoc.enabled,
238 "python" | "py" => self.python.enabled,
239 "rust" | "rs" => self.rust.enabled,
240 "java" | "kotlin" => true, "go" => true, _ => false,
243 }
244 }
245}
246
247#[cfg(test)]
248mod tests {
249 use super::*;
250
251 #[test]
252 fn test_bridge_config_defaults() {
253 let config = BridgeConfig::new();
254 assert!(!config.enabled);
255 assert_eq!(config.precedence, Precedence::AcpFirst);
256 assert_eq!(config.strictness, Strictness::Permissive);
257 assert!(config.jsdoc.enabled);
258 assert!(config.python.enabled);
259 assert!(config.rust.enabled);
260 }
261
262 #[test]
263 fn test_bridge_config_enabled() {
264 let config = BridgeConfig::enabled();
265 assert!(config.enabled);
266 assert!(config.is_enabled_for("typescript"));
267 assert!(config.is_enabled_for("python"));
268 assert!(config.is_enabled_for("rust"));
269 }
270
271 #[test]
272 fn test_is_enabled_for_disabled_global() {
273 let config = BridgeConfig::new();
274 assert!(!config.is_enabled_for("typescript"));
275 assert!(!config.is_enabled_for("python"));
276 }
277
278 #[test]
279 fn test_is_enabled_for_specific_disabled() {
280 let mut config = BridgeConfig::enabled();
281 config.python.enabled = false;
282
283 assert!(config.is_enabled_for("typescript"));
284 assert!(!config.is_enabled_for("python"));
285 }
286
287 #[test]
288 fn test_precedence_display() {
289 assert_eq!(Precedence::AcpFirst.to_string(), "acp-first");
290 assert_eq!(Precedence::NativeFirst.to_string(), "native-first");
291 assert_eq!(Precedence::Merge.to_string(), "merge");
292 }
293
294 #[test]
295 fn test_config_serialization() {
296 let config = BridgeConfig::enabled();
297 let json = serde_json::to_string_pretty(&config).unwrap();
298 let parsed: BridgeConfig = serde_json::from_str(&json).unwrap();
299
300 assert!(parsed.enabled);
301 assert_eq!(parsed.precedence, Precedence::AcpFirst);
302 }
303}