mod async_wrappers;
mod module_init;
mod scan_args_defaults;
mod serde_bindings;
mod sync_wrappers;
pub(super) use async_wrappers::gen_async_function;
pub(super) use module_init::gen_module_init;
pub(super) use sync_wrappers::{gen_function, gen_magnus_unimplemented_body};
#[cfg(test)]
mod tests {
use super::*;
use crate::core::config::ResolvedCrateConfig;
use crate::core::config::new_config::NewAlefConfig;
use crate::core::ir::{FunctionDef, ParamDef, PrimitiveType, TypeRef};
fn resolved_one(toml: &str) -> ResolvedCrateConfig {
let cfg: NewAlefConfig = toml::from_str(toml).unwrap();
cfg.resolve().unwrap().remove(0)
}
fn make_config() -> ResolvedCrateConfig {
resolved_one(
r#"
[workspace]
languages = ["ruby"]
[[crates]]
name = "test-lib"
sources = ["src/lib.rs"]
[crates.ruby]
gem_name = "test_lib"
"#,
)
}
fn simple_func(name: &str, error: bool) -> FunctionDef {
FunctionDef {
name: name.to_string(),
rust_path: format!("test_lib::{name}"),
original_rust_path: String::new(),
params: vec![ParamDef {
name: "input".to_string(),
ty: TypeRef::String,
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: crate::core::ir::CoreWrapper::None,
}],
return_type: TypeRef::String,
is_async: false,
error_type: if error { Some("Error".to_string()) } else { None },
doc: String::new(),
cfg: None,
sanitized: false,
return_sanitized: false,
returns_ref: false,
returns_cow: false,
return_newtype_wrapper: None,
binding_excluded: false,
binding_exclusion_reason: None,
version: Default::default(),
}
}
#[test]
fn gen_function_emits_fn_name() {
let func = simple_func("process", false);
let mapper = crate::backends::magnus::type_map::MagnusMapper;
let api = crate::core::ir::ApiSurface {
crate_name: "test_lib".to_string(),
version: "0.1.0".to_string(),
types: vec![],
functions: vec![],
enums: vec![],
errors: vec![],
excluded_type_paths: ::std::collections::HashMap::new(),
excluded_trait_names: ::std::collections::HashSet::new(),
services: vec![],
handler_contracts: vec![],
unsupported_public_items: Vec::new(),
};
let code = gen_function(
&func,
&mapper,
&Default::default(),
&Default::default(),
"test_lib",
&api,
);
assert!(code.contains("fn process("), "must emit function name");
assert!(code.contains("input: String"), "must include typed param");
}
#[test]
fn gen_function_with_error_wraps_result() {
let func = simple_func("process", true);
let mapper = crate::backends::magnus::type_map::MagnusMapper;
let api = crate::core::ir::ApiSurface {
crate_name: "test_lib".to_string(),
version: "0.1.0".to_string(),
types: vec![],
functions: vec![],
enums: vec![],
errors: vec![],
excluded_type_paths: ::std::collections::HashMap::new(),
excluded_trait_names: ::std::collections::HashSet::new(),
services: vec![],
handler_contracts: vec![],
unsupported_public_items: Vec::new(),
};
let code = gen_function(
&func,
&mapper,
&Default::default(),
&Default::default(),
"test_lib",
&api,
);
assert!(code.contains("Result<"), "error function must return Result");
}
#[test]
fn gen_module_init_emits_magnus_init_attr() {
let config = make_config();
let api = crate::core::ir::ApiSurface {
crate_name: "test_lib".to_string(),
version: "0.1.0".to_string(),
types: vec![],
functions: vec![],
enums: vec![],
errors: vec![],
excluded_type_paths: ::std::collections::HashMap::new(),
excluded_trait_names: ::std::collections::HashSet::new(),
services: vec![],
handler_contracts: vec![],
unsupported_public_items: Vec::new(),
};
let code = gen_module_init(
"TestLib",
&api,
&config,
&Default::default(),
&Default::default(),
&Default::default(),
&[],
&Default::default(),
&[],
);
assert!(code.contains("#[magnus::init]"), "must emit #[magnus::init]");
assert!(code.contains("fn ruby_init(ruby: &Ruby)"), "must emit init fn");
assert!(code.contains("define_module(\"TestLib\")"), "must define the module");
}
#[test]
fn needs_variadic_arity_detects_optional_params() {
let required = ParamDef {
name: "x".to_string(),
ty: TypeRef::Primitive(PrimitiveType::U32),
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: crate::core::ir::CoreWrapper::None,
};
let optional = ParamDef {
optional: true,
..required.clone()
};
assert!(
!scan_args_defaults::needs_variadic_arity(std::slice::from_ref(&required)),
"required-only: no variadic"
);
assert!(
scan_args_defaults::needs_variadic_arity(&[optional]),
"optional param: needs variadic"
);
}
}