use depyler_core::DepylerPipeline;
#[test]
fn test_simple_classmethod_factory() {
let python = r#"
class Person:
def __init__(self, name: str):
self.name = name
@classmethod
def create_john(cls):
return cls("John")
"#;
let pipeline = DepylerPipeline::new();
let result = pipeline.transpile(python);
assert!(
result.is_ok(),
"Transpilation failed: {:?}",
result.as_ref().err()
);
let rust_code = result.unwrap();
assert!(
rust_code.contains("struct Person"),
"Should have Person struct.\nGot:\n{}",
rust_code
);
assert!(
rust_code.contains("fn create_john"),
"Should have create_john function.\nGot:\n{}",
rust_code
);
let person_impl_start = rust_code.find("impl Person").unwrap_or(0);
let person_impl = &rust_code[person_impl_start..];
let person_impl_end = person_impl.find("\n}\n").unwrap_or(person_impl.len()) + 2;
let person_impl_block = &person_impl[..person_impl_end];
let create_john_start = person_impl_block.find("fn create_john").unwrap_or(0);
let create_john_section = &person_impl_block[create_john_start..];
let has_self_in_create_john = create_john_section.contains("&self)") || create_john_section.contains("cls:");
assert!(
!has_self_in_create_john,
"Classmethod create_john should not have &self or cls parameter.\nGot:\n{}",
create_john_section
);
let has_constructor = rust_code.contains("Self::new")
|| rust_code.contains("Person::new")
|| rust_code.contains("Self {");
assert!(
has_constructor,
"Should use Self::new or constructor.\nGot:\n{}",
rust_code
);
let has_cls_var = rust_code.contains("cls(");
assert!(
!has_cls_var,
"Should not have undefined cls variable.\nGot:\n{}",
rust_code
);
}
#[test]
fn test_classmethod_with_parameters() {
let python = r#"
class User:
def __init__(self, name: str, age: int):
self.name = name
self.age = age
@classmethod
def create(cls, name: str, age: int):
return cls(name, age)
"#;
let pipeline = DepylerPipeline::new();
let result = pipeline.transpile(python);
assert!(
result.is_ok(),
"Transpilation failed: {:?}",
result.as_ref().err()
);
let rust_code = result.unwrap();
assert!(
rust_code.contains("fn create"),
"Should have create function.\nGot:\n{}",
rust_code
);
assert!(
rust_code.contains("name") && rust_code.contains("age"),
"Should have name and age parameters.\nGot:\n{}",
rust_code
);
let has_cls_param = rust_code.contains("cls:");
assert!(
!has_cls_param,
"Should not have cls parameter.\nGot:\n{}",
rust_code
);
let has_constructor_call = rust_code.contains("Self::new") || rust_code.contains("User::new");
assert!(
has_constructor_call,
"Should call constructor.\nGot:\n{}",
rust_code
);
}
#[test]
fn test_classmethod_accessing_class_constants() {
let python = r#"
class Config:
DEFAULT_NAME: str = "Unknown"
def __init__(self, name: str):
self.name = name
@classmethod
def create_default(cls):
return cls(cls.DEFAULT_NAME)
"#;
let pipeline = DepylerPipeline::new();
let result = pipeline.transpile(python);
assert!(
result.is_ok(),
"Transpilation failed: {:?}",
result.as_ref().err()
);
let rust_code = result.unwrap();
assert!(
rust_code.contains("fn create_default"),
"Should have create_default function.\nGot:\n{}",
rust_code
);
let has_const_ref = rust_code.contains("DEFAULT_NAME") || rust_code.contains("default_name");
assert!(
has_const_ref,
"Should reference DEFAULT_NAME.\nGot:\n{}",
rust_code
);
let has_qualified_const = rust_code.contains("Self::DEFAULT_NAME")
|| rust_code.contains("Config::DEFAULT_NAME")
|| rust_code.contains("Self::default_name")
|| rust_code.contains("Config::default_name");
assert!(
has_qualified_const,
"Should use qualified constant access.\nGot:\n{}",
rust_code
);
}
#[test]
fn test_multiple_classmethods() {
let python = r#"
class Point:
def __init__(self, x: int, y: int):
self.x = x
self.y = y
@classmethod
def origin(cls):
return cls(0, 0)
@classmethod
def unit_x(cls):
return cls(1, 0)
@classmethod
def unit_y(cls):
return cls(0, 1)
"#;
let pipeline = DepylerPipeline::new();
let result = pipeline.transpile(python);
assert!(
result.is_ok(),
"Transpilation failed: {:?}",
result.as_ref().err()
);
let rust_code = result.unwrap();
assert!(
rust_code.contains("fn origin"),
"Should have origin function.\nGot:\n{}",
rust_code
);
assert!(
rust_code.contains("fn unit_x"),
"Should have unit_x function.\nGot:\n{}",
rust_code
);
assert!(
rust_code.contains("fn unit_y"),
"Should have unit_y function.\nGot:\n{}",
rust_code
);
let cls_count = rust_code.matches("cls:").count();
assert_eq!(
cls_count, 0,
"No function should have cls parameter.\nGot:\n{}",
rust_code
);
}
#[test]
fn test_classmethod_calling_another_classmethod() {
let python = r#"
class Rectangle:
def __init__(self, width: int, height: int):
self.width = width
self.height = height
@classmethod
def square(cls, size: int):
return cls(size, size)
@classmethod
def unit_square(cls):
return cls.square(1)
"#;
let pipeline = DepylerPipeline::new();
let result = pipeline.transpile(python);
assert!(
result.is_ok(),
"Transpilation failed: {:?}",
result.as_ref().err()
);
let rust_code = result.unwrap();
assert!(
rust_code.contains("fn square"),
"Should have square function.\nGot:\n{}",
rust_code
);
assert!(
rust_code.contains("fn unit_square"),
"Should have unit_square function.\nGot:\n{}",
rust_code
);
let calls_square =
rust_code.contains("Self::square") || rust_code.contains("Rectangle::square");
assert!(
calls_square,
"Should call square classmethod.\nGot:\n{}",
rust_code
);
}
#[test]
fn test_instance_method_calling_classmethod() {
let python = r#"
class Counter:
def __init__(self, value: int):
self.value = value
@classmethod
def zero(cls):
return cls(0)
def reset(self):
return Counter.zero()
"#;
let pipeline = DepylerPipeline::new();
let result = pipeline.transpile(python);
assert!(
result.is_ok(),
"Transpilation failed: {:?}",
result.as_ref().err()
);
let rust_code = result.unwrap();
assert!(
rust_code.contains("fn zero"),
"Should have zero function.\nGot:\n{}",
rust_code
);
assert!(
rust_code.contains("fn reset"),
"Should have reset method.\nGot:\n{}",
rust_code
);
let has_self = rust_code.contains("&self") || rust_code.contains("& self");
assert!(has_self, "reset should have &self.\nGot:\n{}", rust_code);
let calls_zero = rust_code.contains("Counter::zero") || rust_code.contains("Self::zero");
assert!(
calls_zero,
"Should call zero classmethod.\nGot:\n{}",
rust_code
);
}
#[test]
fn test_classmethod_with_type_annotations() {
let python = r#"
class Temperature:
def __init__(self, celsius: float):
self.celsius = celsius
@classmethod
def from_fahrenheit(cls, fahrenheit: float) -> Temperature:
celsius = (fahrenheit - 32.0) * 5.0 / 9.0
return cls(celsius)
"#;
let pipeline = DepylerPipeline::new();
let result = pipeline.transpile(python);
assert!(
result.is_ok(),
"Transpilation failed: {:?}",
result.as_ref().err()
);
let rust_code = result.unwrap();
assert!(
rust_code.contains("fn from_fahrenheit"),
"Should have from_fahrenheit function.\nGot:\n{}",
rust_code
);
let has_float =
rust_code.contains("f32") || rust_code.contains("f64") || rust_code.contains("fahrenheit");
assert!(
has_float,
"Should have float parameter.\nGot:\n{}",
rust_code
);
let has_return_type = rust_code.contains("-> Self")
|| rust_code.contains("-> Temperature")
|| rust_code.contains("Self");
assert!(
has_return_type,
"Should return Self or Temperature.\nGot:\n{}",
rust_code
);
}
#[test]
fn test_classmethod_as_alternative_constructor() {
let python = r#"
class Date:
def __init__(self, year: int, month: int, day: int):
self.year = year
self.month = month
self.day = day
@classmethod
def from_string(cls, date_str: str):
# Simplified parsing
return cls(2024, 1, 1)
@classmethod
def today(cls):
return cls(2024, 10, 9)
"#;
let pipeline = DepylerPipeline::new();
let result = pipeline.transpile(python);
assert!(
result.is_ok(),
"Transpilation failed: {:?}",
result.as_ref().err()
);
let rust_code = result.unwrap();
assert!(
rust_code.contains("fn from_string"),
"Should have from_string function.\nGot:\n{}",
rust_code
);
assert!(
rust_code.contains("fn today"),
"Should have today function.\nGot:\n{}",
rust_code
);
let has_constructors = rust_code.contains("Self::new")
|| rust_code.contains("Date::new")
|| rust_code.contains("Self {");
assert!(
has_constructors,
"Should have constructor calls.\nGot:\n{}",
rust_code
);
}
#[test]
fn test_mix_class_static_instance_methods() {
let python = r#"
class Calculator:
DEFAULT_OFFSET: int = 10
def __init__(self, offset: int):
self.offset = offset
@staticmethod
def add(a: int, b: int) -> int:
return a + b
@classmethod
def with_default_offset(cls):
return cls(cls.DEFAULT_OFFSET)
def add_with_offset(self, a: int, b: int) -> int:
return Calculator.add(a, b) + self.offset
"#;
let pipeline = DepylerPipeline::new();
let result = pipeline.transpile(python);
assert!(
result.is_ok(),
"Transpilation failed: {:?}",
result.as_ref().err()
);
let rust_code = result.unwrap();
assert!(
rust_code.contains("fn add"),
"Should have add static function.\nGot:\n{}",
rust_code
);
assert!(
rust_code.contains("fn with_default_offset"),
"Should have with_default_offset classmethod.\nGot:\n{}",
rust_code
);
assert!(
rust_code.contains("fn add_with_offset"),
"Should have add_with_offset instance method.\nGot:\n{}",
rust_code
);
let has_self = rust_code.contains("&self") || rust_code.contains("& self");
assert!(
has_self,
"add_with_offset should have &self.\nGot:\n{}",
rust_code
);
}
#[test]
fn test_classmethod_with_default_parameters() {
let python = r#"
class Person:
def __init__(self, name: str, age: int):
self.name = name
self.age = age
@classmethod
def create_anonymous(cls, age: int = 18):
return cls("Anonymous", age)
"#;
let pipeline = DepylerPipeline::new();
let result = pipeline.transpile(python);
assert!(
result.is_ok(),
"Transpilation failed: {:?}",
result.as_ref().err()
);
let rust_code = result.unwrap();
assert!(
rust_code.contains("fn create_anonymous"),
"Should have create_anonymous function.\nGot:\n{}",
rust_code
);
let has_age_param = rust_code.contains("age");
assert!(
has_age_param,
"Should have age parameter.\nGot:\n{}",
rust_code
);
let has_constructor = rust_code.contains("Self::new")
|| rust_code.contains("Person::new")
|| rust_code.contains("\"Anonymous\"");
assert!(
has_constructor,
"Should construct with 'Anonymous'.\nGot:\n{}",
rust_code
);
}