Skip to main content

alef_core/config/
languages.rs

1use serde::{Deserialize, Serialize};
2use std::collections::HashMap;
3use std::path::PathBuf;
4
5use super::extras::Language;
6
7/// Configuration for a single capsule type entry in `PythonConfig::capsule_types`.
8///
9/// Supports two TOML forms via `#[serde(untagged)]`:
10///
11/// - String: `Language = "tree_sitter.Language"` → capsule round-trip via `into_raw()`
12/// - Struct: `Parser = { python_type = "tree_sitter.Parser", construct_from = "Language" }` → Python-side construction
13#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
14#[serde(untagged)]
15pub enum CapsuleTypeConfig {
16    /// Capsule round-trip: the Rust type exposes `into_raw()` returning a raw pointer.
17    /// The generated code calls `PyCapsule_New(value.into_raw(), capsule_name, None)` on return,
18    /// and `PyCapsule_GetPointer` + `from_raw()` on input.
19    ///
20    /// Value is the fully-qualified Python capsule name (e.g. `"tree_sitter.Language"`).
21    Capsule(String),
22    /// Python-side construction: the type does not have a direct `into_raw()`.
23    /// Instead, the generated code constructs the Python type by calling a Python factory
24    /// (e.g. `tree_sitter.Parser(language)`) where `language` is a bound capsule argument.
25    ConstructFrom {
26        /// The fully-qualified Python type to import and call (e.g. `"tree_sitter.Parser"`).
27        python_type: String,
28        /// The capsule-type argument name to pass to the Python constructor.
29        /// Must be one of the other capsule-type entries (e.g. `"Language"`).
30        construct_from: String,
31    },
32}
33
34impl CapsuleTypeConfig {
35    /// Returns the Python type string (dotted path) for this config entry.
36    pub fn python_type(&self) -> &str {
37        match self {
38            Self::Capsule(name) => name,
39            Self::ConstructFrom { python_type, .. } => python_type,
40        }
41    }
42
43    /// Returns the `construct_from` dependency type name, if this is a `ConstructFrom` entry.
44    pub fn construct_from(&self) -> Option<&str> {
45        match self {
46            Self::ConstructFrom { construct_from, .. } => Some(construct_from.as_str()),
47            Self::Capsule(_) => None,
48        }
49    }
50
51    /// Returns true when this entry represents a raw capsule round-trip (not Python-side construction).
52    pub fn is_capsule_roundtrip(&self) -> bool {
53        matches!(self, Self::Capsule(_))
54    }
55}
56
57#[derive(Debug, Clone, Serialize, Deserialize)]
58pub struct PythonConfig {
59    pub module_name: Option<String>,
60    pub async_runtime: Option<String>,
61    pub stubs: Option<StubsConfig>,
62    /// PyPI package name (e.g. `"html-to-markdown"`). Used as the `[project] name` in
63    /// `pyproject.toml` and to derive the `python-packages` list for maturin.
64    /// Defaults to the crate name.
65    #[serde(default)]
66    pub pip_name: Option<String>,
67    /// Per-language feature override. When set, these features are used instead of
68    /// `[crate] features` for this language's binding crate.
69    #[serde(default)]
70    pub features: Option<Vec<String>>,
71    /// Override the serde rename_all strategy for JSON field names (e.g. "camelCase", "snake_case").
72    /// When set, this takes priority over the IR type-level serde_rename_all.
73    #[serde(default)]
74    pub serde_rename_all: Option<String>,
75    /// Map of type name -> capsule config for PyCapsule pass-through.
76    /// Types listed here are emitted as PyCapsule_New / PyCapsule_GetPointer instead of
77    /// opaque `#[pyclass]` wrappers. Use `CapsuleTypeConfig::Capsule` for raw capsule
78    /// round-trips and `CapsuleTypeConfig::ConstructFrom` for Python-side construction.
79    #[serde(default)]
80    pub capsule_types: HashMap<String, CapsuleTypeConfig>,
81    /// When true, wrap blocking function bodies in py.allow_threads() to release the GIL.
82    // TODO: Wire into gen_bindings.rs to emit py.allow_threads(|| { ... }) for non-async functions.
83    #[serde(default)]
84    pub release_gil: bool,
85    /// Functions to exclude from Python binding generation.
86    #[serde(default)]
87    pub exclude_functions: Vec<String>,
88    /// Types to exclude from Python binding generation.
89    #[serde(default)]
90    pub exclude_types: Vec<String>,
91    /// Additional Cargo dependencies for this language's binding crate only.
92    #[serde(default)]
93    pub extra_dependencies: HashMap<String, toml::Value>,
94    /// Override the scaffold output directory for this language's Cargo.toml and package files.
95    #[serde(default)]
96    pub scaffold_output: Option<PathBuf>,
97    /// Per-field name remapping for this language. Key is `TypeName.field_name` (e.g.
98    /// `"LayoutDetection.class"`), value is the desired binding field name. Applied after
99    /// automatic keyword escaping, so an explicit entry takes priority.
100    #[serde(default)]
101    pub rename_fields: HashMap<String, String>,
102    /// Prefix wrapper for default tool invocations. When set, prepends this string to default
103    /// commands across all pipelines (lint, test, build, etc.).
104    /// E.g., `run_wrapper = "uv run --no-sync"` turns `ruff format packages/python` into
105    /// `uv run --no-sync ruff format packages/python`.
106    #[serde(default)]
107    pub run_wrapper: Option<String>,
108    /// Extra paths to append to default lint commands (format, check, typecheck).
109    /// Space-separated paths are appended to the command.
110    #[serde(default)]
111    pub extra_lint_paths: Vec<String>,
112    /// Additional `from <module> import <symbol>` lines to emit in the generated `__init__.py`.
113    /// Key is the relative or absolute Python module path (e.g. `"._supported_languages"`),
114    /// value is the list of symbols to import. The symbols are also added to `__all__`.
115    ///
116    /// Use this to re-export hand-written sibling modules (e.g. generated by a project's own
117    /// build script) without alef's cleanup culling them. The hand-written file must NOT contain
118    /// the substrings `"DO NOT EDIT"`, `"auto-generated by alef"`, or `"AUTO-GENERATED by alef"`
119    /// in its first 5 lines, or alef's cleanup pipeline will treat it as a stale alef artifact.
120    #[serde(default)]
121    pub extra_init_imports: std::collections::BTreeMap<String, Vec<String>>,
122}
123
124#[derive(Debug, Clone, Serialize, Deserialize)]
125pub struct StubsConfig {
126    pub output: PathBuf,
127}
128
129/// Configuration for a single capsule type entry in `NodeConfig::capsule_types`.
130///
131/// When set, the named Rust type is NOT emitted as a `#[napi]` opaque wrapper.
132/// Instead, functions returning this type produce a `JsObject` carrying the raw
133/// pointer in a configurable `Napi::External<T>` property — the layout consumed
134/// by the `tree-sitter` npm package's `Parser.setLanguage()`.
135///
136/// TOML form:
137/// ```toml
138/// [crates.node.capsule_types.Language]
139/// type = "Language"
140/// from_module = "tree-sitter"
141/// property_name = "language"
142/// type_tag = { lower = "0x8AF2E5212AD58ABF", upper = "0xD5006CAD83ABBA16" }
143/// ```
144#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
145pub struct NodeCapsuleTypeConfig {
146    /// User-facing class name in the ecosystem library (e.g. `"Language"`).
147    /// Emitted as the return-type annotation in the generated `index.d.ts`.
148    #[serde(rename = "type")]
149    pub type_name: String,
150    /// npm package to import the type from (e.g. `"tree-sitter"`).
151    /// Emitted as the `from` clause in the generated `import type` line.
152    pub from_module: String,
153    /// Codegen strategy. Currently only `"external_pointer"` is supported.
154    /// Defaults to `"external_pointer"`.
155    #[serde(default = "default_node_capsule_construct")]
156    pub construct: String,
157    /// JS property name to set on the returned object. `node-tree-sitter`
158    /// reads `value["language"]`; other consumers may use different names.
159    /// Defaults to `"__parser"` for back-compat with existing configs.
160    #[serde(default = "default_node_capsule_property_name")]
161    pub property_name: String,
162    /// Optional N-API type tag to apply via `napi_type_tag_object`. Required
163    /// when the consumer library (e.g. `node-tree-sitter`) calls
164    /// `napi_check_object_type_tag` to validate the External before using it.
165    #[serde(default)]
166    pub type_tag: Option<NapiTypeTagConfig>,
167}
168
169/// An N-API `napi_type_tag` value, expressed as two 64-bit hex strings.
170#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
171pub struct NapiTypeTagConfig {
172    /// Lower 64 bits of the tag, hex (e.g. `"0x8AF2E5212AD58ABF"`).
173    pub lower: String,
174    /// Upper 64 bits of the tag, hex (e.g. `"0xD5006CAD83ABBA16"`).
175    pub upper: String,
176}
177
178fn default_node_capsule_construct() -> String {
179    "external_pointer".to_string()
180}
181
182fn default_node_capsule_property_name() -> String {
183    "__parser".to_string()
184}
185
186#[derive(Debug, Clone, Serialize, Deserialize)]
187pub struct NodeConfig {
188    pub package_name: Option<String>,
189    /// Per-language feature override. When set, these features are used instead of
190    /// `[crate] features` for this language's binding crate.
191    #[serde(default)]
192    pub features: Option<Vec<String>>,
193    /// Override the serde rename_all strategy for JSON field names (e.g. "camelCase", "snake_case").
194    /// When set, this takes priority over the IR type-level serde_rename_all.
195    #[serde(default)]
196    pub serde_rename_all: Option<String>,
197    /// Prefix for generated type names (e.g. "Js" produces `JsConversionOptions`).
198    /// Defaults to `"Js"`.
199    #[serde(default)]
200    pub type_prefix: Option<String>,
201    /// Map of Rust type name -> capsule config for raw-pointer passthrough.
202    /// Types listed here skip the default `#[napi]` opaque-wrapper emission;
203    /// functions returning them produce a `JsObject` with a `__parser`
204    /// `Napi::External<T>` property instead. See [`NodeCapsuleTypeConfig`].
205    #[serde(default)]
206    pub capsule_types: HashMap<String, NodeCapsuleTypeConfig>,
207    /// Functions to exclude from Node binding generation.
208    #[serde(default)]
209    pub exclude_functions: Vec<String>,
210    /// Types to exclude from Node binding generation.
211    #[serde(default)]
212    pub exclude_types: Vec<String>,
213    /// Additional Cargo dependencies for this language's binding crate only.
214    #[serde(default)]
215    pub extra_dependencies: HashMap<String, toml::Value>,
216    /// Override the scaffold output directory for this language's Cargo.toml and package files.
217    #[serde(default)]
218    pub scaffold_output: Option<PathBuf>,
219    /// Per-field name remapping for this language. Key is `TypeName.field_name`, value is the
220    /// desired binding field name. Applied after automatic keyword escaping.
221    #[serde(default)]
222    pub rename_fields: HashMap<String, String>,
223    /// Prefix wrapper for default tool invocations. When set, prepends this string to default
224    /// commands across all pipelines (lint, test, build, etc.).
225    #[serde(default)]
226    pub run_wrapper: Option<String>,
227    /// Extra paths to append to default lint commands (format, check, typecheck).
228    #[serde(default)]
229    pub extra_lint_paths: Vec<String>,
230}
231
232#[derive(Debug, Clone, Serialize, Deserialize)]
233pub struct RubyConfig {
234    pub gem_name: Option<String>,
235    pub stubs: Option<StubsConfig>,
236    /// Per-language feature override. When set, these features are used instead of
237    /// `[crate] features` for this language's binding crate.
238    #[serde(default)]
239    pub features: Option<Vec<String>>,
240    /// Override the serde rename_all strategy for JSON field names (e.g. "camelCase", "snake_case").
241    /// When set, this takes priority over the IR type-level serde_rename_all.
242    #[serde(default)]
243    pub serde_rename_all: Option<String>,
244    /// Functions to exclude from Ruby binding generation.
245    #[serde(default)]
246    pub exclude_functions: Vec<String>,
247    /// Types to exclude from Ruby binding generation.
248    #[serde(default)]
249    pub exclude_types: Vec<String>,
250    /// Additional Cargo dependencies for this language's binding crate only.
251    #[serde(default)]
252    pub extra_dependencies: HashMap<String, toml::Value>,
253    /// Override the scaffold output directory for this language's Cargo.toml and package files.
254    #[serde(default)]
255    pub scaffold_output: Option<PathBuf>,
256    /// Per-field name remapping for this language. Key is `TypeName.field_name`, value is the
257    /// desired binding field name. Applied after automatic keyword escaping.
258    #[serde(default)]
259    pub rename_fields: HashMap<String, String>,
260    /// Prefix wrapper for default tool invocations. When set, prepends this string to default
261    /// commands across all pipelines (lint, test, build, etc.).
262    #[serde(default)]
263    pub run_wrapper: Option<String>,
264    /// Extra paths to append to default lint commands (format, check, typecheck).
265    #[serde(default)]
266    pub extra_lint_paths: Vec<String>,
267}
268
269#[derive(Debug, Clone, Serialize, Deserialize)]
270pub struct PhpConfig {
271    pub extension_name: Option<String>,
272    /// Cargo crate name for the PHP binding (e.g. `"ts-pack-core-php"`).
273    /// Used to derive the shared library filename in the e2e test runner.
274    /// When absent, the lib name is derived from `extension_name` by appending `_php`.
275    #[serde(default)]
276    pub cargo_crate_name: Option<String>,
277    /// Override the PHP namespace used for class registration and PSR-4 autoloading.
278    ///
279    /// When set, this value is used verbatim as the PHP namespace (e.g. `"HtmlToMarkdown"`).
280    /// When absent, the namespace is derived from `extension_name` by splitting on `_` and
281    /// converting each segment to PascalCase (e.g. `html_to_markdown` → `Html\To\Markdown`).
282    #[serde(default)]
283    pub namespace: Option<String>,
284    /// Feature gate for ext-php-rs (default: "extension-module").
285    /// All generated code is wrapped in `#[cfg(feature = "...")]`.
286    #[serde(default)]
287    pub feature_gate: Option<String>,
288    /// Output directory for generated PHP facade / stubs (e.g., `packages/php/src/`).
289    #[serde(default)]
290    pub stubs: Option<StubsConfig>,
291    #[serde(default)]
292    pub features: Option<Vec<String>>,
293    /// Override the serde rename_all strategy for JSON field names (e.g. "camelCase", "snake_case").
294    /// When set, this takes priority over the IR type-level serde_rename_all.
295    #[serde(default)]
296    pub serde_rename_all: Option<String>,
297    /// Functions to exclude from PHP binding generation.
298    #[serde(default)]
299    pub exclude_functions: Vec<String>,
300    /// Types to exclude from PHP binding generation.
301    #[serde(default)]
302    pub exclude_types: Vec<String>,
303    /// Additional Cargo dependencies for this language's binding crate only.
304    #[serde(default)]
305    pub extra_dependencies: HashMap<String, toml::Value>,
306    /// Override the scaffold output directory for this language's Cargo.toml and package files.
307    #[serde(default)]
308    pub scaffold_output: Option<PathBuf>,
309    /// Per-field name remapping for this language. Key is `TypeName.field_name`, value is the
310    /// desired binding field name. Applied after automatic keyword escaping.
311    #[serde(default)]
312    pub rename_fields: HashMap<String, String>,
313    /// Prefix wrapper for default tool invocations. When set, prepends this string to default
314    /// commands across all pipelines (lint, test, build, etc.).
315    #[serde(default)]
316    pub run_wrapper: Option<String>,
317    /// Extra paths to append to default lint commands (format, check, typecheck).
318    #[serde(default)]
319    pub extra_lint_paths: Vec<String>,
320}
321
322#[derive(Debug, Clone, Serialize, Deserialize)]
323pub struct ElixirConfig {
324    pub app_name: Option<String>,
325    #[serde(default)]
326    pub features: Option<Vec<String>>,
327    /// Override the serde rename_all strategy for JSON field names (e.g. "camelCase", "snake_case").
328    /// When set, this takes priority over the IR type-level serde_rename_all.
329    #[serde(default)]
330    pub serde_rename_all: Option<String>,
331    /// Functions to exclude from Elixir NIF generation.
332    #[serde(default)]
333    pub exclude_functions: Vec<String>,
334    /// Types to exclude from Elixir NIF generation.
335    #[serde(default)]
336    pub exclude_types: Vec<String>,
337    /// Additional Cargo dependencies for this language's binding crate only.
338    #[serde(default)]
339    pub extra_dependencies: HashMap<String, toml::Value>,
340    /// Override the scaffold output directory for this language's Cargo.toml and package files.
341    #[serde(default)]
342    pub scaffold_output: Option<PathBuf>,
343    /// Per-field name remapping for this language. Key is `TypeName.field_name`, value is the
344    /// desired binding field name. Applied after automatic keyword escaping.
345    #[serde(default)]
346    pub rename_fields: HashMap<String, String>,
347    /// Prefix wrapper for default tool invocations. When set, prepends this string to default
348    /// commands across all pipelines (lint, test, build, etc.).
349    #[serde(default)]
350    pub run_wrapper: Option<String>,
351    /// Extra paths to append to default lint commands (format, check, typecheck).
352    #[serde(default)]
353    pub extra_lint_paths: Vec<String>,
354    /// Functions that should be scheduled on the dirty CPU scheduler.
355    /// HTML parsing and other CPU-intensive NIFs should be listed here to avoid
356    /// blocking BEAM scheduler threads.
357    #[serde(default)]
358    pub cpu_bound_functions: Vec<String>,
359}
360
361#[derive(Debug, Clone, Serialize, Deserialize)]
362pub struct WasmConfig {
363    #[serde(default)]
364    pub exclude_functions: Vec<String>,
365    #[serde(default)]
366    pub exclude_types: Vec<String>,
367    #[serde(default)]
368    pub type_overrides: HashMap<String, String>,
369    #[serde(default)]
370    pub features: Option<Vec<String>>,
371    /// Override the serde rename_all strategy for JSON field names (e.g. "camelCase", "snake_case").
372    /// When set, this takes priority over the IR type-level serde_rename_all.
373    #[serde(default)]
374    pub serde_rename_all: Option<String>,
375    /// Prefix for generated type names (e.g. "Wasm" produces `WasmConversionOptions`).
376    /// Defaults to `"Wasm"`.
377    #[serde(default)]
378    pub type_prefix: Option<String>,
379    /// Functions to exclude from the public TypeScript re-export (index.ts) while still
380    /// generating the Rust binding. Use this when a custom module provides a wrapper.
381    #[serde(default)]
382    pub exclude_reexports: Vec<String>,
383    /// Wide-character C functions to shim for WASM external scanner interop.
384    #[serde(default)]
385    pub env_shims: Vec<String>,
386    /// Additional Cargo dependencies for the WASM binding crate only.
387    #[serde(default)]
388    pub extra_dependencies: HashMap<String, toml::Value>,
389    /// Per-field name remapping for this language. Key is `TypeName.field_name`, value is the
390    /// desired binding field name. Applied after automatic keyword escaping.
391    #[serde(default)]
392    pub rename_fields: HashMap<String, String>,
393    /// Prefix wrapper for default tool invocations. When set, prepends this string to default
394    /// commands across all pipelines (lint, test, build, etc.).
395    #[serde(default)]
396    pub run_wrapper: Option<String>,
397    /// Extra paths to append to default lint commands (format, check, typecheck).
398    #[serde(default)]
399    pub extra_lint_paths: Vec<String>,
400    /// Override the core Cargo dependency name and path for the WASM binding crate.
401    /// When set, the binding `Cargo.toml` depends on this crate (resolved as
402    /// `../<override>`) instead of the umbrella `[crate.name]`. Use this to point
403    /// the WASM binding at a wasm-safe sub-crate while other languages keep the
404    /// facade. Defaults to unset.
405    #[serde(default)]
406    pub core_crate_override: Option<String>,
407    /// Keys to subtract from the merged `extra_dependencies` set for this
408    /// language only. Useful when `[crate.extra_dependencies]` lists sibling
409    /// crates that the WASM target cannot link.
410    #[serde(default)]
411    pub exclude_extra_dependencies: Vec<String>,
412    /// Hand-written Rust modules to declare in the generated lib.rs with `pub mod <name>;`
413    /// and re-export with `pub use <name>::*;`. Separate from `[custom_modules].wasm` which
414    /// only adds TypeScript `export *` re-exports. Use this for Rust-side dispatch/glue modules.
415    #[serde(default)]
416    pub custom_rust_modules: Vec<String>,
417    /// Per-type field exclusions for the generated From impls and binding struct.
418    /// Key is the type name (e.g. "ServerConfig"), value is a list of field names to skip.
419    /// Use when source fields are gated behind `#[cfg(not(target_arch = "wasm32"))]` and
420    /// therefore don't exist in the wasm32 compilation environment.
421    #[serde(default)]
422    pub exclude_fields: HashMap<String, Vec<String>>,
423    /// Source crate names whose types are re-exported by the `core_crate_override`
424    /// crate. References to `<original_crate>::TypeName` in generated code are
425    /// rewritten to `<override_crate>::TypeName`. Only meaningful when
426    /// `core_crate_override` is set.
427    /// Example: with `core_crate_override = "mylib-http"`, setting
428    /// `source_crate_remaps = ["mylib-core", "mylib"]` rewrites
429    /// `mylib_core::Method` and `mylib::Method` references to
430    /// `mylib_http::Method` (assumes `mylib-http` re-exports them via
431    /// `pub use mylib_core::*`).
432    #[serde(default)]
433    pub source_crate_remaps: Vec<String>,
434}
435
436#[derive(Debug, Clone, Serialize, Deserialize)]
437pub struct FfiConfig {
438    pub prefix: Option<String>,
439    #[serde(default = "default_error_style")]
440    pub error_style: String,
441    pub header_name: Option<String>,
442    /// Native library name for Go cgo/Java Panama/C# P/Invoke (e.g., "ts_pack_ffi").
443    /// Defaults to `{prefix}_ffi`.
444    #[serde(default)]
445    pub lib_name: Option<String>,
446    /// If true, generate visitor/callback FFI support.
447    #[serde(default)]
448    pub visitor_callbacks: bool,
449    #[serde(default)]
450    pub features: Option<Vec<String>>,
451    /// Override the serde rename_all strategy for JSON field names (e.g. "camelCase", "snake_case").
452    /// When set, this takes priority over the IR type-level serde_rename_all.
453    #[serde(default)]
454    pub serde_rename_all: Option<String>,
455    /// Functions to exclude from FFI binding generation.
456    #[serde(default)]
457    pub exclude_functions: Vec<String>,
458    /// Types to exclude from FFI binding generation.
459    #[serde(default)]
460    pub exclude_types: Vec<String>,
461    /// Per-field name remapping for this language. Key is `TypeName.field_name`, value is the
462    /// desired binding field name. Applied after automatic keyword escaping.
463    #[serde(default)]
464    pub rename_fields: HashMap<String, String>,
465    /// Rust expression used to construct an error value of this crate's
466    /// `error_type` from a runtime `String` message inside generated FFI
467    /// trait-bridge plugin shims (`plugin_impl_initialize`, `plugin_impl_shutdown`).
468    ///
469    /// The expression has access to a local variable `msg: String` containing
470    /// the underlying error message and is interpolated verbatim. Example
471    /// values:
472    ///
473    /// ```toml
474    /// # downstream whose error type has a struct variant with two fields:
475    /// plugin_error_constructor = """
476    /// kreuzberg::KreuzbergError::Plugin { message: msg, plugin_name: String::new() }
477    /// """
478    ///
479    /// # downstream whose error type implements `From<String>`:
480    /// plugin_error_constructor = "MyError::from(msg)"
481    /// ```
482    ///
483    /// Defaults to `None`. When unset, the plugin shim still emits — backends
484    /// fall back to a `format!("{}: {}", prefix, msg)`-style construction via
485    /// the configured `error_constructor`. Downstreams that don't expose
486    /// trait-bridged plugins can ignore this knob entirely.
487    #[serde(default)]
488    pub plugin_error_constructor: Option<String>,
489}
490
491fn default_error_style() -> String {
492    "last_error".to_string()
493}
494
495#[derive(Debug, Clone, Serialize, Deserialize)]
496pub struct GoConfig {
497    pub module: Option<String>,
498    /// Override the Go package name (default: derived from module path)
499    pub package_name: Option<String>,
500    #[serde(default)]
501    pub features: Option<Vec<String>>,
502    /// Types to exclude from Go binding generation.
503    ///
504    /// Go bindings call the generated C FFI directly through cgo, so types excluded from
505    /// `[crates.ffi].exclude_types` are also excluded automatically by the Go backend.
506    #[serde(default)]
507    pub exclude_types: Vec<String>,
508    /// Override the serde rename_all strategy for JSON field names (e.g. "camelCase", "snake_case").
509    /// When set, this takes priority over the IR type-level serde_rename_all.
510    #[serde(default)]
511    pub serde_rename_all: Option<String>,
512    /// Per-field name remapping for this language. Key is `TypeName.field_name`, value is the
513    /// desired binding field name. Applied after automatic keyword escaping.
514    #[serde(default)]
515    pub rename_fields: HashMap<String, String>,
516    /// Prefix wrapper for default tool invocations. When set, prepends this string to default
517    /// commands across all pipelines (lint, test, build, etc.).
518    #[serde(default)]
519    pub run_wrapper: Option<String>,
520    /// Extra paths to append to default lint commands (format, check, typecheck).
521    #[serde(default)]
522    pub extra_lint_paths: Vec<String>,
523}
524
525#[derive(Debug, Clone, Serialize, Deserialize)]
526pub struct JavaConfig {
527    pub package: Option<String>,
528    /// Override the Maven `<groupId>` emitted by alef-scaffold and alef-e2e. When unset,
529    /// `java_group_id()` falls back to the Java `package` value. Set this when the
530    /// published Maven coords differ from the Java package path (e.g. group
531    /// `dev.kreuzberg`, package `dev.kreuzberg.htmltomarkdown`).
532    #[serde(default)]
533    pub group_id: Option<String>,
534    /// Override the Maven `<artifactId>` emitted by alef-scaffold and alef-e2e. When
535    /// unset, defaults to the crate name (the `[[crates]] name = "..."`). Set this when
536    /// the published artifactId differs from the source crate name (e.g. crate
537    /// `html-to-markdown-rs` published as `html-to-markdown`).
538    #[serde(default)]
539    pub artifact_id: Option<String>,
540    #[serde(default = "default_java_ffi_style")]
541    pub ffi_style: String,
542    #[serde(default)]
543    pub features: Option<Vec<String>>,
544    /// Types to exclude from Java binding generation.
545    ///
546    /// Java's Panama bindings call the generated C FFI directly, so types excluded from
547    /// `[crates.ffi].exclude_types` are also excluded automatically by the Java backend.
548    #[serde(default)]
549    pub exclude_types: Vec<String>,
550    /// Override the serde rename_all strategy for JSON field names (e.g. "camelCase", "snake_case").
551    /// When set, this takes priority over the IR type-level serde_rename_all.
552    #[serde(default)]
553    pub serde_rename_all: Option<String>,
554    /// Per-field name remapping for this language. Key is `TypeName.field_name`, value is the
555    /// desired binding field name. Applied after automatic keyword escaping.
556    #[serde(default)]
557    pub rename_fields: HashMap<String, String>,
558    /// Prefix wrapper for default tool invocations. When set, prepends this string to default
559    /// commands across all pipelines (lint, test, build, etc.).
560    #[serde(default)]
561    pub run_wrapper: Option<String>,
562    /// Extra paths to append to default lint commands (format, check, typecheck).
563    /// Ignored when project_file is set.
564    #[serde(default)]
565    pub extra_lint_paths: Vec<String>,
566    /// Project file for Maven/Gradle (e.g., "pom.xml", "build.gradle"). When set, default
567    /// lint/build/test commands target this file instead of the output directory.
568    #[serde(default)]
569    pub project_file: Option<String>,
570}
571
572fn default_java_ffi_style() -> String {
573    "panama".to_string()
574}
575
576/// FFI strategy for Kotlin JVM / Android binding emission.
577///
578/// - `"panama"` (default): consumes the Java/Panama FFM facade emitted by
579///   `alef-backend-java`. Requires JDK 22+ at runtime. Not supported on
580///   Android Runtime.
581/// - `"jni"`: emits a `object <Module>Bridge { external fun native<...>(...) }`
582///   object with JNI declarations and a `DefaultClient` class holding a `Long`
583///   handle. Compatible with Android Runtime (JDK 11). Consumers must ship a
584///   `<crate>-jni` Rust crate exporting matching `Java_*` JNI symbols.
585#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, PartialEq, Eq)]
586#[serde(rename_all = "lowercase")]
587pub enum KotlinFfiStyle {
588    #[default]
589    Panama,
590    Jni,
591}
592
593/// Target platform for Kotlin code generation.
594///
595/// - `"jvm"` (default): emits source consuming the Java/Panama FFM facade.
596/// - `"native"`: emits Kotlin/Native source consuming the cbindgen C FFI library.
597/// - `"multiplatform"`: emits Kotlin Multiplatform project scaffolding.
598#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, PartialEq, Eq)]
599#[serde(rename_all = "lowercase")]
600pub enum KotlinTarget {
601    #[default]
602    Jvm,
603    Native,
604    // Multiplatform — Phase 3 KMP stage; placeholder so the enum is forward-compatible.
605    Multiplatform,
606}
607
608#[derive(Debug, Clone, Default, Serialize, Deserialize)]
609pub struct KotlinConfig {
610    pub package: Option<String>,
611    #[serde(default)]
612    pub features: Option<Vec<String>>,
613    /// Override the serde rename_all strategy for JSON field names (e.g. "camelCase", "snake_case").
614    /// When set, this takes priority over the IR type-level serde_rename_all.
615    #[serde(default)]
616    pub serde_rename_all: Option<String>,
617    /// Per-field name remapping for this language. Key is `TypeName.field_name`, value is the
618    /// desired binding field name. Applied after automatic keyword escaping.
619    #[serde(default)]
620    pub rename_fields: HashMap<String, String>,
621    /// Functions to exclude from Kotlin binding generation.
622    #[serde(default)]
623    pub exclude_functions: Vec<String>,
624    /// Types to exclude from Kotlin binding generation.
625    #[serde(default)]
626    pub exclude_types: Vec<String>,
627    /// Prefix wrapper for default tool invocations. When set, prepends this string to default
628    /// commands across all pipelines (lint, test, build, etc.).
629    #[serde(default)]
630    pub run_wrapper: Option<String>,
631    /// Extra paths to append to default lint commands (format, check, typecheck).
632    #[serde(default)]
633    pub extra_lint_paths: Vec<String>,
634    /// Target platform for Kotlin output. `"jvm"` (default) emits source consuming
635    /// the Java/Panama FFM facade; `"native"` emits Kotlin/Native source consuming
636    /// the cbindgen C FFI library. `"multiplatform"` emits KMP scaffolding.
637    #[serde(default)]
638    pub target: KotlinTarget,
639    /// Emission mode controlling which Kotlin project layout is generated.
640    ///
641    /// Accepted values:
642    /// - `"jvm"` (default) — standard JVM-only project under `packages/kotlin/`
643    /// - `"kmp"` — Kotlin Multiplatform project under `packages/kotlin-mpp/`
644    /// - `"android"` — Android library project under `packages/kotlin-android/`
645    ///
646    /// When `None`, defaults to `"jvm"`.
647    #[serde(default)]
648    pub mode: Option<String>,
649    /// FFI strategy. `"panama"` (default) consumes the Java/Panama FFM facade.
650    /// `"jni"` emits a Kotlin Bridge object with `external fun` declarations
651    /// and a `DefaultClient` class holding a `Long` handle. Android backend
652    /// forces `"jni"` regardless of this setting.
653    #[serde(default)]
654    pub ffi_style: KotlinFfiStyle,
655}
656
657/// Configuration for the dedicated Kotlin/Android backend (`alef-backend-kotlin-android`).
658///
659/// Distinct from [`KotlinConfig`] (Kotlin/JVM). When a crate targets the
660/// `kotlin_android` language slug, this struct controls the emitted
661/// `build.gradle.kts`, `AndroidManifest.xml`, namespace, Maven publish
662/// coordinates, ABI list, and the bundled Java facade emitted into
663/// `src/main/java/` so the AAR is self-contained.
664#[derive(Debug, Clone, Default, Serialize, Deserialize)]
665pub struct KotlinAndroidConfig {
666    /// JVM-style package for Kotlin bindings (e.g. `dev.kreuzberg`).
667    /// Defaults to the crate name.
668    #[serde(default)]
669    pub package: Option<String>,
670    /// Android library manifest `namespace`. Defaults to `package`.
671    #[serde(default)]
672    pub namespace: Option<String>,
673    /// Maven `artifactId` for the generated AAR. Defaults to `{crate}-android`.
674    #[serde(default)]
675    pub artifact_id: Option<String>,
676    /// Maven `groupId` for the generated AAR. No default — when unset the
677    /// emitter falls back to `package`.
678    #[serde(default)]
679    pub group_id: Option<String>,
680    /// Android compile SDK level. Defaults to `template_versions::toolchain::ANDROID_COMPILE_SDK`.
681    #[serde(default)]
682    pub compile_sdk: Option<u32>,
683    /// Android min SDK level. Defaults to `template_versions::toolchain::ANDROID_MIN_SDK`.
684    #[serde(default)]
685    pub min_sdk: Option<u32>,
686    /// JVM bytecode target for Kotlin and Java compilation
687    /// (e.g. `"17"`). Defaults to `template_versions::toolchain::ANDROID_JVM_TARGET`.
688    #[serde(default)]
689    pub jvm_target: Option<String>,
690    /// ABIs to scaffold under `src/main/jniLibs/<abi>/`. Defaults to
691    /// `["arm64-v8a", "x86_64"]`.
692    #[serde(default)]
693    pub abis: Option<Vec<String>>,
694    /// Override the serde rename_all strategy for JSON field names.
695    #[serde(default)]
696    pub serde_rename_all: Option<String>,
697    /// Per-field name remapping for this language. Key is `TypeName.field_name`.
698    #[serde(default)]
699    pub rename_fields: HashMap<String, String>,
700    /// Functions to exclude from generation.
701    #[serde(default)]
702    pub exclude_functions: Vec<String>,
703    /// Types to exclude from generation.
704    #[serde(default)]
705    pub exclude_types: Vec<String>,
706    /// Prefix wrapper for default tool invocations.
707    #[serde(default)]
708    pub run_wrapper: Option<String>,
709    /// Extra paths to append to default lint commands.
710    #[serde(default)]
711    pub extra_lint_paths: Vec<String>,
712    /// Per-language feature override. When set, these features are used instead of
713    /// `[crate] features` for this language's binding crate.
714    #[serde(default)]
715    pub features: Option<Vec<String>>,
716}
717
718/// Configuration for the JNI Rust shim crate emitter (`alef-backend-jni`).
719///
720/// No crate-specific fields are required — all identifiers are derived from
721/// the paired `[crates.kotlin_android]` section (package, features, etc.).
722/// This config struct exists so the crate can target `jni` in `languages`
723/// without needing to configure anything extra.
724#[derive(Debug, Clone, Default, Serialize, Deserialize)]
725pub struct JniConfig {}
726
727/// Dart bridging style: FRB (default) or raw `dart:ffi`.
728#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, PartialEq, Eq)]
729#[serde(rename_all = "lowercase")]
730pub enum DartStyle {
731    /// flutter_rust_bridge — emits a Rust crate plus Dart wrappers using
732    /// FRB-generated bridge symbols. Default.
733    #[default]
734    Frb,
735    /// Raw `dart:ffi` over the cbindgen C ABI — emits Dart-only source that
736    /// loads the shared library at runtime. Cheaper to ship; loses FRB's
737    /// async ergonomics and freezed-style data classes.
738    Ffi,
739}
740
741#[derive(Debug, Clone, Serialize, Deserialize)]
742pub struct GleamConfig {
743    pub app_name: Option<String>,
744    /// Erlang atom name for @external(erlang, "<nif>", ...) lookups (e.g., "my_app_nif").
745    /// Defaults to the app_name.
746    #[serde(default)]
747    pub nif_module: Option<String>,
748    #[serde(default)]
749    pub features: Option<Vec<String>>,
750    /// Override the serde rename_all strategy for JSON field names (e.g. "camelCase", "snake_case").
751    /// When set, this takes priority over the IR type-level serde_rename_all.
752    #[serde(default)]
753    pub serde_rename_all: Option<String>,
754    /// Per-field name remapping for this language. Key is `TypeName.field_name`, value is the
755    /// desired binding field name. Applied after automatic keyword escaping.
756    #[serde(default)]
757    pub rename_fields: HashMap<String, String>,
758    /// Functions to exclude from Gleam binding generation.
759    #[serde(default)]
760    pub exclude_functions: Vec<String>,
761    /// Types to exclude from Gleam binding generation.
762    #[serde(default)]
763    pub exclude_types: Vec<String>,
764    /// Prefix wrapper for default tool invocations.
765    #[serde(default)]
766    pub run_wrapper: Option<String>,
767    /// Extra paths to append to default lint commands.
768    #[serde(default)]
769    pub extra_lint_paths: Vec<String>,
770    /// Per-`element_type` Gleam record-constructor recipes used by the e2e
771    /// generator when emitting `json_object` arg literals. Each entry maps a
772    /// fixture-side `element_type` string (e.g. `"BatchFileItem"`) to a
773    /// structured constructor description that the codegen interpolates per
774    /// JSON-array item. Without an entry the codegen falls back to the
775    /// `json_object_wrapper` (or a plain `json_to_gleam`).
776    ///
777    /// Example:
778    ///
779    /// ```toml
780    /// [[crates.gleam.element_constructors]]
781    /// element_type = "BatchFileItem"
782    /// constructor = "kreuzberg.BatchFileItem"
783    /// [[crates.gleam.element_constructors.fields]]
784    /// gleam_field = "path"
785    /// kind = "file_path"
786    /// json_field = "path"
787    /// [[crates.gleam.element_constructors.fields]]
788    /// gleam_field = "config"
789    /// kind = "literal"
790    /// value = "option.None"
791    /// ```
792    #[serde(default)]
793    pub element_constructors: Vec<GleamElementConstructor>,
794    /// Optional Gleam expression template used to wrap `json_object` arg
795    /// values when no `element_type` recipe matches. The placeholder
796    /// `{json}` is replaced with a Gleam string literal containing the JSON
797    /// form of the arg value, allowing the downstream's Gleam binding to do
798    /// its own parsing.
799    ///
800    /// Example:
801    ///
802    /// ```toml
803    /// [crates.gleam]
804    /// json_object_wrapper = "kreuzberg.config_from_json_string({json})"
805    /// ```
806    ///
807    /// When `None`, the codegen emits `{json}` verbatim (a plain Gleam
808    /// string), matching the iter15 default.
809    #[serde(default)]
810    pub json_object_wrapper: Option<String>,
811}
812
813/// One per-`element_type` Gleam record-constructor recipe. Keyed by the
814/// fixture-side `element_type` string and consumed by the e2e Gleam codegen
815/// when building `json_object` arg literals.
816#[derive(Debug, Clone, Serialize, Deserialize)]
817pub struct GleamElementConstructor {
818    /// Fixture-side `element_type` value this recipe applies to (e.g.
819    /// `"BatchFileItem"`).
820    pub element_type: String,
821    /// Fully-qualified Gleam constructor identifier (e.g.
822    /// `"kreuzberg.BatchFileItem"`). Emitted verbatim before the `(...)` field
823    /// list.
824    pub constructor: String,
825    /// Ordered list of fields to emit inside the constructor's `(...)` block,
826    /// in argument-position order. Each field describes how its value is
827    /// derived from the per-item JSON object.
828    pub fields: Vec<GleamElementField>,
829}
830
831/// One field inside a [`GleamElementConstructor`]'s argument list.
832///
833/// `kind` selects the source/encoding strategy:
834/// * `"file_path"` — read `json_field` from the JSON object as a string,
835///   prefix with the configured `test_documents_dir` when the value does not
836///   start with `/`, and emit as a Gleam string literal.
837/// * `"byte_array"` — read `json_field` from the JSON object as a JSON
838///   `Array(Number)` and emit as a Gleam BitArray literal `<<n1, n2, …>>`.
839/// * `"string"` — read `json_field` as a string, emit as a Gleam string
840///   literal; falls back to `default` (or empty) if missing.
841/// * `"literal"` — emit `value` verbatim (no JSON lookup). Use for
842///   constant fields like `config: option.None`.
843#[derive(Debug, Clone, Serialize, Deserialize)]
844pub struct GleamElementField {
845    /// Gleam record field name (e.g. `"path"`, `"config"`).
846    pub gleam_field: String,
847    /// Source/encoding strategy. See struct doc.
848    pub kind: String,
849    /// JSON object key to read, when `kind` is one of the JSON-driven
850    /// strategies. Required for `"file_path"`, `"byte_array"`, `"string"`;
851    /// ignored for `"literal"`.
852    #[serde(default)]
853    pub json_field: Option<String>,
854    /// Default Gleam expression when `json_field` is missing/null. Only
855    /// honoured by the `"string"` strategy today.
856    #[serde(default)]
857    pub default: Option<String>,
858    /// Verbatim Gleam expression to emit when `kind = "literal"`.
859    #[serde(default)]
860    pub value: Option<String>,
861}
862
863#[derive(Debug, Clone, Default, Serialize, Deserialize)]
864pub struct DartConfig {
865    /// Dart pub.dev package name (e.g. `"my_package"`). Used as the `name` in
866    /// `pubspec.yaml`. Defaults to a snake_case derivation of the crate name.
867    #[serde(default)]
868    pub pubspec_name: Option<String>,
869    /// Dart library name (the `library` declaration). Defaults to the pubspec name.
870    #[serde(default)]
871    pub lib_name: Option<String>,
872    /// Dart package name override (e.g. for pub.dev scoped packages).
873    #[serde(default)]
874    pub package_name: Option<String>,
875    /// Bridging style. `"frb"` (default) uses flutter_rust_bridge; `"ffi"` emits
876    /// raw `dart:ffi` source over the cbindgen C library.
877    #[serde(default)]
878    pub style: DartStyle,
879    /// flutter_rust_bridge version to pin in generated pubspec.yaml.
880    /// Defaults to `template_versions::cargo::FLUTTER_RUST_BRIDGE` when unset.
881    #[serde(default)]
882    pub frb_version: Option<String>,
883    /// Cargo features to enable on the binding crate.
884    #[serde(default)]
885    pub features: Option<Vec<String>>,
886    /// Additional Cargo dependencies for the generated Dart Rust bridge crate.
887    #[serde(default)]
888    pub extra_dependencies: HashMap<String, toml::Value>,
889    /// Override the serde rename_all strategy for JSON field names (e.g. "camelCase", "snake_case").
890    #[serde(default)]
891    pub serde_rename_all: Option<String>,
892    /// Per-field name remapping. Key is `TypeName.field_name`, value is the
893    /// desired binding field name. Applied after automatic keyword escaping.
894    #[serde(default)]
895    pub rename_fields: HashMap<String, String>,
896    /// Functions to exclude from Dart binding generation.
897    #[serde(default)]
898    pub exclude_functions: Vec<String>,
899    /// Types to exclude from Dart binding generation.
900    #[serde(default)]
901    pub exclude_types: Vec<String>,
902    /// Prefix wrapper for default tool invocations.
903    #[serde(default)]
904    pub run_wrapper: Option<String>,
905    /// Extra paths to append to default lint commands.
906    #[serde(default)]
907    pub extra_lint_paths: Vec<String>,
908    /// Override the core Cargo dependency name and path for the Dart binding crate.
909    /// When set, the binding `Cargo.toml` depends on this crate (resolved as
910    /// `../../../crates/<override>`) instead of the umbrella `[crate.name]`.
911    /// Defaults to unset.
912    #[serde(default)]
913    pub core_crate_override: Option<String>,
914    /// Keys to subtract from the merged `extra_dependencies` set for this
915    /// language only.
916    #[serde(default)]
917    pub exclude_extra_dependencies: Vec<String>,
918    /// Method names whose Rust bridge body should be emitted as `unimplemented!()`.
919    ///
920    /// Use this when a function's FFI signature (e.g. nested tuples containing
921    /// `Vec<u8>`) cannot be represented across the FRB bridge at all. Consumers must
922    /// list the method names explicitly — this field has no built-in defaults so the
923    /// knob is library-agnostic.
924    ///
925    /// Example (`alef.toml`):
926    /// ```toml
927    /// [crates.dart]
928    /// stub_methods = ["batch_extract_bytes", "batch_extract_bytes_sync"]
929    /// ```
930    #[serde(default)]
931    pub stub_methods: Vec<String>,
932    /// Per-target Cargo dependency overrides for the binding crate.
933    ///
934    /// When set, the emitted `Cargo.toml` wraps the base core dependency in a
935    /// `[target.'cfg(not(<cfg>))'.dependencies]` section and adds a matching
936    /// `[target.'cfg(<cfg>)'.dependencies]` block using `override_features`
937    /// (and `default_features = false` when `override_default_features = false`).
938    /// Required when the binding has to swap the feature set on a specific
939    /// target triple, e.g. Android x86_64 dropping ORT-dependent features.
940    ///
941    /// Example (`alef.toml`):
942    /// ```toml
943    /// [[crates.dart.target_dep_overrides]]
944    /// cfg = "all(target_os = \"android\", target_arch = \"x86_64\")"
945    /// features = ["android-target"]
946    /// default_features = false
947    /// ```
948    #[serde(default)]
949    pub target_dep_overrides: Vec<DartTargetDepOverride>,
950}
951
952#[derive(Debug, Clone, Serialize, Deserialize)]
953pub struct DartTargetDepOverride {
954    /// Cargo `cfg(...)` predicate (without the `cfg(...)` wrapper). Example:
955    /// `all(target_os = "android", target_arch = "x86_64")`.
956    pub cfg: String,
957    /// Features to enable on the core dependency for this target.
958    #[serde(default)]
959    pub features: Vec<String>,
960    /// When false (default), emit `default-features = false` for this target.
961    /// When true, allow the core dep's default features through.
962    #[serde(default)]
963    pub default_features: bool,
964}
965
966#[derive(Debug, Clone, Default, Serialize, Deserialize)]
967pub struct SwiftConfig {
968    /// Swift module name (e.g. `"MyLibrary"`). Defaults to PascalCase of the crate name.
969    #[serde(default)]
970    pub module_name: Option<String>,
971    /// Swift package name. Defaults to the module name.
972    #[serde(default)]
973    pub package_name: Option<String>,
974    /// swift-bridge version. Defaults to `template_versions::cargo::SWIFT_BRIDGE` when unset.
975    #[serde(default)]
976    pub swift_bridge_version: Option<String>,
977    /// Minimum macOS deployment target. Defaults to `template_versions::toolchain::SWIFT_MIN_MACOS` when unset.
978    #[serde(default)]
979    pub min_macos_version: Option<String>,
980    /// Minimum iOS deployment target. Defaults to `template_versions::toolchain::SWIFT_MIN_IOS` when unset.
981    #[serde(default)]
982    pub min_ios_version: Option<String>,
983    /// Cargo features to enable on the binding crate.
984    #[serde(default)]
985    pub features: Option<Vec<String>>,
986    /// Override the serde rename_all strategy for JSON field names (e.g. "camelCase", "snake_case").
987    #[serde(default)]
988    pub serde_rename_all: Option<String>,
989    /// Per-field name remapping. Key is `TypeName.field_name`, value is the
990    /// desired binding field name. Applied after automatic keyword escaping.
991    #[serde(default)]
992    pub rename_fields: HashMap<String, String>,
993    /// Functions to exclude from Swift binding generation.
994    #[serde(default)]
995    pub exclude_functions: Vec<String>,
996    /// Types to exclude from Swift binding generation.
997    #[serde(default)]
998    pub exclude_types: Vec<String>,
999    /// Fields to exclude from Swift binding generation.
1000    /// Format: `"TypeName.field_name"`.
1001    #[serde(default)]
1002    pub exclude_fields: Vec<String>,
1003    /// Prefix wrapper for default tool invocations.
1004    #[serde(default)]
1005    pub run_wrapper: Option<String>,
1006    /// Extra paths to append to default lint commands.
1007    #[serde(default)]
1008    pub extra_lint_paths: Vec<String>,
1009    /// Override the core Cargo dependency name and path for the Swift binding crate.
1010    /// When set, the binding `Cargo.toml` depends on this crate (resolved as
1011    /// `../../../crates/<override>`) instead of the umbrella `[crate.name]`.
1012    /// Defaults to unset.
1013    #[serde(default)]
1014    pub core_crate_override: Option<String>,
1015    /// Keys to subtract from the merged `extra_dependencies` set for this
1016    /// language only.
1017    #[serde(default)]
1018    pub exclude_extra_dependencies: Vec<String>,
1019    /// Override the auto-generated `create_<type>(api_key, base_url)` constructor
1020    /// body for opaque client types that expose methods. When set, the swift backend
1021    /// emits this snippet verbatim as the function body (no implicit `Ok(...)`).
1022    ///
1023    /// Use this when the source crate's constructor signature differs from the
1024    /// default `Type::new(api_key, base_url)` shape — e.g. liter-llm uses
1025    /// `DefaultClient::new(ClientConfig, Option<&str>)` and needs to build a
1026    /// `ClientConfig` from the bridge inputs first.
1027    ///
1028    /// The snippet is parameterised by `{type_name}` (the wrapper newtype name)
1029    /// and runs in a function body with `api_key: String` and `base_url: Option<String>`
1030    /// already in scope. It must return `Result<{type_name}, String>`.
1031    #[serde(default)]
1032    pub client_constructor_body: HashMap<String, String>,
1033}
1034
1035#[derive(Debug, Clone, Serialize, Deserialize)]
1036pub struct ZigConfig {
1037    pub module_name: Option<String>,
1038    #[serde(default)]
1039    pub features: Option<Vec<String>>,
1040    /// Override the serde rename_all strategy for JSON field names (e.g. "camelCase", "snake_case").
1041    /// When set, this takes priority over the IR type-level serde_rename_all.
1042    #[serde(default)]
1043    pub serde_rename_all: Option<String>,
1044    /// Per-field name remapping for this language. Key is `TypeName.field_name`, value is the
1045    /// desired binding field name. Applied after automatic keyword escaping.
1046    #[serde(default)]
1047    pub rename_fields: HashMap<String, String>,
1048    /// Functions to exclude from Zig binding generation.
1049    #[serde(default)]
1050    pub exclude_functions: Vec<String>,
1051    /// Types to exclude from Zig binding generation.
1052    #[serde(default)]
1053    pub exclude_types: Vec<String>,
1054    /// Prefix wrapper for default tool invocations.
1055    #[serde(default)]
1056    pub run_wrapper: Option<String>,
1057    /// Extra paths to append to default lint commands.
1058    #[serde(default)]
1059    pub extra_lint_paths: Vec<String>,
1060}
1061
1062#[derive(Debug, Clone, Serialize, Deserialize)]
1063pub struct CSharpConfig {
1064    pub namespace: Option<String>,
1065    /// NuGet `<PackageId>` to publish under. When unset, falls back to `namespace`.
1066    /// Use this when the published artifact id must differ from the C# `RootNamespace` —
1067    /// e.g. when the unprefixed name is owned by a third party on nuget.org and
1068    /// you publish under a vendor-prefixed id like `KreuzbergDev.<Lib>`.
1069    #[serde(default)]
1070    pub package_id: Option<String>,
1071    pub target_framework: Option<String>,
1072    #[serde(default)]
1073    pub features: Option<Vec<String>>,
1074    /// Override the serde rename_all strategy for JSON field names (e.g. "camelCase", "snake_case").
1075    /// When set, this takes priority over the IR type-level serde_rename_all.
1076    #[serde(default)]
1077    pub serde_rename_all: Option<String>,
1078    /// Per-field name remapping for this language. Key is `TypeName.field_name`, value is the
1079    /// desired binding field name. Applied after automatic keyword escaping.
1080    #[serde(default)]
1081    pub rename_fields: HashMap<String, String>,
1082    /// Prefix wrapper for default tool invocations. When set, prepends this string to default
1083    /// commands across all pipelines (lint, test, build, etc.).
1084    #[serde(default)]
1085    pub run_wrapper: Option<String>,
1086    /// Extra paths to append to default lint commands (format, check, typecheck).
1087    /// Ignored when project_file is set.
1088    #[serde(default)]
1089    pub extra_lint_paths: Vec<String>,
1090    /// Project file for C# (e.g., "MyProject.csproj", "MySolution.sln"). When set, default
1091    /// lint/build/test commands target this file instead of the output directory.
1092    #[serde(default)]
1093    pub project_file: Option<String>,
1094    /// Types to exclude from C# binding generation.
1095    ///
1096    /// C# bindings call the generated C FFI through P/Invoke, so types excluded from
1097    /// `[crates.ffi].exclude_types` are also excluded automatically by the C# backend.
1098    #[serde(default)]
1099    pub exclude_types: Vec<String>,
1100    /// Functions to exclude from C# binding generation (e.g., functions not present in the
1101    /// C FFI layer). Excluded functions are omitted from both NativeMethods.cs and the
1102    /// wrapper class.
1103    #[serde(default)]
1104    pub exclude_functions: Vec<String>,
1105}
1106
1107#[derive(Debug, Clone, Serialize, Deserialize)]
1108pub struct RConfig {
1109    pub package_name: Option<String>,
1110    #[serde(default)]
1111    pub features: Option<Vec<String>>,
1112    /// Override the serde rename_all strategy for JSON field names (e.g. "camelCase", "snake_case").
1113    /// When set, this takes priority over the IR type-level serde_rename_all.
1114    #[serde(default)]
1115    pub serde_rename_all: Option<String>,
1116    /// Per-field name remapping for this language. Key is `TypeName.field_name`, value is the
1117    /// desired binding field name. Applied after automatic keyword escaping.
1118    #[serde(default)]
1119    pub rename_fields: HashMap<String, String>,
1120    /// Prefix wrapper for default tool invocations. When set, prepends this string to default
1121    /// commands across all pipelines (lint, test, build, etc.).
1122    #[serde(default)]
1123    pub run_wrapper: Option<String>,
1124    /// Extra paths to append to default lint commands (format, check, typecheck).
1125    #[serde(default)]
1126    pub extra_lint_paths: Vec<String>,
1127}
1128
1129/// Custom modules that alef should declare (mod X;) but not generate.
1130/// These are hand-written modules imported by the generated lib.rs.
1131#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1132pub struct CustomModulesConfig {
1133    #[serde(default)]
1134    pub python: Vec<String>,
1135    #[serde(default)]
1136    pub node: Vec<String>,
1137    #[serde(default)]
1138    pub ruby: Vec<String>,
1139    #[serde(default)]
1140    pub php: Vec<String>,
1141    #[serde(default)]
1142    pub elixir: Vec<String>,
1143    #[serde(default)]
1144    pub wasm: Vec<String>,
1145    #[serde(default)]
1146    pub ffi: Vec<String>,
1147    #[serde(default)]
1148    pub go: Vec<String>,
1149    #[serde(default)]
1150    pub java: Vec<String>,
1151    #[serde(default)]
1152    pub csharp: Vec<String>,
1153    #[serde(default)]
1154    pub r: Vec<String>,
1155}
1156
1157impl CustomModulesConfig {
1158    pub fn for_language(&self, lang: Language) -> &[String] {
1159        match lang {
1160            Language::Python => &self.python,
1161            Language::Node => &self.node,
1162            Language::Ruby => &self.ruby,
1163            Language::Php => &self.php,
1164            Language::Elixir => &self.elixir,
1165            Language::Wasm => &self.wasm,
1166            Language::Ffi => &self.ffi,
1167            Language::Go => &self.go,
1168            Language::Java => &self.java,
1169            Language::Csharp => &self.csharp,
1170            Language::R => &self.r,
1171            Language::Rust => &[], // Rust doesn't need custom modules (no binding crate)
1172            Language::Kotlin
1173            | Language::KotlinAndroid
1174            | Language::Swift
1175            | Language::Dart
1176            | Language::Gleam
1177            | Language::Zig
1178            | Language::Jni
1179            | Language::C => &[],
1180        }
1181    }
1182}
1183
1184/// Custom classes/functions from hand-written modules to register in module init.
1185#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1186pub struct CustomRegistration {
1187    #[serde(default)]
1188    pub classes: Vec<String>,
1189    #[serde(default)]
1190    pub functions: Vec<String>,
1191    #[serde(default)]
1192    pub init_calls: Vec<String>,
1193}
1194
1195/// Per-language custom registrations.
1196#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1197pub struct CustomRegistrationsConfig {
1198    #[serde(default)]
1199    pub python: Option<CustomRegistration>,
1200    #[serde(default)]
1201    pub node: Option<CustomRegistration>,
1202    #[serde(default)]
1203    pub ruby: Option<CustomRegistration>,
1204    #[serde(default)]
1205    pub php: Option<CustomRegistration>,
1206    #[serde(default)]
1207    pub elixir: Option<CustomRegistration>,
1208    #[serde(default)]
1209    pub wasm: Option<CustomRegistration>,
1210}
1211
1212impl CustomRegistrationsConfig {
1213    pub fn for_language(&self, lang: Language) -> Option<&CustomRegistration> {
1214        match lang {
1215            Language::Python => self.python.as_ref(),
1216            Language::Node => self.node.as_ref(),
1217            Language::Ruby => self.ruby.as_ref(),
1218            Language::Php => self.php.as_ref(),
1219            Language::Elixir => self.elixir.as_ref(),
1220            Language::Wasm => self.wasm.as_ref(),
1221            _ => None,
1222        }
1223    }
1224}