use super::helpers::try_parse_path;
use super::ToSyn;
use crate::pure::ast::PureFile;
fn assert_valid_rust(source: &str) -> String {
let pure = PureFile::from_source(source).expect("Input should parse into PureFile");
let output = pure
.to_source()
.expect("to_source should succeed for valid PureFile");
syn::parse_str::<syn::File>(&output).unwrap_or_else(|_| {
panic!(
"Generated code must be valid Rust syntax.\nInput:\n{}\nOutput:\n{}",
source, output
)
});
output
}
fn assert_compiles(source: &str) -> String {
use std::io::Write;
use std::process::Command;
let output = assert_valid_rust(source);
let temp_dir = tempfile::tempdir().expect("Failed to create temp dir");
let src_dir = temp_dir.path().join("src");
std::fs::create_dir_all(&src_dir).expect("Failed to create src dir");
let lib_path = src_dir.join("lib.rs");
let mut file = std::fs::File::create(&lib_path).expect("Failed to create lib.rs");
writeln!(file, "#![allow(unused, dead_code)]").unwrap();
write!(file, "{}", output).expect("Failed to write source");
let cargo_toml = temp_dir.path().join("Cargo.toml");
std::fs::write(
&cargo_toml,
r#"[package]
name = "roundtrip_test"
version = "0.1.0"
edition = "2021"
"#,
)
.expect("Failed to write Cargo.toml");
let result = Command::new("cargo")
.args(["check", "--message-format=short"])
.current_dir(temp_dir.path())
.output()
.expect("Failed to run cargo check");
if !result.status.success() {
let stderr = String::from_utf8_lossy(&result.stderr);
panic!(
"Generated code failed to compile.\n\
=== Input ===\n{}\n\
=== Generated ===\n{}\n\
=== Errors ===\n{}",
source, output, stderr
);
}
output
}
#[test]
fn test_roundtrip_simple_fn() {
let source = "fn main() {}";
let output = assert_valid_rust(source);
assert!(output.contains("fn main()"));
}
#[test]
fn test_roundtrip_struct() {
let source = r#"
struct Point {
x: i32,
y: i32,
}
"#;
let output = assert_valid_rust(source);
assert!(output.contains("struct Point"));
assert!(output.contains("x: i32"));
assert!(output.contains("y: i32"));
}
#[test]
fn test_roundtrip_impl() {
let source = r#"
impl Point {
fn new(x: i32, y: i32) -> Self {
Self { x, y }
}
}
"#;
let output = assert_valid_rust(source);
assert!(output.contains("impl Point"));
assert!(output.contains("fn new"));
}
#[test]
fn test_roundtrip_use() {
let source = "use std::io;";
let output = assert_valid_rust(source);
assert!(output.contains("use std::io"));
}
#[test]
fn test_roundtrip_complex() {
let source = r#"
use std::collections::HashMap;
struct Config {
name: String,
value: i32,
}
impl Config {
fn new(name: &str, value: i32) -> Self {
Self {
name: name.to_string(),
value,
}
}
fn get_value(&self) -> i32 {
self.value
}
}
fn main() {
let config = Config::new("test", 42);
let result = config.get_value();
println!("{}", result);
}
"#;
let output = assert_valid_rust(source);
assert!(output.contains("use std::collections::HashMap"));
assert!(output.contains("struct Config"));
assert!(output.contains("impl Config"));
assert!(output.contains("fn main()"));
}
#[test]
fn test_roundtrip_path_pattern_option() {
let source = r#"
fn handle_option(x: Option<i32>) {
match x {
Some(v) => println!("{}", v),
None => println!("none"),
}
}
"#;
let output = assert_valid_rust(source);
assert!(output.contains("Some"));
assert!(output.contains("None"));
}
#[test]
fn test_roundtrip_qualified_path_pattern() {
let source = r#"
enum Status {
Ok,
Err,
}
fn check(s: Status) {
match s {
Status::Ok => println!("ok"),
Status::Err => println!("err"),
}
}
"#;
let output = assert_valid_rust(source);
assert!(output.contains("Status::Ok"));
assert!(output.contains("Status::Err"));
}
#[test]
fn test_roundtrip_tuple_struct_pattern() {
let source = r#"
fn extract(opt: Option<i32>) {
if let Some(v) = opt {
println!("{}", v);
}
}
"#;
let output = assert_valid_rust(source);
assert!(output.contains("Some"));
}
#[test]
fn test_roundtrip_tuple_struct_multi_field() {
let source = r#"
struct Point(i32, i32);
fn extract(p: Point) {
let Point(x, y) = p;
println!("{} {}", x, y);
}
"#;
let output = assert_valid_rust(source);
assert!(output.contains("Point"));
}
#[test]
fn test_roundtrip_raw_identifier_fn() {
let source = r#"
fn r#async() -> i32 {
42
}
"#;
let output = assert_valid_rust(source);
assert!(output.contains("r#async") || output.contains("async"));
}
#[test]
fn test_roundtrip_raw_identifier_variable() {
let source = r#"
fn use_raw() {
let r#type = 1;
let r#match = 2;
println!("{} {}", r#type, r#match);
}
"#;
let output = assert_valid_rust(source);
assert!(output.contains("fn use_raw"));
}
#[test]
fn test_roundtrip_range_pattern() {
let source = r#"
fn check_range(n: i32) {
match n {
0..=10 => println!("small"),
_ => println!("large"),
}
}
"#;
let output = assert_valid_rust(source);
assert!(output.contains("0"));
assert!(output.contains("10"));
}
#[test]
fn test_roundtrip_slice_pattern() {
let source = r#"
fn check_slice(arr: &[i32]) {
match arr {
[] => println!("empty"),
[first] => println!("{}", first),
[first, rest @ ..] => println!("{} and more", first),
}
}
"#;
let output = assert_valid_rust(source);
assert!(output.contains("first"));
}
#[test]
fn test_parse_path_with_generics() {
let path = try_parse_path("Vec<i32>").unwrap();
assert_eq!(path.segments.len(), 1);
assert_eq!(path.segments[0].ident.to_string(), "Vec");
let path = try_parse_path("Vec<Option<String>>").unwrap();
assert_eq!(path.segments.len(), 1);
let path = try_parse_path("std::vec::Vec<i32>").unwrap();
assert_eq!(path.segments.len(), 3);
}
#[test]
fn test_roundtrip_assignment_expression() {
let source = r#"
fn assign_test() {
let mut x = 0;
x = 42;
}
"#;
let output = assert_valid_rust(source);
assert!(output.contains("x = 42"));
}
#[test]
fn test_roundtrip_derive_attribute() {
let source = r#"
#[derive(Debug, Clone)]
struct Foo {
x: i32,
}
"#;
let output = assert_valid_rust(source);
assert!(output.contains("#[derive(Debug, Clone)]"));
}
#[test]
fn test_roundtrip_generic_type() {
let source = r#"
fn generic_fn() -> Vec<String> {
Vec::new()
}
"#;
let output = assert_valid_rust(source);
assert!(output.contains("Vec<String>") || output.contains("Vec< String >"));
}
#[test]
fn test_roundtrip_struct_with_generic_field() {
let source = r#"
struct Container {
items: Vec<LintCategory>,
}
"#;
let output = assert_valid_rust(source);
assert!(output.contains("Vec"));
assert!(output.contains("LintCategory"));
}
#[test]
fn test_roundtrip_struct_pattern_with_rest() {
let source = r#"
struct Point { x: i32, y: i32, z: i32 }
fn use_pattern(p: Point) {
let Point { x, .. } = p;
println!("{}", x);
}
"#;
let output = assert_valid_rust(source);
assert!(
output.contains(".."),
"Output should contain '..': {}",
output
);
}
#[test]
fn test_roundtrip_rest_only_struct_pattern() {
let source = r#"
enum Foo {
Bar { x: i32, y: i32 },
Baz,
}
fn match_foo(f: Foo) {
match f {
Foo::Bar { .. } => println!("bar"),
Foo::Baz => println!("baz"),
}
}
"#;
let output = assert_valid_rust(source);
assert!(
output.contains("Bar { .. }") || output.contains("Bar {..}"),
"Output should contain 'Bar {{ .. }}', got: {}",
output
);
assert!(
!output.contains("Bar()"),
"Output should NOT contain 'Bar()', got: {}",
output
);
}
#[test]
fn test_roundtrip_turbofish_method_call() {
let source = r#"
fn turbofish_test() {
let v = [1, 2, 3].iter().collect::<Vec<_>>();
let s = "hello".parse::<i32>();
}
"#;
let output = assert_valid_rust(source);
assert!(
output.contains("collect::<") || output.contains("collect ::< "),
"Output should contain turbofish 'collect::<': {}",
output
);
assert!(
output.contains("parse::<") || output.contains("parse ::< "),
"Output should contain turbofish 'parse::<': {}",
output
);
}
#[test]
fn test_roundtrip_range_expression() {
let source = r#"
fn range_test() {
let a = 0..10;
let b = 0..=10;
let c = ..10;
let d = 0..;
for i in 0..5 {
println!("{}", i);
}
}
"#;
let output = assert_valid_rust(source);
assert!(
output.contains("0..10") || output.contains("0 .. 10"),
"Output should contain '0..10': {}",
output
);
assert!(
output.contains("0..=10") || output.contains("0 ..= 10"),
"Output should contain '0..=10': {}",
output
);
}
#[test]
fn test_roundtrip_cast_expression() {
let source = r#"
fn cast_test() {
let x = 42u8 as i32;
let y = 3.14f64 as i64;
let ptr = &x as *const i32;
}
"#;
let output = assert_valid_rust(source);
assert!(
output.contains("as i32"),
"Output should contain 'as i32': {}",
output
);
assert!(
output.contains("as i64"),
"Output should contain 'as i64': {}",
output
);
}
#[test]
fn test_roundtrip_if_let_expression() {
let source = r#"
fn if_let_test(opt: Option<i32>) {
if let Some(x) = opt {
println!("{}", x);
}
}
"#;
let output = assert_valid_rust(source);
assert!(
output.contains("if let Some"),
"Output should contain 'if let Some': {}",
output
);
}
#[test]
fn test_roundtrip_while_let_expression() {
let source = r#"
fn while_let_test(mut iter: std::vec::IntoIter<i32>) {
while let Some(x) = iter.next() {
println!("{}", x);
}
}
"#;
let output = assert_valid_rust(source);
assert!(
output.contains("while let Some"),
"Output should contain 'while let Some': {}",
output
);
}
#[test]
fn test_roundtrip_move_closure() {
let source = r#"
fn move_closure_test() {
let x = 42;
let f = move || x + 1;
}
"#;
let output = assert_valid_rust(source);
assert!(
output.contains("move"),
"Output should contain 'move': {}",
output
);
}
#[test]
fn test_roundtrip_async_closure() {
let source = r#"
fn async_closure_test() {
let f = async || 42;
}
"#;
let output = assert_valid_rust(source);
assert!(
output.contains("async"),
"Output should contain 'async': {}",
output
);
}
#[test]
fn test_roundtrip_typed_closure() {
let source = r#"
fn typed_closure_test() {
let f = |x: i32, y: String| -> bool { x > 0 };
}
"#;
let output = assert_valid_rust(source);
assert!(
output.contains("i32"),
"Output should contain param type 'i32': {}",
output
);
assert!(
output.contains("String"),
"Output should contain param type 'String': {}",
output
);
assert!(
output.contains("bool"),
"Output should contain return type 'bool': {}",
output
);
}
#[test]
fn test_roundtrip_async_block() {
let source = r#"
fn async_block_test() {
let fut = async {
42
};
let fut_move = async move {
42
};
}
"#;
let output = assert_valid_rust(source);
assert!(
output.contains("async"),
"Output should contain 'async': {}",
output
);
}
#[test]
fn test_roundtrip_unsafe_block() {
let source = r#"
fn unsafe_block_test() {
let x = unsafe {
std::mem::zeroed::<i32>()
};
}
"#;
let output = assert_valid_rust(source);
assert!(
output.contains("unsafe"),
"Output should contain 'unsafe': {}",
output
);
}
#[test]
fn test_roundtrip_array_repeat() {
let source = r#"
fn array_repeat_test() {
let zeros = [0; 10];
let ones = [1u8; 256];
}
"#;
let output = assert_valid_rust(source);
assert!(
output.contains("[0; 10]") || output.contains("[0 ; 10]"),
"Output should contain '[0; 10]': {}",
output
);
}
#[test]
fn test_add_derive_via_pure_attribute() {
use crate::pure::ast::{
PureAttrMeta, PureAttribute, PureField, PureFields, PureGenerics, PureStruct, PureType,
PureVis,
};
let mut s = PureStruct {
name: "Foo".to_string(),
vis: PureVis::Public,
generics: PureGenerics::default(),
attrs: vec![],
fields: PureFields::Named(vec![PureField {
name: "x".to_string(),
vis: PureVis::Private,
ty: PureType::Path("i32".to_string()),
attrs: vec![],
}]),
};
s.attrs.push(PureAttribute {
path: "derive".to_string(),
meta: PureAttrMeta::List("Debug, Clone".to_string()),
is_inner: false,
});
let syn_struct = s.to_syn().unwrap();
let output = prettyplease::unparse(&syn::File {
shebang: None,
attrs: vec![],
items: vec![syn::Item::Struct(syn_struct)],
});
assert!(
output.contains("#[derive(Debug, Clone)]") || output.contains("#[derive(Debug , Clone)]"),
"Output should contain '#[derive(Debug, Clone)]': {}",
output
);
}
#[test]
#[ignore = "slow: requires cargo check"]
fn compile_check_struct_with_impl() {
let source = r#"
#[derive(Debug, Clone)]
pub struct Point {
pub x: i32,
pub y: i32,
}
impl Point {
pub fn new(x: i32, y: i32) -> Self {
Self { x, y }
}
pub fn distance(&self, other: &Point) -> f64 {
let dx = (self.x - other.x) as f64;
let dy = (self.y - other.y) as f64;
(dx * dx + dy * dy).sqrt()
}
}
"#;
assert_compiles(source);
}
#[test]
#[ignore = "slow: requires cargo check"]
fn compile_check_enum_with_match() {
let source = r#"
#[derive(Debug, Clone, PartialEq)]
pub enum Status {
Pending,
InProgress { progress: u8 },
Done,
}
impl Status {
pub fn is_complete(&self) -> bool {
matches!(self, Status::Done)
}
pub fn description(&self) -> &'static str {
match self {
Status::Pending => "Not started",
Status::InProgress { progress } if *progress > 50 => "More than half done",
Status::InProgress { .. } => "In progress",
Status::Done => "Complete",
}
}
}
"#;
assert_compiles(source);
}
#[test]
#[ignore = "slow: requires cargo check"]
fn compile_check_generics_and_traits() {
let source = r#"
use std::fmt::Display;
pub trait Printable {
fn print(&self);
}
pub struct Container<T> {
value: T,
}
impl<T: Display> Container<T> {
pub fn new(value: T) -> Self {
Self { value }
}
}
impl<T: Display> Printable for Container<T> {
fn print(&self) {
println!("{}", self.value);
}
}
"#;
assert_compiles(source);
}
#[test]
#[ignore = "slow: requires cargo check"]
fn compile_check_complex_expressions() {
let source = r#"
pub fn process_data(data: Vec<i32>) -> Vec<i32> {
data.iter()
.filter(|&x| *x > 0)
.map(|x| x * 2)
.collect::<Vec<_>>()
}
pub fn with_closure() -> impl Fn(i32) -> i32 {
let multiplier = 3;
move |x| x * multiplier
}
"#;
assert_compiles(source);
}
#[test]
#[ignore = "slow: requires cargo check"]
fn compile_check_full_module() {
let source = r#"
//! A simple todo list module.
use std::collections::HashMap;
/// A single todo item.
#[derive(Debug, Clone)]
pub struct TodoItem {
pub id: u32,
pub title: String,
pub completed: bool,
}
impl TodoItem {
pub fn new(id: u32, title: impl Into<String>) -> Self {
Self {
id,
title: title.into(),
completed: false,
}
}
pub fn complete(&mut self) {
self.completed = true;
}
}
/// A collection of todo items.
#[derive(Debug, Default)]
pub struct TodoList {
items: HashMap<u32, TodoItem>,
next_id: u32,
}
impl TodoList {
pub fn new() -> Self {
Self::default()
}
pub fn add(&mut self, title: impl Into<String>) -> u32 {
let id = self.next_id;
self.next_id += 1;
self.items.insert(id, TodoItem::new(id, title));
id
}
pub fn get(&self, id: u32) -> Option<&TodoItem> {
self.items.get(&id)
}
pub fn complete(&mut self, id: u32) -> bool {
if let Some(item) = self.items.get_mut(&id) {
item.complete();
true
} else {
false
}
}
pub fn pending(&self) -> impl Iterator<Item = &TodoItem> {
self.items.values().filter(|item| !item.completed)
}
}
"#;
assert_compiles(source);
}
#[test]
fn test_roundtrip_let_type_annotation() {
let source = r#"
fn test_type_annotations() {
let x: i32 = 42;
let args: Vec<String> = vec![];
let map: std::collections::HashMap<String, i32> = Default::default();
}
"#;
let output = assert_valid_rust(source);
assert!(
output.contains(": i32"),
"Output should preserve ': i32' type annotation: {}",
output
);
assert!(
output.contains(": Vec<String>"),
"Output should preserve ': Vec<String>' type annotation: {}",
output
);
assert!(
output.contains("HashMap<String, i32>"),
"Output should preserve 'HashMap<String, i32>' type annotation: {}",
output
);
}
#[test]
fn test_roundtrip_let_mut_type_annotation() {
let source = r#"
fn test_mut_type_annotations() {
let mut count: usize = 0;
let mut buffer: Vec<u8> = Vec::new();
}
"#;
let output = assert_valid_rust(source);
assert!(
output.contains("mut count: usize"),
"Output should preserve 'mut count: usize': {}",
output
);
assert!(
output.contains("mut buffer: Vec<u8>"),
"Output should preserve 'mut buffer: Vec<u8>': {}",
output
);
}
#[test]
fn test_roundtrip_labeled_loop() {
let source = r#"
fn labeled_loop_test() {
'outer: loop {
'inner: loop {
break 'outer;
}
}
}
"#;
let output = assert_valid_rust(source);
assert!(
output.contains("'outer") || output.contains("' outer"),
"Output should contain 'outer label: {}",
output
);
assert!(
output.contains("'inner") || output.contains("' inner"),
"Output should contain 'inner label: {}",
output
);
assert!(
output.contains("break 'outer") || output.contains("break ' outer"),
"Output should contain 'break 'outer': {}",
output
);
}
#[test]
fn test_roundtrip_labeled_while() {
let source = r#"
fn labeled_while_test() {
let mut i = 0;
'outer: while i < 10 {
i += 1;
if i == 5 {
continue 'outer;
}
}
}
"#;
let output = assert_valid_rust(source);
assert!(
output.contains("'outer") || output.contains("' outer"),
"Output should contain 'outer label: {}",
output
);
assert!(
output.contains("continue 'outer") || output.contains("continue ' outer"),
"Output should contain 'continue 'outer': {}",
output
);
}
#[test]
fn test_roundtrip_labeled_for() {
let source = r#"
fn labeled_for_test() {
'outer: for i in 0..10 {
for j in 0..10 {
if i + j > 15 {
break 'outer;
}
}
}
}
"#;
let output = assert_valid_rust(source);
assert!(
output.contains("'outer") || output.contains("' outer"),
"Output should contain 'outer label: {}",
output
);
}
#[test]
fn test_roundtrip_labeled_block() {
let source = r#"
fn labeled_block_test() -> i32 {
let result = 'block: {
if true {
break 'block 42;
}
0
};
result
}
"#;
let output = assert_valid_rust(source);
assert!(
output.contains("'block") || output.contains("' block"),
"Output should contain 'block label: {}",
output
);
assert!(
output.contains("break 'block 42") || output.contains("break ' block 42"),
"Output should contain 'break 'block 42': {}",
output
);
}
#[test]
fn test_roundtrip_break_with_value() {
let source = r#"
fn break_with_value_test() -> i32 {
loop {
break 42;
}
}
"#;
let output = assert_valid_rust(source);
assert!(
output.contains("break 42"),
"Output should contain 'break 42': {}",
output
);
}
#[test]
fn test_roundtrip_break_label_and_value() {
let source = r#"
fn break_label_value_test() -> i32 {
'outer: loop {
loop {
break 'outer 100;
}
}
}
"#;
let output = assert_valid_rust(source);
assert!(
output.contains("break 'outer 100") || output.contains("break ' outer 100"),
"Output should contain 'break 'outer 100': {}",
output
);
}
#[test]
fn test_roundtrip_continue_with_label() {
let source = r#"
fn continue_label_test() {
'outer: for i in 0..5 {
for j in 0..5 {
if j == 2 {
continue 'outer;
}
}
}
}
"#;
let output = assert_valid_rust(source);
assert!(
output.contains("continue 'outer") || output.contains("continue ' outer"),
"Output should contain 'continue 'outer': {}",
output
);
}
#[test]
fn test_roundtrip_extern_c_fn() {
let source = r#"
extern "C" fn c_function(x: i32) -> i32 {
x + 1
}
"#;
let output = assert_valid_rust(source);
assert!(
output.contains(r#"extern "C""#),
r#"Output should contain 'extern "C"': {}"#,
output
);
}
#[test]
fn test_roundtrip_extern_system_fn() {
let source = r#"
extern "system" fn system_function() {}
"#;
let output = assert_valid_rust(source);
assert!(
output.contains(r#"extern "system""#),
r#"Output should contain 'extern "system"': {}"#,
output
);
}
#[test]
fn test_roundtrip_extern_block() {
let source = r#"
extern "C" {
fn strlen(s: *const u8) -> usize;
fn memcpy(dest: *mut u8, src: *const u8, n: usize) -> *mut u8;
}
"#;
let output = assert_valid_rust(source);
assert!(
output.contains(r#"extern "C""#),
r#"Output should contain 'extern "C"': {}"#,
output
);
assert!(
output.contains("strlen"),
"Output should contain 'strlen': {}",
output
);
assert!(
output.contains("memcpy"),
"Output should contain 'memcpy': {}",
output
);
}
#[test]
fn test_roundtrip_nested_labeled_loops() {
let source = r#"
fn nested_labels() {
'a: loop {
'b: loop {
'c: loop {
break 'a;
}
continue 'b;
}
}
}
"#;
let output = assert_valid_rust(source);
assert!(
output.contains("'a") || output.contains("' a"),
"Output should contain label 'a: {}",
output
);
assert!(
output.contains("'b") || output.contains("' b"),
"Output should contain label 'b: {}",
output
);
assert!(
output.contains("'c") || output.contains("' c"),
"Output should contain label 'c: {}",
output
);
assert!(
output.contains("break 'a") || output.contains("break ' a"),
"Output should contain 'break 'a': {}",
output
);
assert!(
output.contains("continue 'b") || output.contains("continue ' b"),
"Output should contain 'continue 'b': {}",
output
);
}
#[test]
#[ignore = "slow: requires cargo check"]
fn compile_check_labeled_loops() {
let source = r#"
pub fn search_matrix(matrix: Vec<Vec<i32>>, target: i32) -> Option<(usize, usize)> {
'row: for (i, row) in matrix.iter().enumerate() {
for (j, &val) in row.iter().enumerate() {
if val == target {
return Some((i, j));
}
if val > target {
continue 'row;
}
}
}
None
}
pub fn find_first_match(data: &[&[i32]], predicate: impl Fn(i32) -> bool) -> Option<i32> {
'outer: for slice in data {
for &item in *slice {
if predicate(item) {
break 'outer Some(item);
}
}
}
None
}
"#;
assert_compiles(source);
}
#[test]
#[ignore = "slow: requires cargo check"]
fn compile_check_extern_functions() {
let source = r#"
extern "C" fn add_c(a: i32, b: i32) -> i32 {
a + b
}
extern "C" fn noop() {}
pub fn use_extern() -> i32 {
add_c(1, 2)
}
"#;
assert_compiles(source);
}
#[test]
fn test_roundtrip_inline_mod_with_uses() {
let source = r#"
mod utils {
use std::collections::HashMap;
use std::io::Write;
pub fn helper() {}
}
"#;
let output = assert_valid_rust(source);
assert!(output.contains("mod utils"), "Output: {}", output);
assert!(
output.contains("use std::collections::HashMap"),
"Output: {}",
output
);
assert!(output.contains("use std::io::Write"), "Output: {}", output);
assert!(output.contains("pub fn helper"), "Output: {}", output);
}
#[test]
fn test_roundtrip_inline_mod_complex() {
let source = r#"
pub mod models {
use serde::{Deserialize, Serialize};
pub struct User {
pub name: String,
pub age: u32,
}
pub fn create_user(name: String, age: u32) -> User {
User { name, age }
}
}
"#;
let output = assert_valid_rust(source);
assert!(output.contains("pub mod models"), "Output: {}", output);
assert!(output.contains("use serde"), "Output: {}", output);
assert!(output.contains("struct User"), "Output: {}", output);
}
#[test]
fn test_roundtrip_file_mod_declaration() {
let source = r#"
mod utils;
pub mod config;
"#;
let output = assert_valid_rust(source);
assert!(
output.contains("mod utils ;") || output.contains("mod utils;"),
"Output: {}",
output
);
assert!(
output.contains("pub mod config ;") || output.contains("pub mod config;"),
"Output: {}",
output
);
}
#[test]
fn test_roundtrip_nested_inline_mods() {
let source = r#"
mod outer {
use std::io;
mod inner {
use std::fmt;
pub fn format_it() {}
}
pub fn outer_fn() {}
}
"#;
let output = assert_valid_rust(source);
assert!(output.contains("mod outer"), "Output: {}", output);
assert!(output.contains("mod inner"), "Output: {}", output);
assert!(output.contains("use std::io"), "Output: {}", output);
assert!(output.contains("use std::fmt"), "Output: {}", output);
}
#[test]
fn test_roundtrip_mod_use_ordering() {
let source = r#"
mod ordered {
use a::A;
use b::B;
use c::C;
struct Item;
}
"#;
let output = assert_valid_rust(source);
let use_pos = output.find("use a::A").expect("use a::A not found");
let struct_pos = output.find("struct Item").expect("struct Item not found");
assert!(
use_pos < struct_pos,
"Use statements should come before items. Output: {}",
output
);
}
#[test]
fn test_to_source_result_success() {
let source = r#"
fn hello() -> String {
"world".to_string()
}
"#;
let pure = PureFile::from_source(source).expect("Should parse");
let result = pure.to_source();
assert!(
result.is_ok(),
"to_source should succeed: {:?}",
result.err()
);
let output = result.unwrap();
assert!(output.contains("fn hello"));
assert!(output.contains("world"));
}
#[test]
fn test_to_syn_result_success() {
let source = r#"
struct Point { x: i32, y: i32 }
"#;
let pure = PureFile::from_source(source).expect("Should parse");
let result = pure.to_syn();
assert!(result.is_ok(), "to_syn should succeed: {:?}", result.err());
let file = result.unwrap();
assert_eq!(file.items.len(), 1);
}