alef_core/ir.rs
1use serde::{Deserialize, Serialize};
2
3/// Indicates the core Rust type wraps the resolved type in a smart pointer or cow.
4/// Used by codegen to generate correct From/Into conversions.
5#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
6pub enum CoreWrapper {
7 #[default]
8 None,
9 /// `Cow<'static, str>` — binding uses String, core needs `.into()`
10 Cow,
11 /// `Arc<T>` — binding unwraps, core wraps with `Arc::new()`
12 Arc,
13 /// `bytes::Bytes` — binding uses `Vec<u8>`, core needs `Bytes::from()`
14 Bytes,
15 /// `Arc<Mutex<T>>` — binding wraps with `Arc::new(Mutex::new())`, methods call `.lock()`
16 ArcMutex,
17}
18
19/// Typed default value for a field, enabling backends to emit language-native defaults.
20#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
21pub enum DefaultValue {
22 BoolLiteral(bool),
23 StringLiteral(String),
24 IntLiteral(i64),
25 FloatLiteral(f64),
26 EnumVariant(String),
27 /// Empty collection or Default::default()
28 Empty,
29 /// None / null
30 None,
31}
32
33/// Complete API surface extracted from a Rust crate's public interface.
34#[derive(Debug, Clone, Default, Serialize, Deserialize)]
35pub struct ApiSurface {
36 pub crate_name: String,
37 pub version: String,
38 pub types: Vec<TypeDef>,
39 pub functions: Vec<FunctionDef>,
40 pub enums: Vec<EnumDef>,
41 pub errors: Vec<ErrorDef>,
42 /// Type names → fully qualified rust_paths for types that were extracted but
43 /// then excluded from the public binding surface. Preserved so trait_bridge
44 /// codegen can still reference them by qualified path when they appear in
45 /// trait method signatures (e.g. `Renderer::render(&InternalDocument)`).
46 #[serde(default)]
47 pub excluded_type_paths: std::collections::HashMap<String, String>,
48 /// Subset of `excluded_type_paths` keys whose underlying definition is a trait
49 /// (`is_trait = true` on the original `TypeDef`). The `is_trait` flag is lost
50 /// when the type is stripped, so trait-bridge codegen tracks excluded traits
51 /// separately to decide whether a return-type `Named(name)` referencing an
52 /// excluded item is a non-bridgeable trait object (skip the method, fall back
53 /// to default impl) or a struct/enum still usable via its qualified path.
54 #[serde(default)]
55 pub excluded_trait_names: std::collections::HashSet<String>,
56}
57
58/// A public struct exposed to bindings.
59#[derive(Debug, Clone, Serialize, Deserialize)]
60pub struct TypeDef {
61 pub name: String,
62 pub rust_path: String,
63 /// Original rust_path before path mapping rewrites. Used for From impl
64 /// targets to avoid orphan rule violations when core_import is a re-export facade.
65 #[serde(default)]
66 pub original_rust_path: String,
67 pub fields: Vec<FieldDef>,
68 pub methods: Vec<MethodDef>,
69 pub is_opaque: bool,
70 pub is_clone: bool,
71 /// True if the type derives `Copy` (or is bitwise-copyable).
72 /// Used by FFI codegen to avoid emitting `.clone()` (which trips clippy::clone_on_copy).
73 #[serde(default)]
74 pub is_copy: bool,
75 pub doc: String,
76 #[serde(default)]
77 pub cfg: Option<String>,
78 /// True if this type was extracted from a trait definition.
79 /// Trait types need `dyn` keyword when used as opaque inner types.
80 #[serde(default)]
81 pub is_trait: bool,
82 /// True if the type implements Default (via derive or manual impl).
83 /// Used by backends like NAPI to make all fields optional with defaults.
84 #[serde(default)]
85 pub has_default: bool,
86 /// True if some fields were stripped due to `#[cfg]` conditions.
87 /// When true, struct literal initializers need `..Default::default()` to fill
88 /// the missing fields that may exist when the core crate is compiled with features.
89 #[serde(default)]
90 pub has_stripped_cfg_fields: bool,
91 /// True if this type appears as a function return type.
92 /// Used to select output DTO style (e.g., TypedDict for Python return types).
93 #[serde(default)]
94 pub is_return_type: bool,
95 /// Serde `rename_all` strategy for this type (e.g., `"camelCase"`, `"snake_case"`).
96 /// Used by Go/Java/C# backends to emit correct JSON tags matching Rust serde config.
97 #[serde(default)]
98 pub serde_rename_all: Option<String>,
99 /// True if the type derives `serde::Serialize` and `serde::Deserialize`.
100 /// Used by FFI backend to gate `from_json`/`to_json` generation — types
101 /// without serde derives cannot be (de)serialized.
102 #[serde(default)]
103 pub has_serde: bool,
104 /// Super-traits of this trait (e.g., `["Plugin"]` for `OcrBackend: Plugin`).
105 /// Only populated when `is_trait` is true. Used by trait bridge codegen
106 /// to determine which super-trait impls to generate.
107 #[serde(default)]
108 pub super_traits: Vec<String>,
109 /// True when source metadata explicitly excludes this type/trait from generated
110 /// polyglot binding surfaces (via `#[cfg_attr(alef, alef(skip))]` or `#[doc(hidden)]`).
111 #[serde(default)]
112 pub binding_excluded: bool,
113 /// Human-readable reason for `binding_excluded`, used in diagnostics.
114 #[serde(default)]
115 pub binding_exclusion_reason: Option<String>,
116}
117
118/// A field on a public struct.
119#[derive(Debug, Clone, Serialize, Deserialize)]
120pub struct FieldDef {
121 pub name: String,
122 pub ty: TypeRef,
123 pub optional: bool,
124 pub default: Option<String>,
125 pub doc: String,
126 /// True if this field's type was sanitized (e.g., Duration→u64, trait object→String).
127 /// Fields marked sanitized cannot participate in auto-generated From/Into conversions.
128 #[serde(default)]
129 pub sanitized: bool,
130 /// True if the core field type is `Box<T>` (or `Option<Box<T>>`).
131 /// Used by FFI backends to insert proper deref when cloning field values.
132 #[serde(default)]
133 pub is_boxed: bool,
134 /// Fully qualified Rust path for the field's type (e.g. `my_crate::types::OutputFormat`).
135 /// Used by backends to disambiguate types with the same short name.
136 #[serde(default)]
137 pub type_rust_path: Option<String>,
138 /// `#[cfg(...)]` condition string on this field, if any.
139 /// Used by backends to conditionally include fields in struct literals.
140 #[serde(default)]
141 pub cfg: Option<String>,
142 /// Typed default value for language-native default emission.
143 #[serde(default)]
144 pub typed_default: Option<DefaultValue>,
145 /// Core wrapper on this field (Cow, Arc, Bytes). Affects From/Into codegen.
146 #[serde(default)]
147 pub core_wrapper: CoreWrapper,
148 /// Core wrapper on Vec inner elements (e.g., `Vec<Arc<T>>`).
149 #[serde(default)]
150 pub vec_inner_core_wrapper: CoreWrapper,
151 /// Full Rust path of the newtype wrapper that was resolved away for this field,
152 /// e.g. `"my_crate::NodeIndex"` when `NodeIndex(u32)` was resolved to `u32`.
153 /// When set, binding→core codegen must wrap values into the newtype
154 /// (e.g. `my_crate::NodeIndex(val.field)`) and core→binding codegen must unwrap (`.0`).
155 #[serde(default)]
156 pub newtype_wrapper: Option<String>,
157 /// Explicit `#[serde(rename = "...")]` on this field, if any. Preserved so binding
158 /// structs that mirror the core struct can serialize/deserialize using the same wire
159 /// names (e.g. core `tool_type` with `#[serde(rename = "type")]` round-trips as `"type"`).
160 #[serde(default)]
161 pub serde_rename: Option<String>,
162 /// True when the field carries `#[serde(flatten)]`. Backends use this to emit
163 /// language-native flatten support: Jackson `@JsonAnyGetter`/`@JsonAnySetter`
164 /// in Java, `[JsonExtensionData]` in C# — both keyed `Map<String, Object>` /
165 /// `Dictionary<string, JsonElement>` so unknown sibling fields land under the
166 /// flattened bag instead of being rejected.
167 #[serde(default)]
168 pub serde_flatten: bool,
169 /// True when source metadata explicitly excludes this field from generated
170 /// polyglot binding surfaces.
171 #[serde(default)]
172 pub binding_excluded: bool,
173 /// Human-readable reason for `binding_excluded`, used in diagnostics.
174 #[serde(default)]
175 pub binding_exclusion_reason: Option<String>,
176 /// Original Rust type string before sanitization (e.g. `"Vec<(String, String)>"`).
177 /// Populated by `sanitize_unknown_types()` when a type is downgraded.
178 /// Allows backends to reconstruct proper serialization/deserialization logic
179 /// even when the sanitized `ty` field only carries the simplified type.
180 #[serde(default)]
181 pub original_type: Option<String>,
182}
183
184/// A method on a public struct.
185#[derive(Debug, Clone, Serialize, Deserialize)]
186pub struct MethodDef {
187 pub name: String,
188 pub params: Vec<ParamDef>,
189 pub return_type: TypeRef,
190 pub is_async: bool,
191 pub is_static: bool,
192 pub error_type: Option<String>,
193 pub doc: String,
194 pub receiver: Option<ReceiverKind>,
195 /// True if any param or return type was sanitized during unknown type resolution.
196 /// Methods with sanitized signatures cannot be auto-delegated.
197 #[serde(default)]
198 pub sanitized: bool,
199 /// Fully qualified trait path if this method comes from a trait impl
200 /// (e.g. "liter_llm::LlmClient"). None for inherent methods.
201 #[serde(default)]
202 pub trait_source: Option<String>,
203 /// True if the core function returns a reference (`&T`, `Option<&T>`, etc.).
204 /// Used by code generators to insert `.clone()` before type conversion.
205 #[serde(default)]
206 pub returns_ref: bool,
207 /// True if the core function returns `Cow<'_, T>` where T is a named type (not str/bytes).
208 /// Used by code generators to emit `.into_owned()` before type conversion.
209 #[serde(default)]
210 pub returns_cow: bool,
211 /// Full Rust path of the newtype wrapper that was resolved away for the return type,
212 /// e.g. `"my_crate::NodeIndex"` when the return type `NodeIndex(u32)` was resolved to `u32`.
213 /// When set, codegen must unwrap the returned newtype value (e.g. `result.0`) before returning.
214 #[serde(default)]
215 pub return_newtype_wrapper: Option<String>,
216 /// True if this method has a default implementation in the trait definition.
217 /// Methods with defaults can be optionally implemented by the foreign object
218 /// in trait bridge codegen.
219 #[serde(default)]
220 pub has_default_impl: bool,
221 /// True when source metadata explicitly excludes this method from generated
222 /// polyglot binding surfaces (via `#[cfg_attr(alef, alef(skip))]` or `#[doc(hidden)]`).
223 #[serde(default)]
224 pub binding_excluded: bool,
225 /// Human-readable reason for `binding_excluded`, used in diagnostics.
226 #[serde(default)]
227 pub binding_exclusion_reason: Option<String>,
228}
229
230/// How `self` is received.
231#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
232pub enum ReceiverKind {
233 Ref,
234 RefMut,
235 Owned,
236}
237
238/// A free function exposed to bindings.
239#[derive(Debug, Clone, Serialize, Deserialize)]
240pub struct FunctionDef {
241 pub name: String,
242 pub rust_path: String,
243 #[serde(default)]
244 pub original_rust_path: String,
245 pub params: Vec<ParamDef>,
246 pub return_type: TypeRef,
247 pub is_async: bool,
248 pub error_type: Option<String>,
249 pub doc: String,
250 #[serde(default)]
251 pub cfg: Option<String>,
252 /// True if any param or return type was sanitized during unknown type resolution.
253 #[serde(default)]
254 pub sanitized: bool,
255 /// True if the return type was sanitized (Named replaced with String). When true,
256 /// the binding-side return type is wider than the actual core return — codegen must
257 /// JSON-serialize the core value rather than treating it as the binding type.
258 #[serde(default)]
259 pub return_sanitized: bool,
260 /// True if the core function returns a reference (`&T`, `Option<&T>`, etc.).
261 /// Used by code generators to insert `.clone()` before type conversion.
262 #[serde(default)]
263 pub returns_ref: bool,
264 /// True if the core function returns `Cow<'_, T>` where T is a named type (not str/bytes).
265 /// Used by code generators to emit `.into_owned()` before type conversion.
266 #[serde(default)]
267 pub returns_cow: bool,
268 /// Full Rust path of the newtype wrapper that was resolved away for the return type.
269 /// When set, codegen must unwrap the returned newtype value (e.g. `result.0`).
270 #[serde(default)]
271 pub return_newtype_wrapper: Option<String>,
272 /// True when source metadata explicitly excludes this function from generated
273 /// polyglot binding surfaces (via `#[cfg_attr(alef, alef(skip))]` or `#[doc(hidden)]`).
274 #[serde(default)]
275 pub binding_excluded: bool,
276 /// Human-readable reason for `binding_excluded`, used in diagnostics.
277 #[serde(default)]
278 pub binding_exclusion_reason: Option<String>,
279}
280
281/// A function/method parameter.
282#[derive(Debug, Clone, Serialize, Deserialize)]
283pub struct ParamDef {
284 pub name: String,
285 pub ty: TypeRef,
286 pub optional: bool,
287 pub default: Option<String>,
288 /// True if this param's type was sanitized during unknown type resolution.
289 #[serde(default)]
290 pub sanitized: bool,
291 /// Typed default value for language-native default emission.
292 #[serde(default)]
293 pub typed_default: Option<DefaultValue>,
294 /// True if the original Rust parameter was a reference (`&T`).
295 /// Used by codegen to generate owned intermediates and pass refs.
296 #[serde(default)]
297 pub is_ref: bool,
298 /// True if the original Rust parameter was a mutable reference (`&mut T`).
299 /// Used by codegen to generate `&mut` refs when calling core functions.
300 #[serde(default)]
301 pub is_mut: bool,
302 /// Full Rust path of the newtype wrapper that was resolved away for this param,
303 /// e.g. `"my_crate::NodeIndex"` when `NodeIndex(u32)` was resolved to `u32`.
304 /// When set, codegen must wrap the raw value back into the newtype when calling core:
305 /// `my_crate::NodeIndex(param)` instead of just `param`.
306 #[serde(default)]
307 pub newtype_wrapper: Option<String>,
308 /// Original Rust type before sanitization, stored when param.sanitized=true.
309 /// Allows codegen to reconstruct proper deserialization logic.
310 /// E.g. `"Vec<(PathBuf, Option<FileExtractionConfig>)>"` when sanitized to `Vec<String>`.
311 #[serde(default)]
312 pub original_type: Option<String>,
313}
314
315/// A public enum.
316#[derive(Debug, Clone, Serialize, Deserialize)]
317pub struct EnumDef {
318 pub name: String,
319 pub rust_path: String,
320 #[serde(default)]
321 pub original_rust_path: String,
322 pub variants: Vec<EnumVariant>,
323 pub doc: String,
324 #[serde(default)]
325 pub cfg: Option<String>,
326 /// True if the enum derives `Copy`. Only unit-variant enums can derive Copy.
327 /// Used by FFI codegen to avoid emitting `.clone()` (which trips clippy::clone_on_copy).
328 #[serde(default)]
329 pub is_copy: bool,
330 /// True if the enum derives both `serde::Serialize` and `serde::Deserialize`.
331 /// Used by host-language emission (e.g. Swift `Codable`) to gate JSON-bridge conformance.
332 #[serde(default)]
333 pub has_serde: bool,
334 /// Serde tag property name for internally tagged enums (from `#[serde(tag = "...")]`)
335 #[serde(default, skip_serializing_if = "Option::is_none")]
336 pub serde_tag: Option<String>,
337 /// True when the enum has `#[serde(untagged)]`.
338 /// Absence of `serde_tag` does NOT imply untagged — it means externally-tagged (the serde
339 /// default). Only set this when the attribute is explicitly present on the Rust type.
340 #[serde(default)]
341 pub serde_untagged: bool,
342 /// Serde rename strategy for enum variants (from `#[serde(rename_all = "...")]`)
343 #[serde(default, skip_serializing_if = "Option::is_none")]
344 pub serde_rename_all: Option<String>,
345 /// True when source metadata explicitly excludes this enum from generated
346 /// polyglot binding surfaces (via `#[cfg_attr(alef, alef(skip))]` or `#[doc(hidden)]`).
347 #[serde(default)]
348 pub binding_excluded: bool,
349 /// Human-readable reason for `binding_excluded`, used in diagnostics.
350 #[serde(default)]
351 pub binding_exclusion_reason: Option<String>,
352}
353
354/// An enum variant.
355#[derive(Debug, Clone, Serialize, Deserialize)]
356pub struct EnumVariant {
357 pub name: String,
358 pub fields: Vec<FieldDef>,
359 pub doc: String,
360 /// True if this variant has `#[default]` attribute (used by `#[derive(Default)]`).
361 #[serde(default)]
362 pub is_default: bool,
363 /// Explicit serde rename for this variant (from `#[serde(rename = "...")]`).
364 #[serde(default, skip_serializing_if = "Option::is_none")]
365 pub serde_rename: Option<String>,
366 /// True if this is a tuple variant (unnamed fields like `Variant(T1, T2)`).
367 /// False for struct variants with named fields or unit variants.
368 #[serde(default)]
369 pub is_tuple: bool,
370}
371
372/// An error type (enum used in Result<T, E>).
373#[derive(Debug, Clone, Serialize, Deserialize)]
374pub struct ErrorDef {
375 pub name: String,
376 pub rust_path: String,
377 #[serde(default)]
378 pub original_rust_path: String,
379 pub variants: Vec<ErrorVariant>,
380 pub doc: String,
381 /// True when source metadata explicitly excludes this error type from generated
382 /// polyglot binding surfaces (via `#[cfg_attr(alef, alef(skip))]` or `#[doc(hidden)]`).
383 #[serde(default)]
384 pub binding_excluded: bool,
385 /// Human-readable reason for `binding_excluded`, used in diagnostics.
386 #[serde(default)]
387 pub binding_exclusion_reason: Option<String>,
388}
389
390/// An error variant.
391#[derive(Debug, Clone, Serialize, Deserialize)]
392pub struct ErrorVariant {
393 pub name: String,
394 /// The `#[error("...")]` message template string, e.g. `"I/O error: {0}"`.
395 pub message_template: Option<String>,
396 /// Fields on this variant (struct or tuple fields).
397 #[serde(default)]
398 pub fields: Vec<FieldDef>,
399 /// True if any field has `#[source]` or `#[from]`.
400 #[serde(default)]
401 pub has_source: bool,
402 /// True if any field has `#[from]` (auto From conversion).
403 #[serde(default)]
404 pub has_from: bool,
405 /// True if this is a unit variant (no fields).
406 #[serde(default)]
407 pub is_unit: bool,
408 pub doc: String,
409}
410
411/// Reference to a type, with enough info for codegen.
412#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
413pub enum TypeRef {
414 Primitive(PrimitiveType),
415 String,
416 /// Rust `char` — single Unicode character. Binding layer represents as single-char string.
417 Char,
418 Bytes,
419 Optional(Box<TypeRef>),
420 Vec(Box<TypeRef>),
421 Map(Box<TypeRef>, Box<TypeRef>),
422 Named(String),
423 Path,
424 Unit,
425 Json,
426 Duration,
427}
428
429impl TypeRef {
430 /// Returns true if this type reference contains `Named(name)` at any depth.
431 pub fn references_named(&self, name: &str) -> bool {
432 match self {
433 Self::Named(n) => n == name,
434 Self::Optional(inner) | Self::Vec(inner) => inner.references_named(name),
435 Self::Map(k, v) => k.references_named(name) || v.references_named(name),
436 _ => false,
437 }
438 }
439}
440
441/// Rust primitive types.
442#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
443pub enum PrimitiveType {
444 Bool,
445 U8,
446 U16,
447 U32,
448 U64,
449 I8,
450 I16,
451 I32,
452 I64,
453 F32,
454 F64,
455 Usize,
456 Isize,
457}