alef_core/config/e2e.rs
1//! E2E test generation configuration types.
2
3use serde::{Deserialize, Serialize};
4use std::collections::{HashMap, HashSet};
5
6/// Root e2e configuration from `[e2e]` section of alef.toml.
7#[derive(Debug, Clone, Serialize, Deserialize, Default)]
8pub struct E2eConfig {
9 /// Directory containing fixture JSON files (default: "fixtures").
10 #[serde(default = "default_fixtures_dir")]
11 pub fixtures: String,
12 /// Output directory for generated e2e test projects (default: "e2e").
13 #[serde(default = "default_output_dir")]
14 pub output: String,
15 /// Languages to generate e2e tests for. Defaults to top-level `languages` list.
16 #[serde(default)]
17 pub languages: Vec<String>,
18 /// Function call configuration.
19 pub call: CallConfig,
20 /// Per-language package reference overrides.
21 #[serde(default)]
22 pub packages: HashMap<String, PackageRef>,
23 /// Per-language formatter commands.
24 #[serde(default)]
25 pub format: HashMap<String, String>,
26 /// Field path aliases: maps fixture field paths to actual API struct paths.
27 /// E.g., "metadata.title" -> "metadata.document.title"
28 /// Supports struct access (foo.bar), map access (foo[key]), direct fields.
29 #[serde(default)]
30 pub fields: HashMap<String, String>,
31 /// Fields that are Optional/nullable in the return type.
32 /// Rust generators use .as_deref().unwrap_or("") for strings, .is_some() for structs.
33 #[serde(default)]
34 pub fields_optional: HashSet<String>,
35}
36
37fn default_fixtures_dir() -> String {
38 "fixtures".to_string()
39}
40
41fn default_output_dir() -> String {
42 "e2e".to_string()
43}
44
45/// Configuration for the function call in each test.
46#[derive(Debug, Clone, Serialize, Deserialize, Default)]
47pub struct CallConfig {
48 /// The function name (alef applies language naming conventions).
49 #[serde(default)]
50 pub function: String,
51 /// The module/package where the function lives.
52 #[serde(default)]
53 pub module: String,
54 /// Variable name for the return value (default: "result").
55 #[serde(default = "default_result_var")]
56 pub result_var: String,
57 /// Whether the function is async.
58 #[serde(default)]
59 pub r#async: bool,
60 /// How fixture `input` fields map to function arguments.
61 #[serde(default)]
62 pub args: Vec<ArgMapping>,
63 /// Per-language overrides for module/function/etc.
64 #[serde(default)]
65 pub overrides: HashMap<String, CallOverride>,
66}
67
68fn default_result_var() -> String {
69 "result".to_string()
70}
71
72/// Maps a fixture input field to a function argument.
73#[derive(Debug, Clone, Serialize, Deserialize)]
74pub struct ArgMapping {
75 /// Argument name in the function signature.
76 pub name: String,
77 /// JSON field path in the fixture's `input` object.
78 pub field: String,
79 /// Type hint for code generation.
80 #[serde(rename = "type", default = "default_arg_type")]
81 pub arg_type: String,
82 /// Whether this argument is optional.
83 #[serde(default)]
84 pub optional: bool,
85}
86
87fn default_arg_type() -> String {
88 "string".to_string()
89}
90
91/// Per-language override for function call configuration.
92#[derive(Debug, Clone, Serialize, Deserialize, Default)]
93pub struct CallOverride {
94 /// Override the module/import path.
95 #[serde(default)]
96 pub module: Option<String>,
97 /// Override the function name.
98 #[serde(default)]
99 pub function: Option<String>,
100 /// Override the crate name (Rust only).
101 #[serde(default)]
102 pub crate_name: Option<String>,
103 /// Override the class name (Java/C# only).
104 #[serde(default)]
105 pub class: Option<String>,
106 /// Import alias (Go only, e.g., `htmd`).
107 #[serde(default)]
108 pub alias: Option<String>,
109 /// C header file name (C only).
110 #[serde(default)]
111 pub header: Option<String>,
112 /// FFI symbol prefix (C only).
113 #[serde(default)]
114 pub prefix: Option<String>,
115 /// For json_object args: the constructor to use instead of raw dict/object.
116 /// E.g., "ConversionOptions" — generates `ConversionOptions(**options)` in Python,
117 /// `new ConversionOptions(options)` in TypeScript.
118 #[serde(default)]
119 pub options_type: Option<String>,
120 /// How to pass json_object args: "kwargs" (default), "dict", or "json".
121 ///
122 /// - `"kwargs"`: construct `OptionsType(key=val, ...)` (requires `options_type`).
123 /// - `"dict"`: pass as a plain dict/object literal `{"key": "val"}`.
124 /// - `"json"`: pass via `json.loads('...')` / `JSON.parse('...')`.
125 #[serde(default)]
126 pub options_via: Option<String>,
127 /// Maps fixture option field names to their enum type names.
128 /// E.g., `{"headingStyle": "HeadingStyle", "codeBlockStyle": "CodeBlockStyle"}`.
129 /// The generator imports these types and maps string values to enum constants.
130 #[serde(default)]
131 pub enum_fields: HashMap<String, String>,
132 /// Module to import enum types from (if different from the main module).
133 /// E.g., "html_to_markdown._html_to_markdown" for PyO3 native enums.
134 #[serde(default)]
135 pub enum_module: Option<String>,
136}
137
138/// Per-language package reference configuration.
139#[derive(Debug, Clone, Serialize, Deserialize, Default)]
140pub struct PackageRef {
141 /// Package/crate/gem/module name.
142 #[serde(default)]
143 pub name: Option<String>,
144 /// Relative path from e2e/{lang}/ to the package.
145 #[serde(default)]
146 pub path: Option<String>,
147 /// Go module path.
148 #[serde(default)]
149 pub module: Option<String>,
150 /// Package version (e.g., for go.mod require directives).
151 #[serde(default)]
152 pub version: Option<String>,
153}