pub(super) fn adapter_param_python_type(rust_type: &str) -> &str {
match rust_type {
"String" | "&str" | "&'static str" => "str",
"bytes::Bytes" | "Vec<u8>" | "&[u8]" => "bytes",
"()" => "None",
other => other,
}
}
pub(super) fn streaming_item_converter(
adapter: &crate::core::config::AdapterConfig,
options_dataclass_types: &std::collections::HashSet<String>,
) -> Option<String> {
use heck::ToSnakeCase;
if !matches!(adapter.pattern, crate::core::config::AdapterPattern::Streaming) {
return None;
}
let item_type = adapter.item_type.as_deref()?;
if !options_dataclass_types.contains(item_type) {
return None;
}
Some(format!("_from_native_{}", item_type.to_snake_case()))
}
pub(super) fn adapter_return_converter(
adapter: &crate::core::config::AdapterConfig,
options_dataclass_types: &std::collections::HashSet<String>,
) -> Option<String> {
use heck::ToSnakeCase;
if !matches!(adapter.pattern, crate::core::config::AdapterPattern::AsyncMethod) {
return None;
}
let return_type = adapter.returns.as_deref()?;
if !options_dataclass_types.contains(return_type) {
return None;
}
Some(format!("_from_native_{}", return_type.to_snake_case()))
}
fn adapter_param_conversions(
params: &[crate::core::config::AdapterParam],
options_dataclass_types: &std::collections::HashSet<String>,
) -> (String, Vec<String>) {
use heck::ToSnakeCase;
let mut conversions = String::new();
let mut args = Vec::with_capacity(params.len());
for param in params {
if !options_dataclass_types.contains(¶m.ty) {
args.push(param.name.clone());
continue;
}
let snake = param.ty.to_snake_case();
let var = format!("_rust_{}", param.name);
let body = format!("_to_rust_{snake}({})", param.name);
super::signature_params::emit_param_conversion(&mut conversions, &var, ¶m.name, &body, param.optional);
if !param.optional {
conversions.push_str(&crate::backends::pyo3::template_env::render(
"config_default_on_none.jinja",
minijinja::context! { var => &var, name => ¶m.ty },
));
}
args.push(var);
}
(conversions, args)
}
pub(super) fn emit_adapter_wrapper(
out: &mut String,
adapter: &crate::core::config::AdapterConfig,
types: &[crate::core::ir::TypeDef],
options_dataclass_types: &std::collections::HashSet<String>,
) {
use crate::core::config::AdapterPattern;
use heck::ToSnakeCase;
let adapter_name = &adapter.name;
let owner_type = adapter.owner_type.as_deref().unwrap_or("Handle");
let (param_parts, request_construction) = if matches!(&adapter.pattern, AdapterPattern::Streaming)
&& adapter.request_type.is_some()
&& adapter.params.len() == 1
{
let param = &adapter.params[0];
let short_name = ¶m.ty;
let ir_type = types.iter().find(|t| &t.name == short_name);
if let Some(ty_def) = ir_type {
if let Some(first_field) = ty_def.fields.first() {
let field_name = &first_field.name;
let is_vec = matches!(&first_field.ty, crate::core::ir::TypeRef::Vec(_));
let python_type = if is_vec { "list[str]" } else { "str" };
let wrapper_params = vec![format!("engine: {owner_type}"), format!("{field_name}: {python_type}")];
let construction = format!(" req = _rust.{short_name}({field_name}={field_name})\n");
(wrapper_params, Some(construction))
} else {
let mut params = vec![format!("engine: {owner_type}")];
for p in &adapter.params {
let python_type = adapter_param_python_type(&p.ty);
let ann = if p.optional {
format!("{python_type} | None = None")
} else {
python_type.to_string()
};
params.push(format!("{}: {ann}", p.name));
}
(params, None)
}
} else {
let mut params = vec![format!("engine: {owner_type}")];
for p in &adapter.params {
let python_type = adapter_param_python_type(&p.ty);
let annotation = if p.optional {
format!("{python_type} | None = None")
} else {
python_type.to_string()
};
params.push(format!("{}: {}", p.name, annotation));
}
(params, None)
}
} else {
let mut params = vec![format!("engine: {owner_type}")];
for param in &adapter.params {
let param_name = ¶m.name;
let python_type = adapter_param_python_type(¶m.ty);
let annotation = if param.optional {
format!("{python_type} | None = None")
} else {
python_type.to_string()
};
params.push(format!("{param_name}: {annotation}"));
}
(params, None)
};
let doc_content = {
let snake = adapter_name.to_snake_case();
let sentence = snake.replace('_', " ");
let mut chars = sentence.chars();
let capitalized = match chars.next() {
None => String::new(),
Some(first) => first.to_uppercase().collect::<String>() + chars.as_str(),
};
format!("{capitalized}.")
};
let (params_list, param_conversions) = if request_construction.is_some() {
("req".to_string(), None)
} else {
let (conversions, args) = adapter_param_conversions(&adapter.params, options_dataclass_types);
(
args.join(", "),
if conversions.is_empty() {
None
} else {
Some(conversions)
},
)
};
match &adapter.pattern {
AdapterPattern::Streaming => {
let item_type = adapter.item_type.as_deref().unwrap_or("()");
let return_type = format!("AsyncIterator[{item_type}]");
out.push_str(&crate::backends::pyo3::template_env::render(
"adapter_streaming_wrapper.jinja",
minijinja::context! {
adapter_name => adapter_name,
params => param_parts.join(", "),
return_type => return_type,
doc_content => doc_content,
request_construction => request_construction.unwrap_or_default(),
param_conversions => param_conversions.unwrap_or_default(),
params_list => params_list,
item_converter => streaming_item_converter(adapter, options_dataclass_types).unwrap_or_default(),
},
));
}
AdapterPattern::AsyncMethod => {
let raw_return = adapter.returns.as_deref().unwrap_or("None");
let return_type = adapter_param_python_type(raw_return);
out.push_str(&crate::backends::pyo3::template_env::render(
"adapter_async_wrapper.jinja",
minijinja::context! {
adapter_name => adapter_name,
params => param_parts.join(", "),
return_type => return_type,
doc_content => doc_content,
request_construction => request_construction.unwrap_or_default(),
param_conversions => param_conversions.unwrap_or_default(),
params_list => params_list,
return_converter => adapter_return_converter(adapter, options_dataclass_types).unwrap_or_default(),
},
));
}
_ => return,
}
out.push_str("\n\n");
}