use serde::{Deserialize, Serialize};
use super::metadata::{CoreWrapper, DefaultValue, ErrorTaxonomy, SerdeContainerConversion, VersionAnnotation};
use super::type_ref::TypeRef;
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct TypeDef {
pub name: String,
pub rust_path: String,
#[serde(default)]
pub original_rust_path: String,
pub fields: Vec<FieldDef>,
pub methods: Vec<MethodDef>,
pub is_opaque: bool,
pub is_clone: bool,
#[serde(default)]
pub is_copy: bool,
pub doc: String,
#[serde(default)]
pub cfg: Option<String>,
#[serde(default)]
pub is_trait: bool,
#[serde(default)]
pub has_default: bool,
#[serde(default)]
pub has_stripped_cfg_fields: bool,
#[serde(default)]
pub is_return_type: bool,
#[serde(default)]
pub serde_rename_all: Option<String>,
#[serde(default)]
pub has_serde: bool,
#[serde(default)]
pub serde_container_default: bool,
#[serde(default)]
pub serde_container_conversion: SerdeContainerConversion,
#[serde(default)]
pub super_traits: Vec<String>,
#[serde(default)]
pub binding_excluded: bool,
#[serde(default)]
pub binding_exclusion_reason: Option<String>,
#[serde(default)]
pub is_variant_wrapper: bool,
#[serde(default)]
pub has_lifetime_params: bool,
#[serde(default)]
pub has_private_fields: bool,
#[serde(default)]
pub version: VersionAnnotation,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct FieldDef {
pub name: String,
pub ty: TypeRef,
pub optional: bool,
pub default: Option<String>,
pub doc: String,
#[serde(default)]
pub sanitized: bool,
#[serde(default)]
pub is_boxed: bool,
#[serde(default)]
pub type_rust_path: Option<String>,
#[serde(default)]
pub cfg: Option<String>,
#[serde(default)]
pub typed_default: Option<DefaultValue>,
#[serde(default)]
pub core_wrapper: CoreWrapper,
#[serde(default)]
pub vec_inner_core_wrapper: CoreWrapper,
#[serde(default)]
pub newtype_wrapper: Option<String>,
#[serde(default)]
pub serde_rename: Option<String>,
#[serde(default)]
pub serde_flatten: bool,
#[serde(default)]
pub serde_with: Option<String>,
#[serde(default)]
pub serde_skip_serializing_if: bool,
#[serde(default)]
pub binding_excluded: bool,
#[serde(default)]
pub binding_exclusion_reason: Option<String>,
#[serde(default)]
pub original_type: Option<String>,
#[serde(default)]
pub version: VersionAnnotation,
}
pub(crate) const ADAPTER_HANDLED_REASON_PREFIX: &str = "handled by [[crates.adapters]]";
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct MethodDef {
pub name: String,
pub params: Vec<ParamDef>,
pub return_type: TypeRef,
pub is_async: bool,
pub is_static: bool,
pub error_type: Option<String>,
pub doc: String,
pub receiver: Option<ReceiverKind>,
#[serde(default)]
pub cfg: Option<String>,
#[serde(default)]
pub sanitized: bool,
#[serde(default)]
pub trait_source: Option<String>,
#[serde(default)]
pub returns_ref: bool,
#[serde(default)]
pub returns_cow: bool,
#[serde(default)]
pub return_newtype_wrapper: Option<String>,
#[serde(default)]
pub has_default_impl: bool,
#[serde(default)]
pub binding_excluded: bool,
#[serde(default)]
pub binding_exclusion_reason: Option<String>,
#[serde(default)]
pub version: VersionAnnotation,
}
impl MethodDef {
pub fn returns_ref_to_owner(&self, owner_type_name: &str) -> bool {
self.is_static
&& self.returns_ref
&& matches!(&self.return_type, TypeRef::Named(name) if name == owner_type_name)
}
#[must_use]
pub fn cfg_satisfied(&self, enabled_features: &std::collections::HashSet<&str>) -> bool {
super::surface::cfg_feature_satisfied(self.cfg.as_deref(), enabled_features)
}
#[must_use]
pub fn cfg_within(&self, owner_cfg: Option<&str>) -> Option<String> {
match (owner_cfg, self.cfg.as_deref()) {
(Some(owner), Some(own)) if owner.trim() == own.trim() => Some(owner.to_string()),
(Some(owner), Some(own)) => Some(crate::codegen::cfg::combine_gates(owner, own)),
(Some(owner), None) => Some(owner.to_string()),
(None, own) => own.map(str::to_string),
}
}
#[must_use]
pub fn rust_cfg_attribute(&self) -> String {
self.cfg
.as_deref()
.map(|cfg| format!("#[cfg({cfg})]\n"))
.unwrap_or_default()
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum ReceiverKind {
Ref,
RefMut,
Owned,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct FunctionDef {
pub name: String,
pub rust_path: String,
#[serde(default)]
pub original_rust_path: String,
pub params: Vec<ParamDef>,
pub return_type: TypeRef,
pub is_async: bool,
pub error_type: Option<String>,
pub doc: String,
#[serde(default)]
pub cfg: Option<String>,
#[serde(default)]
pub sanitized: bool,
#[serde(default)]
pub return_sanitized: bool,
#[serde(default)]
pub returns_ref: bool,
#[serde(default)]
pub returns_cow: bool,
#[serde(default)]
pub return_newtype_wrapper: Option<String>,
#[serde(default)]
pub binding_excluded: bool,
#[serde(default)]
pub binding_exclusion_reason: Option<String>,
#[serde(default)]
pub version: VersionAnnotation,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ParamDef {
pub name: String,
pub ty: TypeRef,
pub optional: bool,
pub default: Option<String>,
#[serde(default)]
pub sanitized: bool,
#[serde(default)]
pub typed_default: Option<DefaultValue>,
#[serde(default)]
pub is_ref: bool,
#[serde(default)]
pub is_mut: bool,
#[serde(default)]
pub newtype_wrapper: Option<String>,
#[serde(default)]
pub original_type: Option<String>,
#[serde(default)]
pub map_is_ahash: bool,
#[serde(default)]
pub map_key_is_cow: bool,
#[serde(default)]
pub vec_inner_is_ref: bool,
#[serde(default)]
pub map_is_btree: bool,
#[serde(default)]
pub core_wrapper: CoreWrapper,
}
impl Default for ParamDef {
fn default() -> Self {
Self {
name: String::new(),
ty: TypeRef::Unit,
optional: false,
default: None,
sanitized: false,
typed_default: None,
is_ref: false,
is_mut: false,
newtype_wrapper: None,
original_type: None,
map_is_ahash: false,
map_key_is_cow: false,
vec_inner_is_ref: false,
map_is_btree: false,
core_wrapper: CoreWrapper::None,
}
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct EnumDef {
pub name: String,
pub rust_path: String,
#[serde(default)]
pub original_rust_path: String,
pub variants: Vec<EnumVariant>,
#[serde(default)]
pub methods: Vec<MethodDef>,
pub doc: String,
#[serde(default)]
pub cfg: Option<String>,
#[serde(default)]
pub is_copy: bool,
#[serde(default)]
pub has_serde: bool,
#[serde(default)]
pub has_default: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub serde_tag: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub serde_content: Option<String>,
#[serde(default)]
pub serde_untagged: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub serde_rename_all: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub rename_all_fields: Option<String>,
#[serde(default)]
pub binding_excluded: bool,
#[serde(default)]
pub binding_exclusion_reason: Option<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub excluded_variants: Vec<EnumVariant>,
#[serde(default)]
pub version: VersionAnnotation,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct EnumVariant {
pub name: String,
pub fields: Vec<FieldDef>,
pub doc: String,
#[serde(default)]
pub is_default: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub serde_rename: Option<String>,
#[serde(default)]
pub is_tuple: bool,
#[serde(default)]
pub binding_excluded: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub binding_exclusion_reason: Option<String>,
#[serde(default)]
pub originally_had_data_fields: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub cfg: Option<String>,
#[serde(default)]
pub version: VersionAnnotation,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ErrorDef {
pub name: String,
pub rust_path: String,
#[serde(default)]
pub original_rust_path: String,
pub variants: Vec<ErrorVariant>,
pub doc: String,
#[serde(default)]
pub methods: Vec<MethodDef>,
#[serde(default)]
pub binding_excluded: bool,
#[serde(default)]
pub binding_exclusion_reason: Option<String>,
#[serde(default)]
pub version: VersionAnnotation,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct ErrorVariant {
pub name: String,
#[serde(default)]
pub error_code: Option<u32>,
pub message_template: Option<String>,
#[serde(default)]
pub fields: Vec<FieldDef>,
#[serde(default)]
pub has_source: bool,
#[serde(default)]
pub has_from: bool,
#[serde(default)]
pub is_unit: bool,
#[serde(default)]
pub is_tuple: bool,
pub doc: String,
}
impl ErrorVariant {
pub fn taxonomy(&self, error_type: &str) -> Option<ErrorTaxonomy> {
self.error_code
.map(|code| ErrorTaxonomy::for_variant(code, error_type, &self.name))
}
}
#[cfg(test)]
mod tests {
use super::{EnumDef, EnumVariant, ErrorDef, ErrorVariant, FieldDef, FunctionDef, MethodDef, ParamDef, TypeDef};
#[allow(dead_code)]
fn type_def_field_coverage_witness(value: TypeDef) {
let TypeDef {
name: _, rust_path: _, original_rust_path: _, fields: _, methods: _, is_opaque: _, is_clone: _, is_copy: _, doc: _, cfg: _, is_trait: _, has_default: _, has_stripped_cfg_fields: _, is_return_type: _, serde_rename_all: _, has_serde: _, serde_container_default: _, serde_container_conversion: _, super_traits: _, binding_excluded: _, binding_exclusion_reason: _, is_variant_wrapper: _, has_lifetime_params: _, has_private_fields: _, version: _, } = value;
}
#[allow(dead_code)]
fn field_def_field_coverage_witness(value: FieldDef) {
let FieldDef {
name: _, ty: _, optional: _, default: _, doc: _, sanitized: _, is_boxed: _, type_rust_path: _, cfg: _, typed_default: _, core_wrapper: _, vec_inner_core_wrapper: _, newtype_wrapper: _, serde_rename: _, serde_flatten: _, serde_with: _, serde_skip_serializing_if: _, binding_excluded: _, binding_exclusion_reason: _, original_type: _, version: _, } = value;
}
#[allow(dead_code)]
fn method_def_field_coverage_witness(value: MethodDef) {
let MethodDef {
name: _, params: _, return_type: _, is_async: _, is_static: _, error_type: _, doc: _, receiver: _, cfg: _, sanitized: _, trait_source: _, returns_ref: _, returns_cow: _, return_newtype_wrapper: _, has_default_impl: _, binding_excluded: _, binding_exclusion_reason: _, version: _, } = value;
}
#[allow(dead_code)]
fn function_def_field_coverage_witness(value: FunctionDef) {
let FunctionDef {
name: _, rust_path: _, original_rust_path: _, params: _, return_type: _, is_async: _, error_type: _, doc: _, cfg: _, sanitized: _, return_sanitized: _, returns_ref: _, returns_cow: _, return_newtype_wrapper: _, binding_excluded: _, binding_exclusion_reason: _, version: _, } = value;
}
#[allow(dead_code)]
fn param_def_field_coverage_witness(value: ParamDef) {
let ParamDef {
name: _, ty: _, optional: _, default: _, sanitized: _, typed_default: _, is_ref: _, is_mut: _, newtype_wrapper: _, original_type: _, map_is_ahash: _, map_key_is_cow: _, vec_inner_is_ref: _, map_is_btree: _, core_wrapper: _, } = value;
}
#[allow(dead_code)]
fn enum_def_field_coverage_witness(value: EnumDef) {
let EnumDef {
name: _, rust_path: _, original_rust_path: _, variants: _, methods: _, doc: _, cfg: _, is_copy: _, has_serde: _, has_default: _, serde_tag: _, serde_content: _, serde_untagged: _, serde_rename_all: _, rename_all_fields: _, binding_excluded: _, binding_exclusion_reason: _, excluded_variants: _, version: _, } = value;
}
#[allow(dead_code)]
fn enum_variant_field_coverage_witness(value: EnumVariant) {
let EnumVariant {
name: _, fields: _, doc: _, is_default: _, serde_rename: _, is_tuple: _, binding_excluded: _, binding_exclusion_reason: _, originally_had_data_fields: _, cfg: _, version: _, } = value;
}
#[allow(dead_code)]
fn error_def_field_coverage_witness(value: ErrorDef) {
let ErrorDef {
name: _, rust_path: _, original_rust_path: _, variants: _, doc: _, methods: _, binding_excluded: _, binding_exclusion_reason: _, version: _, } = value;
}
#[allow(dead_code)]
fn error_variant_field_coverage_witness(value: ErrorVariant) {
let ErrorVariant {
name: _, error_code: _, message_template: _, fields: _, has_source: _, has_from: _, is_unit: _, is_tuple: _, doc: _, } = value;
}
fn gated(cfg: Option<&str>) -> MethodDef {
MethodDef {
name: "stream".to_string(),
cfg: cfg.map(str::to_string),
..MethodDef::default()
}
}
#[test]
fn rust_cfg_attribute_is_empty_when_ungated() {
assert_eq!(gated(None).rust_cfg_attribute(), "");
}
#[test]
fn rust_cfg_attribute_wraps_the_condition_with_a_trailing_newline() {
assert_eq!(
gated(Some("feature = \"streaming\"")).rust_cfg_attribute(),
"#[cfg(feature = \"streaming\")]\n"
);
}
#[test]
fn cfg_within_and_combines_owner_and_method_gates() {
assert_eq!(
gated(Some("feature = \"streaming\"")).cfg_within(Some("feature = \"client\"")),
Some("all(feature = \"client\", feature = \"streaming\")".to_string())
);
}
#[test]
fn cfg_within_collapses_an_owner_gate_the_method_already_inherited() {
assert_eq!(
gated(Some("feature = \"client\"")).cfg_within(Some("feature = \"client\"")),
Some("feature = \"client\"".to_string())
);
}
#[test]
fn cfg_within_falls_back_to_whichever_gate_is_present() {
assert_eq!(
gated(None).cfg_within(Some("feature = \"client\"")),
Some("feature = \"client\"".to_string())
);
assert_eq!(
gated(Some("feature = \"streaming\"")).cfg_within(None),
Some("feature = \"streaming\"".to_string())
);
assert_eq!(gated(None).cfg_within(None), None);
}
#[test]
fn cfg_satisfied_matches_the_shared_feature_evaluator() {
let enabled: std::collections::HashSet<&str> = ["streaming"].into_iter().collect();
assert!(gated(Some("feature = \"streaming\"")).cfg_satisfied(&enabled));
assert!(!gated(Some("feature = \"tokenizer\"")).cfg_satisfied(&enabled));
assert!(gated(None).cfg_satisfied(&enabled));
}
}