use python_ast::{parse, parse_enhanced, CodeGen, CodeGenContext, PythonOptions, SymbolTableScopes};
#[test]
fn unsupported_statement_returns_err() {
let result = parse("match x:\n case 1:\n pass", "match_test.py");
let err = result.expect_err("match statement should be rejected");
let message = err.to_string();
assert!(
message.contains("Match") || message.contains("not yet supported"),
"error should name the unsupported construct: {}",
message
);
}
#[test]
fn del_statement_returns_err_with_location() {
let result = parse("x = 1\ndel x", "del_test.py");
let err = result.expect_err("del statement should be rejected");
let message = err.to_string();
assert!(
message.contains("Delete"),
"error should name the construct: {}",
message
);
assert!(
message.contains("line 2"),
"error should carry the source line: {}",
message
);
}
#[test]
fn slice_subscript_parses() {
let result = parse("a[1:3]", "slice_test.py");
assert!(result.is_ok(), "slice subscript should parse");
}
#[test]
fn syntax_error_carries_structured_fields() {
let err = parse_enhanced("def broken(", "broken.py").expect_err("syntax error");
assert!(err.get_field("location").is_some(), "location field");
assert!(err.get_field("help").is_some(), "help field");
assert!(
err.downcast_ref::<python_ast::ParseError>().is_some(),
"typed payload should be recoverable"
);
}
#[test]
fn codegen_error_propagates_with_filename() {
let module = parse("q = a @ b", "matmul_test.py").expect("parses");
let symbols = module.clone().find_symbols(SymbolTableScopes::new());
let result = module.to_rust(
CodeGenContext::Module("matmul_test".to_string()),
PythonOptions::default(),
symbols,
);
let err = result.expect_err("MatMult codegen should fail");
let chain = python_ast::format_error_chain(err.as_ref());
assert!(
chain.contains("matmul_test.py"),
"error should name the source file: {}",
chain
);
}
#[test]
fn supported_module_still_compiles() {
let module = parse("def add(a, b):\n return a + b\n", "ok.py").expect("parses");
let symbols = module.clone().find_symbols(SymbolTableScopes::new());
let rust = module
.to_rust(
CodeGenContext::Module("ok".to_string()),
PythonOptions::default(),
symbols,
)
.expect("codegen succeeds");
assert!(rust.to_string().contains("fn add"));
}
#[test]
fn ann_assign_compiles_as_assignment() {
let module = parse("x: int = 5", "ann.py").expect("parses");
let symbols = module.clone().find_symbols(SymbolTableScopes::new());
let rust = module
.to_rust(
CodeGenContext::Module("ann".to_string()),
PythonOptions::default(),
symbols,
)
.expect("codegen succeeds");
let out = rust.to_string();
assert!(out.contains("pub static x : i64 = 5"), "generated: {}", out);
}
#[test]
fn bare_annotation_is_a_no_op() {
let module = parse("x: int", "ann2.py").expect("parses");
let symbols = module.clone().find_symbols(SymbolTableScopes::new());
let result = module.to_rust(
CodeGenContext::Module("ann2".to_string()),
PythonOptions::default(),
symbols,
);
assert!(result.is_ok(), "bare annotation should compile: {:?}", result.err());
}
#[test]
fn python_options_default_does_not_panic() {
let _ = PythonOptions::default();
}