use crate::core::config::Language;
use crate::core::ir::{FunctionDef, MethodDef, TypeRef};
use crate::docs::naming::{field_name, func_name, to_camel_case, type_name};
use crate::docs::type_mapping::doc_type;
use heck::{ToPascalCase, ToSnakeCase};
pub(crate) fn render_function_signature(func: &FunctionDef, lang: Language, ffi_prefix: &str) -> String {
match lang {
Language::Python => render_python_fn_sig(func, ffi_prefix),
Language::Node | Language::Wasm => render_typescript_fn_sig(func, ffi_prefix),
Language::Go => render_go_fn_sig(func, ffi_prefix),
Language::Java => render_java_fn_sig(func, ffi_prefix),
Language::Ruby => render_ruby_fn_sig(func),
Language::Ffi | Language::C | Language::Jni => render_c_fn_sig(func, ffi_prefix),
Language::Php => render_php_fn_sig(func, ffi_prefix),
Language::Elixir => render_elixir_fn_sig(func),
Language::R => render_r_fn_sig(func),
Language::Csharp => render_csharp_fn_sig(func, ffi_prefix),
Language::Rust => render_rust_fn_sig(func, ffi_prefix),
Language::Kotlin | Language::KotlinAndroid => render_kotlin_fn_sig(func, ffi_prefix),
Language::Swift => render_swift_fn_sig(func, ffi_prefix),
Language::Dart => render_dart_fn_sig(func, ffi_prefix),
Language::Zig => render_zig_fn_sig(func, ffi_prefix),
Language::Gleam => {
format!("// Phase 1: {lang} backend signature generation")
}
}
}
pub(crate) fn render_python_fn_sig(func: &FunctionDef, ffi_prefix: &str) -> String {
let name = func.name.to_snake_case();
let params: Vec<String> = func
.params
.iter()
.map(|p| {
let pname = p.name.to_snake_case();
let pty = doc_type(&p.ty, Language::Python, ffi_prefix);
if p.optional {
format!("{pname}: {pty} = None")
} else {
format!("{pname}: {pty}")
}
})
.collect();
let ret = doc_type(&func.return_type, Language::Python, ffi_prefix);
format!("def {}({}) -> {}", name, params.join(", "), ret)
}
pub(crate) fn render_typescript_fn_sig(func: &FunctionDef, ffi_prefix: &str) -> String {
let name = to_camel_case(&func.name);
let params: Vec<String> = func
.params
.iter()
.map(|p| {
let pname = to_camel_case(&p.name);
let pty = doc_type(&p.ty, Language::Node, ffi_prefix);
if p.optional {
format!("{pname}?: {pty}")
} else {
format!("{pname}: {pty}")
}
})
.collect();
let ret = doc_type(&func.return_type, Language::Node, ffi_prefix);
if func.is_async {
format!("function {}({}): Promise<{}>", name, params.join(", "), ret)
} else {
format!("function {}({}): {}", name, params.join(", "), ret)
}
}
pub(crate) fn render_go_fn_sig(func: &FunctionDef, ffi_prefix: &str) -> String {
let name = func.name.to_pascal_case();
let params: Vec<String> = func
.params
.iter()
.map(|p| {
let pname = to_camel_case(&p.name);
let pty = doc_type(&p.ty, Language::Go, ffi_prefix);
format!("{pname} {pty}")
})
.collect();
let ret = doc_type(&func.return_type, Language::Go, ffi_prefix);
if func.error_type.is_some() {
if ret.is_empty() {
format!("func {}({}) error", name, params.join(", "))
} else {
format!("func {}({}) ({}, error)", name, params.join(", "), ret)
}
} else if ret.is_empty() {
format!("func {}({})", name, params.join(", "))
} else {
format!("func {}({}) {}", name, params.join(", "), ret)
}
}
pub(crate) fn render_java_fn_sig(func: &FunctionDef, ffi_prefix: &str) -> String {
let name = to_camel_case(&func.name);
let ret = doc_type(&func.return_type, Language::Java, ffi_prefix);
let params: Vec<String> = func
.params
.iter()
.map(|p| {
let pname = to_camel_case(&p.name);
let pty = doc_type(&p.ty, Language::Java, ffi_prefix);
format!("{pty} {pname}")
})
.collect();
let throws = func
.error_type
.as_ref()
.map(|e| format!(" throws {}", type_name(e, Language::Java, ffi_prefix)))
.unwrap_or_default();
format!("public static {} {}({}){}", ret, name, params.join(", "), throws)
}
pub(crate) fn render_ruby_fn_sig(func: &FunctionDef) -> String {
let name = func.name.to_snake_case();
let params: Vec<String> = func
.params
.iter()
.map(|p| {
let pname = p.name.to_snake_case();
if p.optional { format!("{pname}: nil") } else { pname }
})
.collect();
format!("def self.{}({})", name, params.join(", "))
}
pub(crate) fn render_c_fn_sig(func: &FunctionDef, ffi_prefix: &str) -> String {
let prefix = ffi_prefix.to_snake_case();
let name = format!("{}_{}", prefix, func.name.to_snake_case());
let ret = doc_type(&func.return_type, Language::Ffi, ffi_prefix);
let params: Vec<String> = func
.params
.iter()
.map(|p| {
let pname = p.name.to_snake_case();
let pty = doc_type(&p.ty, Language::Ffi, ffi_prefix);
format!("{pty} {pname}")
})
.collect();
let ret_str = match &func.return_type {
TypeRef::Named(_) => format!("{}*", ret),
TypeRef::Unit => "void".to_string(),
_ => ret,
};
format!("{} {}({});", ret_str, name, params.join(", "))
}
pub(crate) fn render_php_fn_sig(func: &FunctionDef, ffi_prefix: &str) -> String {
let name = to_camel_case(&func.name);
let params: Vec<String> = func
.params
.iter()
.map(|p| {
let pname = format!("${}", to_camel_case(&p.name));
let pty = doc_type(&p.ty, Language::Php, ffi_prefix);
if p.optional {
format!("?{pty} {pname} = null")
} else {
format!("{pty} {pname}")
}
})
.collect();
let ret = doc_type(&func.return_type, Language::Php, ffi_prefix);
format!("public static function {}({}): {}", name, params.join(", "), ret)
}
pub(crate) fn render_elixir_fn_sig(func: &FunctionDef) -> String {
let name = func.name.to_snake_case();
let params: Vec<String> = func.params.iter().map(|p| p.name.to_snake_case()).collect();
format!(
"@spec {}({}) :: {{:ok, term()}} | {{:error, term()}}\ndef {}({})",
name,
params.join(", "),
name,
params.join(", ")
)
}
pub(crate) fn render_r_fn_sig(func: &FunctionDef) -> String {
let name = func.name.to_snake_case();
let params: Vec<String> = func
.params
.iter()
.map(|p| {
let pname = p.name.to_snake_case();
if p.optional { format!("{pname} = NULL") } else { pname }
})
.collect();
format!("{}({})", name, params.join(", "))
}
pub(crate) fn render_csharp_fn_sig(func: &FunctionDef, ffi_prefix: &str) -> String {
let name = func.name.to_pascal_case();
let ret = doc_type(&func.return_type, Language::Csharp, ffi_prefix);
let params: Vec<String> = func
.params
.iter()
.map(|p| {
let pname = to_camel_case(&p.name);
let pty = doc_type(&p.ty, Language::Csharp, ffi_prefix);
if p.optional {
format!("{pty}? {pname} = null")
} else {
format!("{pty} {pname}")
}
})
.collect();
if func.is_async {
let async_name = if name.ends_with("Async") {
name.clone()
} else {
format!("{name}Async")
};
let task_ret = if ret == "void" {
"Task".to_string()
} else {
format!("Task<{ret}>")
};
format!("public static async {} {}({})", task_ret, async_name, params.join(", "))
} else {
format!("public static {} {}({})", ret, name, params.join(", "))
}
}
pub(crate) fn render_rust_fn_sig(func: &FunctionDef, ffi_prefix: &str) -> String {
let name = func.name.to_snake_case();
let params: Vec<String> = func
.params
.iter()
.map(|p| {
let pname = p.name.to_snake_case();
let pty = doc_type(&p.ty, Language::Rust, ffi_prefix);
if p.optional {
format!("{pname}: Option<{pty}>")
} else {
match &p.ty {
TypeRef::String | TypeRef::Char => format!("{pname}: &str"),
TypeRef::Bytes => format!("{pname}: &[u8]"),
_ => format!("{pname}: {pty}"),
}
}
})
.collect();
let ret = doc_type(&func.return_type, Language::Rust, ffi_prefix);
let error_part = if let Some(err) = &func.error_type {
let err_ty = type_name(err, Language::Rust, ffi_prefix);
if ret == "()" {
format!(" -> Result<(), {err_ty}>")
} else {
format!(" -> Result<{ret}, {err_ty}>")
}
} else if ret == "()" {
String::new()
} else {
format!(" -> {ret}")
};
if func.is_async {
format!("pub async fn {}({}){}", name, params.join(", "), error_part)
} else {
format!("pub fn {}({}){}", name, params.join(", "), error_part)
}
}
pub(crate) fn render_kotlin_fn_sig(func: &FunctionDef, ffi_prefix: &str) -> String {
let name = to_camel_case(&func.name);
let ret = doc_type(&func.return_type, Language::Kotlin, ffi_prefix);
let params: Vec<String> = func
.params
.iter()
.map(|p| {
let pname = to_camel_case(&p.name);
let pty = doc_type(&p.ty, Language::Kotlin, ffi_prefix);
if p.optional {
format!("{pname}: {pty}? = null")
} else {
format!("{pname}: {pty}")
}
})
.collect();
let throws = func
.error_type
.as_ref()
.map(|e| format!("@Throws({}::class)\n", type_name(e, Language::Kotlin, ffi_prefix)))
.unwrap_or_default();
let ret_part = if ret == "Unit" {
String::new()
} else {
format!(": {ret}")
};
format!("{throws}fun {name}({}){ret_part}", params.join(", "))
}
pub(crate) fn render_swift_fn_sig(func: &FunctionDef, ffi_prefix: &str) -> String {
let name = to_camel_case(&func.name);
let ret = doc_type(&func.return_type, Language::Swift, ffi_prefix);
let params: Vec<String> = func
.params
.iter()
.map(|p| {
let pname = to_camel_case(&p.name);
let pty = doc_type(&p.ty, Language::Swift, ffi_prefix);
if p.optional {
format!("{pname}: {pty}? = nil")
} else {
format!("{pname}: {pty}")
}
})
.collect();
let throws = if func.error_type.is_some() { " throws" } else { "" };
let ret_part = if ret == "Void" {
String::new()
} else {
format!(" -> {ret}")
};
format!("public static func {name}({}){throws}{ret_part}", params.join(", "))
}
pub(crate) fn render_dart_fn_sig(func: &FunctionDef, ffi_prefix: &str) -> String {
let name = to_camel_case(&func.name);
let ret = doc_type(&func.return_type, Language::Dart, ffi_prefix);
let required: Vec<String> = func
.params
.iter()
.filter(|p| !p.optional)
.map(|p| {
let pname = to_camel_case(&p.name);
let pty = doc_type(&p.ty, Language::Dart, ffi_prefix);
format!("{pty} {pname}")
})
.collect();
let optional: Vec<String> = func
.params
.iter()
.filter(|p| p.optional)
.map(|p| {
let pname = to_camel_case(&p.name);
let pty = doc_type(&p.ty, Language::Dart, ffi_prefix);
format!("{pty}? {pname}")
})
.collect();
let mut all_params = required;
if !optional.is_empty() {
all_params.push(format!("[{}]", optional.join(", ")));
}
format!("{ret} {name}({})", all_params.join(", "))
}
pub(crate) fn render_zig_fn_sig(func: &FunctionDef, ffi_prefix: &str) -> String {
let name = func.name.to_snake_case();
let ret = doc_type(&func.return_type, Language::Zig, ffi_prefix);
let params: Vec<String> = func
.params
.iter()
.map(|p| {
let pname = p.name.to_snake_case();
let pty = doc_type(&p.ty, Language::Zig, ffi_prefix);
if p.optional {
format!("{pname}: ?{pty}")
} else {
format!("{pname}: {pty}")
}
})
.collect();
let ret_str = if let Some(err) = &func.error_type {
let err_ty = type_name(err, Language::Zig, ffi_prefix);
if ret == "void" {
format!("{err_ty}!void")
} else {
format!("{err_ty}!{ret}")
}
} else {
ret
};
format!("pub fn {name}({}) {ret_str}", params.join(", "))
}
pub(crate) fn render_method_signature(
method: &MethodDef,
type_name_str: &str,
lang: Language,
ffi_prefix: &str,
) -> String {
let name = func_name(&method.name, lang, ffi_prefix);
let ret = doc_type(&method.return_type, lang, ffi_prefix);
match lang {
Language::Python => {
let params: Vec<String> = method
.params
.iter()
.map(|p| {
let pname = field_name(&p.name, lang);
let pty = doc_type(&p.ty, lang, ffi_prefix);
format!("{pname}: {pty}")
})
.collect();
if method.is_static {
format!("@staticmethod\ndef {}({}) -> {}", name, params.join(", "), ret)
} else {
let mut all_params = vec!["self".to_string()];
all_params.extend(params);
format!("def {}({}) -> {}", name, all_params.join(", "), ret)
}
}
Language::Node | Language::Wasm => {
let params: Vec<String> = method
.params
.iter()
.map(|p| {
let pname = field_name(&p.name, lang);
let pty = doc_type(&p.ty, lang, ffi_prefix);
format!("{pname}: {pty}")
})
.collect();
if method.is_static {
format!("static {}({}): {}", name, params.join(", "), ret)
} else {
format!("{}({}): {}", name, params.join(", "), ret)
}
}
Language::Ruby => {
let params: Vec<String> = method.params.iter().map(|p| p.name.to_snake_case()).collect();
if method.is_static {
format!("def self.{}({})", name, params.join(", "))
} else {
format!("def {}({})", name, params.join(", "))
}
}
Language::Go => {
let go_receiver_type = type_name(type_name_str, Language::Go, ffi_prefix);
let receiver = format!("o *{go_receiver_type}");
let params: Vec<String> = method
.params
.iter()
.map(|p| {
let pname = to_camel_case(&p.name);
let pty = doc_type(&p.ty, lang, ffi_prefix);
format!("{pname} {pty}")
})
.collect();
if method.error_type.is_some() {
if ret.is_empty() {
format!("func ({receiver}) {}({}) error", name, params.join(", "))
} else {
format!("func ({receiver}) {}({}) ({}, error)", name, params.join(", "), ret)
}
} else if ret.is_empty() {
format!("func ({receiver}) {}({})", name, params.join(", "))
} else {
format!("func ({receiver}) {}({}) {}", name, params.join(", "), ret)
}
}
Language::Java => {
let java_name = if name == "default" {
"defaultOptions".to_string()
} else {
name.clone()
};
let params: Vec<String> = method
.params
.iter()
.map(|p| {
let pname = to_camel_case(&p.name);
let pty = doc_type(&p.ty, lang, ffi_prefix);
format!("{pty} {pname}")
})
.collect();
let throws = method
.error_type
.as_ref()
.map(|e| format!(" throws {}", type_name(e, lang, ffi_prefix)))
.unwrap_or_default();
if method.is_static {
format!("public static {} {}({}){}", ret, java_name, params.join(", "), throws)
} else {
format!("public {} {}({}){}", ret, java_name, params.join(", "), throws)
}
}
Language::Csharp => {
let params: Vec<String> = method
.params
.iter()
.map(|p| {
let pname = to_camel_case(&p.name);
let pty = doc_type(&p.ty, lang, ffi_prefix);
format!("{pty} {pname}")
})
.collect();
if method.is_async {
let async_name = if name.ends_with("Async") {
name.clone()
} else {
format!("{name}Async")
};
let task_ret = if ret == "void" {
"Task".to_string()
} else {
format!("Task<{ret}>")
};
format!("public async {} {}({})", task_ret, async_name, params.join(", "))
} else {
format!("public {} {}({})", ret, name, params.join(", "))
}
}
Language::Php => {
let params: Vec<String> = method
.params
.iter()
.map(|p| {
let pname = format!("${}", to_camel_case(&p.name));
let pty = doc_type(&p.ty, lang, ffi_prefix);
format!("{pty} {pname}")
})
.collect();
if method.is_static {
format!("public static function {}({}): {}", name, params.join(", "), ret)
} else {
format!("public function {}({}): {}", name, params.join(", "), ret)
}
}
Language::Elixir => {
let params: Vec<String> = method.params.iter().map(|p| p.name.to_snake_case()).collect();
format!("def {}({})", name, params.join(", "))
}
Language::R => {
let params: Vec<String> = method.params.iter().map(|p| p.name.to_snake_case()).collect();
format!("{}({})", name, params.join(", "))
}
Language::Ffi | Language::C | Language::Jni => {
let params: Vec<String> = method
.params
.iter()
.map(|p| {
let pname = p.name.to_snake_case();
let pty = doc_type(&p.ty, lang, ffi_prefix);
format!("{pty} {pname}")
})
.collect();
format!("{} {}({});", ret, name, params.join(", "))
}
Language::Rust => {
let params: Vec<String> = method
.params
.iter()
.map(|p| {
let pname = p.name.to_snake_case();
let pty = doc_type(&p.ty, lang, ffi_prefix);
if p.optional {
format!("{pname}: Option<{pty}>")
} else {
match &p.ty {
TypeRef::String | TypeRef::Char => format!("{pname}: &str"),
TypeRef::Bytes => format!("{pname}: &[u8]"),
_ => format!("{pname}: {pty}"),
}
}
})
.collect();
if method.is_static {
if ret == "()" {
format!("pub fn {}({})", name, params.join(", "))
} else {
format!("pub fn {}({}) -> {}", name, params.join(", "), ret)
}
} else {
let mut all_params = vec!["&self".to_string()];
all_params.extend(params);
if ret == "()" {
format!("pub fn {}({})", name, all_params.join(", "))
} else {
format!("pub fn {}({}) -> {}", name, all_params.join(", "), ret)
}
}
}
Language::Kotlin | Language::KotlinAndroid => {
let params: Vec<String> = method
.params
.iter()
.map(|p| {
let pname = to_camel_case(&p.name);
let pty = doc_type(&p.ty, lang, ffi_prefix);
if p.optional {
format!("{pname}: {pty}? = null")
} else {
format!("{pname}: {pty}")
}
})
.collect();
let throws = method
.error_type
.as_ref()
.map(|e| format!("@Throws({}::class)\n", type_name(e, lang, ffi_prefix)))
.unwrap_or_default();
let ret_part = if ret == "Unit" {
String::new()
} else {
format!(": {ret}")
};
if method.is_static {
format!("{throws}@JvmStatic\nfun {name}({}){ret_part}", params.join(", "))
} else {
format!("{throws}fun {name}({}){ret_part}", params.join(", "))
}
}
Language::Swift => {
let params: Vec<String> = method
.params
.iter()
.map(|p| {
let pname = to_camel_case(&p.name);
let pty = doc_type(&p.ty, lang, ffi_prefix);
if p.optional {
format!("{pname}: {pty}? = nil")
} else {
format!("{pname}: {pty}")
}
})
.collect();
let throws = if method.error_type.is_some() { " throws" } else { "" };
let ret_part = if ret == "Void" {
String::new()
} else {
format!(" -> {ret}")
};
if method.is_static {
format!("public static func {name}({}){throws}{ret_part}", params.join(", "))
} else {
format!("public func {name}({}){throws}{ret_part}", params.join(", "))
}
}
Language::Dart => {
let required: Vec<String> = method
.params
.iter()
.filter(|p| !p.optional)
.map(|p| {
let pname = to_camel_case(&p.name);
let pty = doc_type(&p.ty, lang, ffi_prefix);
format!("{pty} {pname}")
})
.collect();
let optional: Vec<String> = method
.params
.iter()
.filter(|p| p.optional)
.map(|p| {
let pname = to_camel_case(&p.name);
let pty = doc_type(&p.ty, lang, ffi_prefix);
format!("{pty}? {pname}")
})
.collect();
let mut all_params = required;
if !optional.is_empty() {
all_params.push(format!("[{}]", optional.join(", ")));
}
let static_kw = if method.is_static { "static " } else { "" };
format!("{static_kw}{ret} {name}({})", all_params.join(", "))
}
Language::Zig => {
let params: Vec<String> = method
.params
.iter()
.map(|p| {
let pname = p.name.to_snake_case();
let pty = doc_type(&p.ty, lang, ffi_prefix);
if p.optional {
format!("{pname}: ?{pty}")
} else {
format!("{pname}: {pty}")
}
})
.collect();
let ret_str = if let Some(err) = &method.error_type {
let err_ty = type_name(err, lang, ffi_prefix);
if ret == "void" {
format!("{err_ty}!void")
} else {
format!("{err_ty}!{ret}")
}
} else {
ret
};
let receiver_ty = type_name(type_name_str, lang, ffi_prefix);
let mut all_params = if method.is_static {
Vec::new()
} else {
vec![format!("self: *const {receiver_ty}")]
};
all_params.extend(params);
format!("pub fn {name}({}) {ret_str}", all_params.join(", "))
}
Language::Gleam => {
format!("// Phase 1: {lang} backend method signature generation")
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::core::config::Language;
use crate::core::ir::{PrimitiveType, TypeRef};
use crate::docs::test_helpers::{TEST_PREFIX, make_function, make_method, make_param};
#[test]
fn test_render_method_signature_python_sync_with_params_and_return() {
let method = make_method(
"get_text",
vec![make_param("page", TypeRef::Primitive(PrimitiveType::U32), false)],
TypeRef::String,
false,
false,
None,
);
let sig = render_method_signature(&method, "Document", Language::Python, TEST_PREFIX);
assert_eq!(sig, "def get_text(self, page: int) -> str");
}
#[test]
fn test_render_method_signature_python_async() {
let method = make_method("process", vec![], TypeRef::String, true, false, None);
let sig = render_method_signature(&method, "Document", Language::Python, TEST_PREFIX);
assert_eq!(sig, "def process(self) -> str");
}
#[test]
fn test_render_method_signature_python_static() {
let method = make_method(
"create",
vec![make_param("name", TypeRef::String, false)],
TypeRef::Named("Document".to_string()),
false,
true,
None,
);
let sig = render_method_signature(&method, "Document", Language::Python, TEST_PREFIX);
assert_eq!(sig, "@staticmethod\ndef create(name: str) -> Document");
}
#[test]
fn test_render_method_signature_python_optional_return() {
let method = make_method(
"find",
vec![make_param("query", TypeRef::String, false)],
TypeRef::Optional(Box::new(TypeRef::String)),
false,
false,
None,
);
let sig = render_method_signature(&method, "Corpus", Language::Python, TEST_PREFIX);
assert_eq!(sig, "def find(self, query: str) -> str | None");
}
#[test]
fn test_render_method_signature_python_with_error_type() {
let method = make_method(
"parse",
vec![make_param("source", TypeRef::String, false)],
TypeRef::Named("Ast".to_string()),
false,
false,
Some("ParseError"),
);
let sig = render_method_signature(&method, "Parser", Language::Python, TEST_PREFIX);
assert_eq!(sig, "def parse(self, source: str) -> Ast");
}
#[test]
fn test_render_method_signature_node_sync_with_params_and_return() {
let method = make_method(
"get_text",
vec![make_param("page", TypeRef::Primitive(PrimitiveType::U32), false)],
TypeRef::String,
false,
false,
None,
);
let sig = render_method_signature(&method, "Document", Language::Node, TEST_PREFIX);
assert_eq!(sig, "getText(page: number): string");
}
#[test]
fn test_render_method_signature_node_async() {
let method = make_method("process", vec![], TypeRef::String, true, false, None);
let sig = render_method_signature(&method, "Document", Language::Node, TEST_PREFIX);
assert_eq!(sig, "process(): string");
}
#[test]
fn test_render_method_signature_node_static() {
let method = make_method(
"create",
vec![make_param("name", TypeRef::String, false)],
TypeRef::Named("Document".to_string()),
false,
true,
None,
);
let sig = render_method_signature(&method, "Document", Language::Node, TEST_PREFIX);
assert_eq!(sig, "static create(name: string): Document");
}
#[test]
fn test_render_method_signature_node_optional_return() {
let method = make_method(
"find",
vec![],
TypeRef::Optional(Box::new(TypeRef::String)),
false,
false,
None,
);
let sig = render_method_signature(&method, "Corpus", Language::Node, TEST_PREFIX);
assert_eq!(sig, "find(): string | null");
}
#[test]
fn test_render_method_signature_node_with_error_type() {
let method = make_method(
"parse",
vec![make_param("source", TypeRef::String, false)],
TypeRef::Named("Ast".to_string()),
false,
false,
Some("ParseError"),
);
let sig = render_method_signature(&method, "Parser", Language::Node, TEST_PREFIX);
assert_eq!(sig, "parse(source: string): Ast");
}
#[test]
fn test_render_method_signature_rust_sync_with_params_and_return() {
let method = make_method(
"get_text",
vec![make_param("page", TypeRef::Primitive(PrimitiveType::U32), false)],
TypeRef::String,
false,
false,
None,
);
let sig = render_method_signature(&method, "Document", Language::Rust, TEST_PREFIX);
assert_eq!(sig, "pub fn get_text(&self, page: u32) -> String");
}
#[test]
fn test_render_method_signature_rust_async() {
let method = make_method("fetch", vec![], TypeRef::String, true, false, None);
let sig = render_method_signature(&method, "Client", Language::Rust, TEST_PREFIX);
assert_eq!(sig, "pub fn fetch(&self) -> String");
}
#[test]
fn test_render_method_signature_rust_static() {
let method = make_method(
"new",
vec![make_param("name", TypeRef::String, false)],
TypeRef::Named("Document".to_string()),
false,
true,
None,
);
let sig = render_method_signature(&method, "Document", Language::Rust, TEST_PREFIX);
assert_eq!(sig, "pub fn new(name: &str) -> Document");
}
#[test]
fn test_render_method_signature_rust_optional_return() {
let method = make_method(
"find",
vec![],
TypeRef::Optional(Box::new(TypeRef::Named("Node".to_string()))),
false,
false,
None,
);
let sig = render_method_signature(&method, "Tree", Language::Rust, TEST_PREFIX);
assert_eq!(sig, "pub fn find(&self) -> Option<Node>");
}
#[test]
fn test_render_method_signature_rust_with_error_type() {
let method = make_method(
"parse",
vec![make_param("source", TypeRef::String, false)],
TypeRef::Named("Ast".to_string()),
false,
false,
Some("ParseError"),
);
let sig = render_method_signature(&method, "Parser", Language::Rust, TEST_PREFIX);
assert_eq!(sig, "pub fn parse(&self, source: &str) -> Ast");
}
#[test]
fn test_render_method_signature_go_sync_with_params_and_return() {
let method = make_method(
"get_text",
vec![make_param("page", TypeRef::Primitive(PrimitiveType::U32), false)],
TypeRef::String,
false,
false,
None,
);
let sig = render_method_signature(&method, "Document", Language::Go, TEST_PREFIX);
assert_eq!(sig, "func (o *Document) GetText(page uint32) string");
}
#[test]
fn test_render_method_signature_go_async() {
let method = make_method("fetch", vec![], TypeRef::String, true, false, None);
let sig = render_method_signature(&method, "Client", Language::Go, TEST_PREFIX);
assert_eq!(sig, "func (o *Client) Fetch() string");
}
#[test]
fn test_render_method_signature_go_optional_return() {
let method = make_method(
"find",
vec![],
TypeRef::Optional(Box::new(TypeRef::String)),
false,
false,
None,
);
let sig = render_method_signature(&method, "Corpus", Language::Go, TEST_PREFIX);
assert_eq!(sig, "func (o *Corpus) Find() *string");
}
#[test]
fn test_render_method_signature_go_with_error_type() {
let method = make_method(
"parse",
vec![make_param("source", TypeRef::String, false)],
TypeRef::Named("Ast".to_string()),
false,
false,
Some("ParseError"),
);
let sig = render_method_signature(&method, "Parser", Language::Go, TEST_PREFIX);
assert_eq!(sig, "func (o *Parser) Parse(source string) (Ast, error)");
}
#[test]
fn test_render_method_signature_go_error_type_unit_return() {
let method = make_method("save", vec![], TypeRef::Unit, false, false, Some("IoError"));
let sig = render_method_signature(&method, "File", Language::Go, TEST_PREFIX);
assert_eq!(sig, "func (o *File) Save() error");
}
#[test]
fn test_render_method_signature_ruby_sync_with_params() {
let method = make_method(
"get_text",
vec![make_param("page", TypeRef::Primitive(PrimitiveType::U32), false)],
TypeRef::String,
false,
false,
None,
);
let sig = render_method_signature(&method, "Document", Language::Ruby, TEST_PREFIX);
assert_eq!(sig, "def get_text(page)");
}
#[test]
fn test_render_method_signature_ruby_static() {
let method = make_method(
"create",
vec![make_param("name", TypeRef::String, false)],
TypeRef::Named("Document".to_string()),
false,
true,
None,
);
let sig = render_method_signature(&method, "Document", Language::Ruby, TEST_PREFIX);
assert_eq!(sig, "def self.create(name)");
}
#[test]
fn test_render_method_signature_ruby_async() {
let method = make_method("fetch", vec![], TypeRef::String, true, false, None);
let sig = render_method_signature(&method, "Client", Language::Ruby, TEST_PREFIX);
assert_eq!(sig, "def fetch()");
}
#[test]
fn test_render_method_signature_ruby_optional_return() {
let method = make_method(
"find",
vec![],
TypeRef::Optional(Box::new(TypeRef::String)),
false,
false,
None,
);
let sig = render_method_signature(&method, "Corpus", Language::Ruby, TEST_PREFIX);
assert_eq!(sig, "def find()");
}
#[test]
fn test_render_method_signature_ruby_with_error_type() {
let method = make_method("parse", vec![], TypeRef::String, false, false, Some("ParseError"));
let sig = render_method_signature(&method, "Parser", Language::Ruby, TEST_PREFIX);
assert_eq!(sig, "def parse()");
}
#[test]
fn test_render_method_signature_php_sync_with_params_and_return() {
let method = make_method(
"get_text",
vec![make_param("page", TypeRef::Primitive(PrimitiveType::U32), false)],
TypeRef::String,
false,
false,
None,
);
let sig = render_method_signature(&method, "Document", Language::Php, TEST_PREFIX);
assert_eq!(sig, "public function getText(int $page): string");
}
#[test]
fn test_render_method_signature_php_static() {
let method = make_method(
"create",
vec![make_param("name", TypeRef::String, false)],
TypeRef::Named("Document".to_string()),
false,
true,
None,
);
let sig = render_method_signature(&method, "Document", Language::Php, TEST_PREFIX);
assert_eq!(sig, "public static function create(string $name): Document");
}
#[test]
fn test_render_method_signature_php_optional_return() {
let method = make_method(
"find",
vec![],
TypeRef::Optional(Box::new(TypeRef::String)),
false,
false,
None,
);
let sig = render_method_signature(&method, "Corpus", Language::Php, TEST_PREFIX);
assert_eq!(sig, "public function find(): ?string");
}
#[test]
fn test_render_method_signature_php_async() {
let method = make_method("fetch", vec![], TypeRef::String, true, false, None);
let sig = render_method_signature(&method, "Client", Language::Php, TEST_PREFIX);
assert_eq!(sig, "public function fetch(): string");
}
#[test]
fn test_render_method_signature_php_with_error_type() {
let method = make_method("parse", vec![], TypeRef::String, false, false, Some("ParseError"));
let sig = render_method_signature(&method, "Parser", Language::Php, TEST_PREFIX);
assert_eq!(sig, "public function parse(): string");
}
#[test]
fn test_render_method_signature_java_sync_with_params_and_return() {
let method = make_method(
"get_text",
vec![make_param("page", TypeRef::Primitive(PrimitiveType::U32), false)],
TypeRef::String,
false,
false,
None,
);
let sig = render_method_signature(&method, "Document", Language::Java, TEST_PREFIX);
assert_eq!(sig, "public String getText(int page)");
}
#[test]
fn test_render_method_signature_java_static() {
let method = make_method(
"create",
vec![make_param("name", TypeRef::String, false)],
TypeRef::Named("Document".to_string()),
false,
true,
None,
);
let sig = render_method_signature(&method, "Document", Language::Java, TEST_PREFIX);
assert_eq!(sig, "public static Document create(String name)");
}
#[test]
fn test_render_method_signature_java_optional_return() {
let method = make_method(
"find",
vec![],
TypeRef::Optional(Box::new(TypeRef::String)),
false,
false,
None,
);
let sig = render_method_signature(&method, "Corpus", Language::Java, TEST_PREFIX);
assert_eq!(sig, "public Optional<String> find()");
}
#[test]
fn test_render_method_signature_java_async() {
let method = make_method("fetch", vec![], TypeRef::String, true, false, None);
let sig = render_method_signature(&method, "Client", Language::Java, TEST_PREFIX);
assert_eq!(sig, "public String fetch()");
}
#[test]
fn test_render_method_signature_java_with_error_type() {
let method = make_method(
"parse",
vec![make_param("source", TypeRef::String, false)],
TypeRef::Named("Ast".to_string()),
false,
false,
Some("ParseError"),
);
let sig = render_method_signature(&method, "Parser", Language::Java, TEST_PREFIX);
assert_eq!(sig, "public Ast parse(String source) throws ParseError");
}
#[test]
fn test_render_method_signature_csharp_sync_with_params_and_return() {
let method = make_method(
"get_text",
vec![make_param("page", TypeRef::Primitive(PrimitiveType::U32), false)],
TypeRef::String,
false,
false,
None,
);
let sig = render_method_signature(&method, "Document", Language::Csharp, TEST_PREFIX);
assert_eq!(sig, "public string GetText(uint page)");
}
#[test]
fn test_render_method_signature_csharp_async() {
let method = make_method("fetch", vec![], TypeRef::String, true, false, None);
let sig = render_method_signature(&method, "Client", Language::Csharp, TEST_PREFIX);
assert_eq!(sig, "public async Task<string> FetchAsync()");
}
#[test]
fn test_render_method_signature_csharp_async_already_suffixed() {
let method = make_method("fetch_async", vec![], TypeRef::String, true, false, None);
let sig = render_method_signature(&method, "Client", Language::Csharp, TEST_PREFIX);
assert_eq!(sig, "public async Task<string> FetchAsync()");
}
#[test]
fn test_render_method_signature_csharp_optional_return() {
let method = make_method(
"find",
vec![],
TypeRef::Optional(Box::new(TypeRef::String)),
false,
false,
None,
);
let sig = render_method_signature(&method, "Corpus", Language::Csharp, TEST_PREFIX);
assert_eq!(sig, "public string? Find()");
}
#[test]
fn test_render_method_signature_csharp_with_error_type() {
let method = make_method("parse", vec![], TypeRef::String, false, false, Some("ParseError"));
let sig = render_method_signature(&method, "Parser", Language::Csharp, TEST_PREFIX);
assert_eq!(sig, "public string Parse()");
}
#[test]
fn test_render_method_signature_elixir_sync_with_params() {
let method = make_method(
"get_text",
vec![make_param("page", TypeRef::Primitive(PrimitiveType::U32), false)],
TypeRef::String,
false,
false,
None,
);
let sig = render_method_signature(&method, "Document", Language::Elixir, TEST_PREFIX);
assert_eq!(sig, "def get_text(page)");
}
#[test]
fn test_render_method_signature_elixir_async() {
let method = make_method("fetch", vec![], TypeRef::String, true, false, None);
let sig = render_method_signature(&method, "Client", Language::Elixir, TEST_PREFIX);
assert_eq!(sig, "def fetch()");
}
#[test]
fn test_render_method_signature_elixir_optional_return() {
let method = make_method(
"find",
vec![],
TypeRef::Optional(Box::new(TypeRef::String)),
false,
false,
None,
);
let sig = render_method_signature(&method, "Corpus", Language::Elixir, TEST_PREFIX);
assert_eq!(sig, "def find()");
}
#[test]
fn test_render_method_signature_elixir_with_error_type() {
let method = make_method("parse", vec![], TypeRef::String, false, false, Some("ParseError"));
let sig = render_method_signature(&method, "Parser", Language::Elixir, TEST_PREFIX);
assert_eq!(sig, "def parse()");
}
#[test]
fn test_render_method_signature_r_sync_with_params() {
let method = make_method(
"get_text",
vec![make_param("page", TypeRef::Primitive(PrimitiveType::U32), false)],
TypeRef::String,
false,
false,
None,
);
let sig = render_method_signature(&method, "Document", Language::R, TEST_PREFIX);
assert_eq!(sig, "get_text(page)");
}
#[test]
fn test_render_method_signature_r_async() {
let method = make_method("fetch", vec![], TypeRef::String, true, false, None);
let sig = render_method_signature(&method, "Client", Language::R, TEST_PREFIX);
assert_eq!(sig, "fetch()");
}
#[test]
fn test_render_method_signature_r_optional_return() {
let method = make_method(
"find",
vec![],
TypeRef::Optional(Box::new(TypeRef::String)),
false,
false,
None,
);
let sig = render_method_signature(&method, "Corpus", Language::R, TEST_PREFIX);
assert_eq!(sig, "find()");
}
#[test]
fn test_render_method_signature_r_with_error_type() {
let method = make_method("parse", vec![], TypeRef::String, false, false, Some("ParseError"));
let sig = render_method_signature(&method, "Parser", Language::R, TEST_PREFIX);
assert_eq!(sig, "parse()");
}
#[test]
fn test_render_method_signature_wasm_sync() {
let method = make_method(
"get_text",
vec![make_param("page", TypeRef::Primitive(PrimitiveType::U32), false)],
TypeRef::String,
false,
false,
None,
);
let sig = render_method_signature(&method, "Document", Language::Wasm, TEST_PREFIX);
assert_eq!(sig, "getText(page: number): string");
}
#[test]
fn test_render_method_signature_wasm_static() {
let method = make_method(
"create",
vec![],
TypeRef::Named("Document".to_string()),
false,
true,
None,
);
let sig = render_method_signature(&method, "Document", Language::Wasm, TEST_PREFIX);
assert_eq!(sig, "static create(): Document");
}
#[test]
fn test_render_python_fn_sig_basic() {
let func = make_function(
"convert",
vec![make_param("source", TypeRef::String, false)],
TypeRef::String,
false,
None,
);
let sig = render_python_fn_sig(&func, TEST_PREFIX);
assert_eq!(sig, "def convert(source: str) -> str");
}
#[test]
fn test_render_python_fn_sig_async() {
let func = make_function("fetch", vec![], TypeRef::String, true, None);
let sig = render_python_fn_sig(&func, TEST_PREFIX);
assert_eq!(sig, "def fetch() -> str");
}
#[test]
fn test_render_python_fn_sig_optional_param() {
let func = make_function(
"search",
vec![
make_param("query", TypeRef::String, false),
make_param("limit", TypeRef::Primitive(PrimitiveType::U32), true),
],
TypeRef::Vec(Box::new(TypeRef::String)),
false,
None,
);
let sig = render_python_fn_sig(&func, TEST_PREFIX);
assert_eq!(sig, "def search(query: str, limit: int = None) -> list[str]");
}
#[test]
fn test_render_python_fn_sig_complex_return_type() {
let func = make_function(
"get_mapping",
vec![],
TypeRef::Map(
Box::new(TypeRef::String),
Box::new(TypeRef::Primitive(PrimitiveType::I32)),
),
false,
None,
);
let sig = render_python_fn_sig(&func, TEST_PREFIX);
assert_eq!(sig, "def get_mapping() -> dict[str, int]");
}
#[test]
fn test_render_rust_fn_sig_basic() {
let func = make_function(
"convert",
vec![make_param("source", TypeRef::String, false)],
TypeRef::String,
false,
None,
);
let sig = render_rust_fn_sig(&func, TEST_PREFIX);
assert_eq!(sig, "pub fn convert(source: &str) -> String");
}
#[test]
fn test_render_rust_fn_sig_async() {
let func = make_function("fetch", vec![], TypeRef::String, true, None);
let sig = render_rust_fn_sig(&func, TEST_PREFIX);
assert_eq!(sig, "pub async fn fetch() -> String");
}
#[test]
fn test_render_rust_fn_sig_optional_param() {
let func = make_function(
"search",
vec![
make_param("query", TypeRef::String, false),
make_param("limit", TypeRef::Primitive(PrimitiveType::U32), true),
],
TypeRef::Vec(Box::new(TypeRef::String)),
false,
None,
);
let sig = render_rust_fn_sig(&func, TEST_PREFIX);
assert_eq!(sig, "pub fn search(query: &str, limit: Option<u32>) -> Vec<String>");
}
#[test]
fn test_render_rust_fn_sig_error_type_with_return() {
let func = make_function(
"parse",
vec![make_param("source", TypeRef::String, false)],
TypeRef::Named("Ast".to_string()),
false,
Some("ParseError"),
);
let sig = render_rust_fn_sig(&func, TEST_PREFIX);
assert_eq!(sig, "pub fn parse(source: &str) -> Result<Ast, ParseError>");
}
#[test]
fn test_render_rust_fn_sig_error_type_unit_return() {
let func = make_function("save", vec![], TypeRef::Unit, false, Some("IoError"));
let sig = render_rust_fn_sig(&func, TEST_PREFIX);
assert_eq!(sig, "pub fn save() -> Result<(), IoError>");
}
#[test]
fn test_render_go_fn_sig_basic() {
let func = make_function(
"convert",
vec![make_param("source", TypeRef::String, false)],
TypeRef::String,
false,
None,
);
let sig = render_go_fn_sig(&func, TEST_PREFIX);
assert_eq!(sig, "func Convert(source string) string");
}
#[test]
fn test_render_go_fn_sig_async() {
let func = make_function("fetch", vec![], TypeRef::String, true, None);
let sig = render_go_fn_sig(&func, TEST_PREFIX);
assert_eq!(sig, "func Fetch() string");
}
#[test]
fn test_render_go_fn_sig_optional_param() {
let func = make_function(
"search",
vec![make_param("limit", TypeRef::Primitive(PrimitiveType::U32), false)],
TypeRef::Vec(Box::new(TypeRef::String)),
false,
None,
);
let sig = render_go_fn_sig(&func, TEST_PREFIX);
assert_eq!(sig, "func Search(limit uint32) []string");
}
#[test]
fn test_render_go_fn_sig_error_type_with_return() {
let func = make_function(
"parse",
vec![make_param("source", TypeRef::String, false)],
TypeRef::Named("Ast".to_string()),
false,
Some("ParseError"),
);
let sig = render_go_fn_sig(&func, TEST_PREFIX);
assert_eq!(sig, "func Parse(source string) (Ast, error)");
}
#[test]
fn test_render_go_fn_sig_error_type_unit_return() {
let func = make_function("save", vec![], TypeRef::Unit, false, Some("IoError"));
let sig = render_go_fn_sig(&func, TEST_PREFIX);
assert_eq!(sig, "func Save() error");
}
#[test]
fn test_render_java_fn_sig_basic() {
let func = make_function(
"convert",
vec![make_param("source", TypeRef::String, false)],
TypeRef::String,
false,
None,
);
let sig = render_java_fn_sig(&func, TEST_PREFIX);
assert_eq!(sig, "public static String convert(String source)");
}
#[test]
fn test_render_java_fn_sig_async() {
let func = make_function("fetch", vec![], TypeRef::String, true, None);
let sig = render_java_fn_sig(&func, TEST_PREFIX);
assert_eq!(sig, "public static String fetch()");
}
#[test]
fn test_render_java_fn_sig_optional_param() {
let func = make_function(
"search",
vec![make_param("limit", TypeRef::Primitive(PrimitiveType::U32), false)],
TypeRef::Vec(Box::new(TypeRef::String)),
false,
None,
);
let sig = render_java_fn_sig(&func, TEST_PREFIX);
assert_eq!(sig, "public static List<String> search(int limit)");
}
#[test]
fn test_render_java_fn_sig_error_type() {
let func = make_function(
"parse",
vec![make_param("source", TypeRef::String, false)],
TypeRef::Named("Ast".to_string()),
false,
Some("ParseError"),
);
let sig = render_java_fn_sig(&func, TEST_PREFIX);
assert_eq!(sig, "public static Ast parse(String source) throws ParseError");
}
#[test]
fn test_render_csharp_fn_sig_basic() {
let func = make_function(
"convert",
vec![make_param("source", TypeRef::String, false)],
TypeRef::String,
false,
None,
);
let sig = render_csharp_fn_sig(&func, TEST_PREFIX);
assert_eq!(sig, "public static string Convert(string source)");
}
#[test]
fn test_render_csharp_fn_sig_async() {
let func = make_function("fetch", vec![], TypeRef::String, true, None);
let sig = render_csharp_fn_sig(&func, TEST_PREFIX);
assert_eq!(sig, "public static async Task<string> FetchAsync()");
}
#[test]
fn test_render_csharp_fn_sig_optional_param() {
let func = make_function(
"search",
vec![
make_param("query", TypeRef::String, false),
make_param("limit", TypeRef::Primitive(PrimitiveType::U32), true),
],
TypeRef::Vec(Box::new(TypeRef::String)),
false,
None,
);
let sig = render_csharp_fn_sig(&func, TEST_PREFIX);
assert_eq!(
sig,
"public static List<string> Search(string query, uint? limit = null)"
);
}
#[test]
fn test_render_csharp_fn_sig_complex_return_type() {
let func = make_function(
"get_mapping",
vec![],
TypeRef::Map(
Box::new(TypeRef::String),
Box::new(TypeRef::Primitive(PrimitiveType::I32)),
),
false,
None,
);
let sig = render_csharp_fn_sig(&func, TEST_PREFIX);
assert_eq!(sig, "public static Dictionary<string, int> GetMapping()");
}
#[test]
fn test_param_list_python_optional_uses_none_default() {
let func = make_function(
"run",
vec![
make_param("input", TypeRef::String, false),
make_param("config", TypeRef::Named("Config".to_string()), true),
],
TypeRef::Unit,
false,
None,
);
let sig = render_python_fn_sig(&func, TEST_PREFIX);
assert_eq!(sig, "def run(input: str, config: Config = None) -> None");
}
#[test]
fn test_param_list_node_optional_uses_question_mark() {
let func = make_function(
"run",
vec![
make_param("input", TypeRef::String, false),
make_param("config", TypeRef::Named("Config".to_string()), true),
],
TypeRef::Unit,
false,
None,
);
let sig = render_typescript_fn_sig(&func, TEST_PREFIX);
assert_eq!(sig, "function run(input: string, config?: Config): void");
}
#[test]
fn test_param_list_go_no_optional_syntax() {
let func = make_function(
"run",
vec![make_param("input", TypeRef::String, false)],
TypeRef::Unit,
false,
None,
);
let sig = render_go_fn_sig(&func, TEST_PREFIX);
assert_eq!(sig, "func Run(input string)");
}
#[test]
fn test_param_list_rust_string_params_use_refs() {
let func = make_function(
"process",
vec![
make_param("name", TypeRef::String, false),
make_param("initial", TypeRef::Char, false),
make_param("data", TypeRef::Bytes, false),
],
TypeRef::Unit,
false,
None,
);
let sig = render_rust_fn_sig(&func, TEST_PREFIX);
assert_eq!(sig, "pub fn process(name: &str, initial: &str, data: &[u8])");
}
#[test]
fn test_param_list_php_uses_dollar_prefix() {
let func = make_function(
"search",
vec![
make_param("query", TypeRef::String, false),
make_param("limit", TypeRef::Primitive(PrimitiveType::U32), true),
],
TypeRef::Vec(Box::new(TypeRef::String)),
false,
None,
);
let sig = render_php_fn_sig(&func, TEST_PREFIX);
assert_eq!(
sig,
"public static function search(string $query, ?int $limit = null): array<string>"
);
}
#[test]
fn test_render_kotlin_fn_sig_no_error_no_return() {
let func = make_function(
"run",
vec![make_param("input", TypeRef::String, false)],
TypeRef::Unit,
false,
None,
);
let sig = render_kotlin_fn_sig(&func, TEST_PREFIX);
assert_eq!(sig, "fun run(input: String)");
}
#[test]
fn test_render_kotlin_fn_sig_with_optional_and_return() {
let func = make_function(
"search",
vec![
make_param("query", TypeRef::String, false),
make_param("limit", TypeRef::Primitive(PrimitiveType::U32), true),
],
TypeRef::Vec(Box::new(TypeRef::String)),
false,
None,
);
let sig = render_kotlin_fn_sig(&func, TEST_PREFIX);
assert_eq!(sig, "fun search(query: String, limit: Int? = null): List<String>");
}
#[test]
fn test_render_kotlin_fn_sig_with_error_emits_throws_annotation() {
let func = make_function(
"convert",
vec![make_param("html", TypeRef::String, false)],
TypeRef::String,
false,
Some("ConversionError"),
);
let sig = render_kotlin_fn_sig(&func, TEST_PREFIX);
assert_eq!(
sig,
"@Throws(ConversionError::class)\nfun convert(html: String): String"
);
}
#[test]
fn test_render_swift_fn_sig_no_error_no_return() {
let func = make_function(
"run",
vec![make_param("input", TypeRef::String, false)],
TypeRef::Unit,
false,
None,
);
let sig = render_swift_fn_sig(&func, TEST_PREFIX);
assert_eq!(sig, "public static func run(input: String)");
}
#[test]
fn test_render_swift_fn_sig_with_optional_param_emits_nil_default() {
let func = make_function(
"search",
vec![
make_param("query", TypeRef::String, false),
make_param("limit", TypeRef::Primitive(PrimitiveType::U32), true),
],
TypeRef::Vec(Box::new(TypeRef::String)),
false,
None,
);
let sig = render_swift_fn_sig(&func, TEST_PREFIX);
assert_eq!(
sig,
"public static func search(query: String, limit: UInt32? = nil) -> [String]"
);
}
#[test]
fn test_render_swift_fn_sig_with_error_emits_throws() {
let func = make_function(
"convert",
vec![make_param("html", TypeRef::String, false)],
TypeRef::String,
false,
Some("ConversionError"),
);
let sig = render_swift_fn_sig(&func, TEST_PREFIX);
assert_eq!(sig, "public static func convert(html: String) throws -> String");
}
#[test]
fn test_render_dart_fn_sig_required_only() {
let func = make_function(
"run",
vec![make_param("input", TypeRef::String, false)],
TypeRef::Unit,
false,
None,
);
let sig = render_dart_fn_sig(&func, TEST_PREFIX);
assert_eq!(sig, "void run(String input)");
}
#[test]
fn test_render_dart_fn_sig_optional_param_uses_bracketed_positional() {
let func = make_function(
"search",
vec![
make_param("query", TypeRef::String, false),
make_param("limit", TypeRef::Primitive(PrimitiveType::U32), true),
],
TypeRef::Vec(Box::new(TypeRef::String)),
false,
None,
);
let sig = render_dart_fn_sig(&func, TEST_PREFIX);
assert_eq!(sig, "List<String> search(String query, [int? limit])");
}
#[test]
fn test_render_zig_fn_sig_no_error() {
let func = make_function(
"search",
vec![make_param("query", TypeRef::String, false)],
TypeRef::Primitive(PrimitiveType::U32),
false,
None,
);
let sig = render_zig_fn_sig(&func, TEST_PREFIX);
assert_eq!(sig, "pub fn search(query: [:0]const u8) u32");
}
#[test]
fn test_render_zig_fn_sig_with_error_emits_error_union() {
let func = make_function(
"convert",
vec![make_param("html", TypeRef::String, false)],
TypeRef::String,
false,
Some("ConversionError"),
);
let sig = render_zig_fn_sig(&func, TEST_PREFIX);
assert_eq!(sig, "pub fn convert(html: [:0]const u8) ConversionError![:0]const u8");
}
#[test]
fn test_render_zig_fn_sig_optional_param_prefixes_question_mark() {
let func = make_function(
"search",
vec![make_param("limit", TypeRef::Primitive(PrimitiveType::U32), true)],
TypeRef::Unit,
false,
None,
);
let sig = render_zig_fn_sig(&func, TEST_PREFIX);
assert_eq!(sig, "pub fn search(limit: ?u32) void");
}
#[test]
fn test_render_method_signature_kotlin_static_emits_jvmstatic() {
let method = make_method(
"default",
vec![],
TypeRef::Named("ParseOptions".into()),
false,
true,
None,
);
let sig = render_method_signature(&method, "ParseOptions", Language::Kotlin, TEST_PREFIX);
assert_eq!(sig, "@JvmStatic\nfun default(): ParseOptions");
}
#[test]
fn test_render_method_signature_swift_instance_with_throws() {
let method = make_method(
"apply_update",
vec![make_param("update", TypeRef::Named("ParseOptionsUpdate".into()), false)],
TypeRef::Unit,
false,
false,
Some("ConversionError"),
);
let sig = render_method_signature(&method, "ParseOptions", Language::Swift, TEST_PREFIX);
assert_eq!(sig, "public func applyUpdate(update: ParseOptionsUpdate) throws");
}
#[test]
fn test_render_method_signature_dart_instance_method() {
let method = make_method(
"classify_link",
vec![make_param("href", TypeRef::String, false)],
TypeRef::Named("LinkType".into()),
false,
false,
None,
);
let sig = render_method_signature(&method, "LinkMetadata", Language::Dart, TEST_PREFIX);
assert_eq!(sig, "LinkType classifyLink(String href)");
}
#[test]
fn test_render_method_signature_zig_instance_includes_self_receiver() {
let method = make_method(
"warnings",
vec![],
TypeRef::Vec(Box::new(TypeRef::String)),
false,
false,
None,
);
let sig = render_method_signature(&method, "ParseOutput", Language::Zig, TEST_PREFIX);
assert_eq!(sig, "pub fn warnings(self: *const ParseOutput) []const [:0]const u8");
}
#[test]
fn test_render_method_signature_zig_static_omits_self() {
let method = make_method(
"create",
vec![],
TypeRef::Named("ParseOptions".into()),
false,
true,
None,
);
let sig = render_method_signature(&method, "ParseOptions", Language::Zig, TEST_PREFIX);
assert_eq!(sig, "pub fn create() ParseOptions");
}
#[test]
fn test_render_method_signature_kotlin_android_shares_kotlin_renderer() {
let method = make_method(
"convert",
vec![make_param("html", TypeRef::String, false)],
TypeRef::String,
false,
true,
Some("ConversionError"),
);
let sig = render_method_signature(&method, "Converter", Language::KotlinAndroid, TEST_PREFIX);
assert_eq!(
sig,
"@Throws(ConversionError::class)\n@JvmStatic\nfun convert(html: String): String"
);
}
}