use std::borrow::Cow;
use alef_codegen::type_mapper::TypeMapper;
use alef_core::ir::{PrimitiveType, TypeRef};
pub struct JavaMapper;
impl TypeMapper for JavaMapper {
fn primitive(&self, prim: &PrimitiveType) -> Cow<'static, str> {
Cow::Borrowed(match prim {
PrimitiveType::Bool => "boolean",
PrimitiveType::U8 | PrimitiveType::I8 => "byte",
PrimitiveType::U16 | PrimitiveType::I16 => "short",
PrimitiveType::U32 | PrimitiveType::I32 => "int",
PrimitiveType::U64 | PrimitiveType::I64 | PrimitiveType::Usize | PrimitiveType::Isize => "long",
PrimitiveType::F32 => "float",
PrimitiveType::F64 => "double",
})
}
fn string(&self) -> Cow<'static, str> {
Cow::Borrowed("String")
}
fn bytes(&self) -> Cow<'static, str> {
Cow::Borrowed("byte[]")
}
fn path(&self) -> Cow<'static, str> {
Cow::Borrowed("java.nio.file.Path")
}
fn json(&self) -> Cow<'static, str> {
Cow::Borrowed("Object")
}
fn unit(&self) -> Cow<'static, str> {
Cow::Borrowed("void")
}
fn duration(&self) -> Cow<'static, str> {
Cow::Borrowed("Long")
}
fn optional(&self, inner: &str) -> String {
inner.to_string()
}
fn vec(&self, inner: &str) -> String {
format!("List<{inner}>")
}
fn map(&self, key: &str, value: &str) -> String {
format!("Map<{key}, {value}>")
}
fn map_type(&self, ty: &TypeRef) -> String {
match ty {
TypeRef::Primitive(p) => self.primitive(p).into_owned(),
TypeRef::String | TypeRef::Char => self.string().into_owned(),
TypeRef::Bytes => self.bytes().into_owned(),
TypeRef::Path => self.path().into_owned(),
TypeRef::Json => self.json().into_owned(),
TypeRef::Unit => self.unit().into_owned(),
TypeRef::Duration => self.duration().into_owned(),
TypeRef::Named(name) => self.named(name).into_owned(),
TypeRef::Optional(inner) => JavaBoxedMapper.map_type(inner),
TypeRef::Vec(inner) => format!("List<{}>", JavaBoxedMapper.map_type(inner)),
TypeRef::Map(k, v) => {
format!("Map<{}, {}>", JavaBoxedMapper.map_type(k), JavaBoxedMapper.map_type(v))
}
}
}
fn error_wrapper(&self) -> &str {
"CompletableFuture"
}
}
pub struct JavaBoxedMapper;
impl TypeMapper for JavaBoxedMapper {
fn primitive(&self, prim: &PrimitiveType) -> Cow<'static, str> {
Cow::Borrowed(match prim {
PrimitiveType::Bool => "Boolean",
PrimitiveType::U8 | PrimitiveType::I8 => "Byte",
PrimitiveType::U16 | PrimitiveType::I16 => "Short",
PrimitiveType::U32 | PrimitiveType::I32 => "Integer",
PrimitiveType::U64 | PrimitiveType::I64 | PrimitiveType::Usize | PrimitiveType::Isize => "Long",
PrimitiveType::F32 => "Float",
PrimitiveType::F64 => "Double",
})
}
fn string(&self) -> Cow<'static, str> {
Cow::Borrowed("String")
}
fn bytes(&self) -> Cow<'static, str> {
Cow::Borrowed("byte[]")
}
fn path(&self) -> Cow<'static, str> {
Cow::Borrowed("java.nio.file.Path")
}
fn json(&self) -> Cow<'static, str> {
Cow::Borrowed("Object")
}
fn unit(&self) -> Cow<'static, str> {
Cow::Borrowed("Void")
}
fn duration(&self) -> Cow<'static, str> {
Cow::Borrowed("Long")
}
fn optional(&self, inner: &str) -> String {
inner.to_string()
}
fn vec(&self, inner: &str) -> String {
format!("List<{inner}>")
}
fn map(&self, key: &str, value: &str) -> String {
format!("Map<{key}, {value}>")
}
fn error_wrapper(&self) -> &str {
"CompletableFuture"
}
}
pub fn java_type(ty: &TypeRef) -> Cow<'static, str> {
Cow::Owned(JavaMapper.map_type(ty))
}
pub fn java_boxed_type(ty: &TypeRef) -> Cow<'static, str> {
Cow::Owned(JavaBoxedMapper.map_type(ty))
}
pub fn java_return_type(ty: &TypeRef) -> Cow<'static, str> {
match ty {
TypeRef::Optional(inner) => Cow::Owned(format!("Optional<{}>", java_boxed_type(inner))),
other => java_type(other),
}
}
pub fn java_ffi_type(prim: &PrimitiveType) -> &'static str {
match prim {
PrimitiveType::Bool => "ValueLayout.JAVA_BOOLEAN",
PrimitiveType::U8 | PrimitiveType::I8 => "ValueLayout.JAVA_BYTE",
PrimitiveType::U16 | PrimitiveType::I16 => "ValueLayout.JAVA_SHORT",
PrimitiveType::U32 | PrimitiveType::I32 => "ValueLayout.JAVA_INT",
PrimitiveType::U64 | PrimitiveType::I64 | PrimitiveType::Usize | PrimitiveType::Isize => {
"ValueLayout.JAVA_LONG"
}
PrimitiveType::F32 => "ValueLayout.JAVA_FLOAT",
PrimitiveType::F64 => "ValueLayout.JAVA_DOUBLE",
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn java_mapper_primitives() {
let m = JavaMapper;
assert_eq!(m.primitive(&PrimitiveType::Bool), "boolean");
assert_eq!(m.primitive(&PrimitiveType::U8), "byte");
assert_eq!(m.primitive(&PrimitiveType::I8), "byte");
assert_eq!(m.primitive(&PrimitiveType::U16), "short");
assert_eq!(m.primitive(&PrimitiveType::I16), "short");
assert_eq!(m.primitive(&PrimitiveType::U32), "int");
assert_eq!(m.primitive(&PrimitiveType::I32), "int");
assert_eq!(m.primitive(&PrimitiveType::U64), "long");
assert_eq!(m.primitive(&PrimitiveType::I64), "long");
assert_eq!(m.primitive(&PrimitiveType::Usize), "long");
assert_eq!(m.primitive(&PrimitiveType::Isize), "long");
assert_eq!(m.primitive(&PrimitiveType::F32), "float");
assert_eq!(m.primitive(&PrimitiveType::F64), "double");
}
#[test]
fn java_boxed_mapper_primitives() {
let m = JavaBoxedMapper;
assert_eq!(m.primitive(&PrimitiveType::Bool), "Boolean");
assert_eq!(m.primitive(&PrimitiveType::U8), "Byte");
assert_eq!(m.primitive(&PrimitiveType::U32), "Integer");
assert_eq!(m.primitive(&PrimitiveType::U64), "Long");
assert_eq!(m.primitive(&PrimitiveType::F32), "Float");
assert_eq!(m.primitive(&PrimitiveType::F64), "Double");
}
#[test]
fn java_type_string() {
assert_eq!(java_type(&TypeRef::String), "String");
assert_eq!(java_type(&TypeRef::Char), "String");
}
#[test]
fn java_type_bytes() {
assert_eq!(java_type(&TypeRef::Bytes), "byte[]");
}
#[test]
fn java_type_path() {
assert_eq!(java_type(&TypeRef::Path), "java.nio.file.Path");
}
#[test]
fn java_type_json() {
assert_eq!(java_type(&TypeRef::Json), "Object");
}
#[test]
fn java_type_unit() {
assert_eq!(java_type(&TypeRef::Unit), "void");
}
#[test]
fn java_type_duration() {
assert_eq!(java_type(&TypeRef::Duration), "Long");
}
#[test]
fn java_type_vec_uses_boxed_inner() {
assert_eq!(
java_type(&TypeRef::Vec(Box::new(TypeRef::Primitive(PrimitiveType::I32)))),
"List<Integer>"
);
}
#[test]
fn java_type_map_uses_boxed_keys_and_values() {
assert_eq!(
java_type(&TypeRef::Map(
Box::new(TypeRef::String),
Box::new(TypeRef::Primitive(PrimitiveType::U32))
)),
"Map<String, Integer>"
);
}
#[test]
fn java_type_unwraps_optional() {
assert_eq!(java_type(&TypeRef::Optional(Box::new(TypeRef::String))), "String");
}
#[test]
fn java_boxed_type_unit_is_void_class() {
assert_eq!(java_boxed_type(&TypeRef::Unit), "Void");
}
#[test]
fn java_return_type_wraps_optional_string() {
let ty = TypeRef::Optional(Box::new(TypeRef::String));
assert_eq!(java_return_type(&ty), "Optional<String>");
}
#[test]
fn java_return_type_wraps_optional_named() {
let ty = TypeRef::Optional(Box::new(TypeRef::Named("EmbeddingPreset".to_string())));
assert_eq!(java_return_type(&ty), "Optional<EmbeddingPreset>");
}
#[test]
fn java_return_type_wraps_optional_vec() {
let ty = TypeRef::Optional(Box::new(TypeRef::Vec(Box::new(TypeRef::String))));
assert_eq!(java_return_type(&ty), "Optional<List<String>>");
}
#[test]
fn java_return_type_preserves_non_optional() {
assert_eq!(java_return_type(&TypeRef::String), "String");
}
#[test]
fn java_return_type_preserves_vec() {
let ty = TypeRef::Vec(Box::new(TypeRef::String));
assert_eq!(java_return_type(&ty), "List<String>");
}
}