1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
use serde::{Deserialize, Serialize};
/// Indicates the core Rust type wraps the resolved type in a smart pointer or cow.
/// Used by codegen to generate correct From/Into conversions.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
pub enum CoreWrapper {
#[default]
None,
/// `Cow<'static, str>` — binding uses String, core needs `.into()` ~keep
Cow,
/// `Arc<T>` — binding unwraps, core wraps with `Arc::new()` ~keep
Arc,
/// `bytes::Bytes` — binding uses `Vec<u8>`, core needs `Bytes::from()` ~keep
Bytes,
/// `Arc<Mutex<T>>` — binding wraps with `Arc::new(Mutex::new())`, methods call `.lock()` ~keep
ArcMutex,
/// `Box<str>` — binding uses String, core needs `.into()` (same shape as Cow
/// but distinct so backends can keep wrapper-specific behavior addressable). ~keep
Box,
}
/// Typed default value for a field, enabling backends to emit language-native defaults.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum DefaultValue {
BoolLiteral(bool),
StringLiteral(String),
IntLiteral(i64),
FloatLiteral(f64),
EnumVariant(String),
/// A tuple-variant enum default (`Mode::Custom(5)`), each positional argument folded
/// independently and kept in source order. Distinct from [`DefaultValue::EnumVariant`],
/// which names a bare unit-variant path with no arguments of its own.
///
/// Produced only when every argument itself folds to a value-carrying `DefaultValue` (see
/// `extract::extractor::defaults::carries_value`); a call with even one unfoldable argument
/// keeps the whole field [`DefaultValue::Unresolved`] rather than a partially-known payload
/// — rendering some arguments as literals and silently dropping the rest would be a subtler
/// instance of the fabrication `Unresolved` exists to prevent. ~keep
TupleVariant(String, Vec<DefaultValue>),
/// A struct-variant enum default (`Kind::Curated { label: "balanced".to_string() }`), each
/// named field folded independently and kept in source order. Same all-or-nothing rule and
/// rationale as [`DefaultValue::TupleVariant`]. ~keep
StructVariant(String, Vec<(String, DefaultValue)>),
/// A zero-argument Rust function that supplies the value at runtime. ~keep
FunctionCall(String),
/// A public zero-argument Rust function callable from generated binding crates. ~keep
PublicFunctionCall(String),
/// A non-empty collection literal, holding its elements in source order.
///
/// A genuinely empty `vec![]`/`Vec::new()` stays [`DefaultValue::Empty`]: the two are
/// distinct because every backend already renders "the empty collection" natively, whereas
/// this variant carries elements that have to be rendered individually. Only produced when
/// every element is itself representable — anything else falls back to `Empty`, so a
/// backend never emits a default that silently differs from the Rust one. ~keep
ListLiteral(Vec<DefaultValue>),
/// Empty collection or `Default::default()` — the type's own zero, and known to be exactly
/// what the Rust default is. Contrast [`DefaultValue::Unresolved`]. ~keep
Empty,
/// The extractor found the type's `Default` implementation but could not read a value out
/// of it: the body is neither a struct literal nor a delegation alef can constant-fold
/// (`Self::builder().build()`, a `match`, a computed constructor).
///
/// Distinct from [`DefaultValue::Empty`], and the distinction is the entire point of the
/// variant. `Empty` asserts *"the default is exactly this type's zero"* — true for
/// `#[derive(Default)]`, for `Vec::new()`, for `Default::default()` — so a backend
/// substituting its target language's zero is exact. `Unresolved` asserts the opposite:
/// alef does **not** know the value, and a zero would be a guess.
///
/// Before this variant existed both wrote `Empty`, so one enum value carried "exact" and
/// "guess" at once and nothing could tell them apart. Every per-field-literal backend
/// (C#, Java, Kotlin, Swift, Python, Go) then shipped its type-zero directly underneath a
/// generated doc comment quoting the real Rust default — the value the extractor had
/// already read out of the same doc prose.
///
/// The payload is the source text of the `fn default()` body that could not be read, so a
/// diagnostic can name it. ~keep
Unresolved(String),
/// None / null
None,
}
/// Stable identity metadata for one error variant. ~keep
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
pub struct ErrorTaxonomy {
#[serde(default)]
pub code: u32,
#[serde(default)]
pub error_type: String,
#[serde(default)]
pub variant: String,
}
impl ErrorTaxonomy {
pub fn for_variant(code: u32, error_type: &str, variant: &str) -> Self {
Self {
code,
error_type: error_type.to_string(),
variant: variant.to_string(),
}
}
}
/// Deprecation metadata extracted from `#[deprecated(...)]`.
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
pub struct DeprecationInfo {
/// Version when the item was deprecated (from `#[deprecated(since = "...")]`).
#[serde(default, skip_serializing_if = "Option::is_none")]
pub since: Option<String>,
/// Deprecation note (from `#[deprecated(note = "...")]`).
#[serde(default, skip_serializing_if = "Option::is_none")]
pub note: Option<String>,
}
/// Version annotation on an IR item.
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
pub struct VersionAnnotation {
/// Version when this item was introduced (from `#[alef(since = "...")]`).
#[serde(default, skip_serializing_if = "Option::is_none")]
pub since: Option<String>,
/// Deprecation info (from `#[deprecated(...)]`).
#[serde(default, skip_serializing_if = "Option::is_none")]
pub deprecated: Option<DeprecationInfo>,
}
#[cfg(test)]
mod error_taxonomy_tests {
use super::ErrorTaxonomy;
#[test]
fn explicit_variant_code_is_preserved() {
let taxonomy = ErrorTaxonomy::for_variant(101, "sample::RequestError", "InvalidInput");
assert_eq!(taxonomy.code, 101);
assert_eq!(taxonomy.error_type, "sample::RequestError");
assert_eq!(taxonomy.variant, "InvalidInput");
}
#[test]
fn legacy_serialized_taxonomy_defaults_compatibly() {
let taxonomy: ErrorTaxonomy = serde_json::from_str("{}").expect("legacy metadata deserializes");
assert_eq!(taxonomy, ErrorTaxonomy::default());
}
}