#[cfg(test)]
mod tests {
#[test]
fn test_switch_to_match_basic() {
let c_code = r#"
switch (x) {
case 1:
y = 10;
break;
case 2:
y = 20;
break;
default:
y = 0;
}
"#;
let rust_expected = r#"
let y = match x {
1 => 10,
2 => 20,
_ => 0,
};
"#;
assert!(c_code.contains("switch (x)"));
assert!(c_code.contains("case 1:"));
assert!(c_code.contains("default:"));
assert!(rust_expected.contains("match x"));
assert!(rust_expected.contains("_ =>"));
}
#[test]
fn test_switch_return_to_match() {
let c_code = r#"
switch (x) {
case 1:
return 10;
case 2:
return 20;
default:
return 0;
}
"#;
let rust_expected = r#"
return match x {
1 => 10,
2 => 20,
_ => 0,
};
"#;
assert!(c_code.contains("return 10"));
assert!(rust_expected.contains("match x"));
}
#[test]
fn test_switch_multiple_cases() {
let c_code = r#"
switch (x) {
case 1:
case 2:
case 3:
y = 1;
break;
default:
y = 0;
}
"#;
let rust_expected = r#"
let y = match x {
1 | 2 | 3 => 1,
_ => 0,
};
"#;
assert!(c_code.contains("case 1:"));
assert!(c_code.contains("case 2:"));
assert!(c_code.contains("case 3:"));
assert!(rust_expected.contains("1 | 2 | 3"));
}
#[test]
fn test_switch_no_default() {
let c_code = r#"
switch (x) {
case 0:
y = 0;
break;
case 1:
y = 1;
break;
}
"#;
let rust_expected = r#"
let y = match x {
0 => 0,
1 => 1,
_ => panic!("Unhandled case"), // Compiler requires this
};
"#;
assert!(c_code.contains("case 0:"));
assert!(c_code.contains("case 1:"));
assert!(!c_code.contains("default:"));
assert!(rust_expected.contains("_ =>"));
}
#[test]
fn test_switch_empty_cases() {
let _c_code = r#"
switch (x) {
case 1:
break;
case 2:
process();
break;
default:
break;
}
"#;
let rust_expected = r#"
match x {
1 => {},
2 => { process(); },
_ => {},
}
"#;
assert!(rust_expected.contains("1 => {}"));
assert!(rust_expected.contains("_ => {}"));
}
#[test]
fn test_switch_char_to_match() {
let c_code = r#"
switch (c) {
case 'a':
result = 1;
break;
case 'b':
result = 2;
break;
default:
result = 0;
}
"#;
let rust_expected = r#"
let result = match c {
b'a' => 1,
b'b' => 2,
_ => 0,
};
"#;
assert!(c_code.contains("case 'a':"));
assert!(rust_expected.contains("b'a'"));
}
#[test]
fn test_switch_nested() {
let c_code = r#"
switch (x) {
case 1:
switch (y) {
case 10: result = 100; break;
default: result = 10; break;
}
break;
default:
result = 0;
}
"#;
let rust_expected = r#"
let result = match x {
1 => match y {
10 => 100,
_ => 10,
},
_ => 0,
};
"#;
assert!(c_code.contains("switch (x)"));
assert!(c_code.contains("switch (y)"));
assert!(rust_expected.contains("match x"));
assert!(rust_expected.contains("match y"));
}
#[test]
fn test_switch_with_blocks() {
let c_code = r#"
switch (x) {
case 1: {
int temp = x * 2;
result = temp + 1;
break;
}
default:
result = 0;
}
"#;
let rust_expected = r#"
let result = match x {
1 => {
let temp = x * 2;
temp + 1
},
_ => 0,
};
"#;
assert!(c_code.contains("int temp"));
assert!(rust_expected.contains("let temp"));
}
#[test]
fn test_switch_range_to_match_range() {
let c_code = r#"
switch (x) {
case 0: case 1: case 2: case 3: case 4: case 5:
category = 0;
break;
case 6: case 7: case 8: case 9: case 10:
category = 1;
break;
default:
category = 2;
}
"#;
let rust_expected = r#"
let category = match x {
0..=5 => 0,
6..=10 => 1,
_ => 2,
};
"#;
assert!(c_code.contains("case 0:"));
assert!(c_code.contains("case 5:"));
assert!(rust_expected.contains("0..=5"));
}
#[test]
fn test_switch_enum_to_match() {
let c_code = r#"
enum Color { RED, GREEN, BLUE };
switch (color) {
case RED:
value = 1;
break;
case GREEN:
value = 2;
break;
case BLUE:
value = 3;
break;
}
"#;
let rust_expected = r#"
enum Color { Red, Green, Blue }
let value = match color {
Color::Red => 1,
Color::Green => 2,
Color::Blue => 3,
};
"#;
assert!(c_code.contains("case RED:"));
assert!(rust_expected.contains("Color::Red"));
}
#[test]
fn test_switch_function_return() {
let c_code = r#"
int get_category(int x) {
switch (x) {
case 1: return 10;
case 2: return 20;
default: return 0;
}
}
"#;
let rust_expected = r#"
fn get_category(x: i32) -> i32 {
match x {
1 => 10,
2 => 20,
_ => 0,
}
}
"#;
assert!(c_code.contains("int get_category"));
assert!(rust_expected.contains("fn get_category"));
}
#[test]
fn test_switch_with_continue() {
let c_code = r#"
for (int i = 0; i < n; i++) {
switch (arr[i]) {
case 0:
continue;
case 1:
process(i);
break;
default:
break;
}
}
"#;
let rust_expected = r#"
for i in 0..n {
match arr[i] {
0 => continue,
1 => { process(i); },
_ => {},
}
}
"#;
assert!(c_code.contains("continue;"));
assert!(rust_expected.contains("continue"));
}
#[test]
fn test_switch_to_match_with_guards() {
let c_code = r#"
// C switch only supports constant integers
switch (x) {
case 1:
if (y > 10) {
result = 100;
} else {
result = 10;
}
break;
default:
result = 0;
}
"#;
let rust_expected = r#"
let result = match x {
1 if y > 10 => 100, // Pattern guard
1 => 10,
_ => 0,
};
"#;
assert!(c_code.contains("if (y > 10)"));
assert!(rust_expected.contains("if y > 10"));
}
#[test]
fn test_switch_fallthrough_bug_prevented() {
let c_code = r#"
switch (x) {
case 1:
printf("one\n");
// ERROR: forgot break, falls through to case 2
case 2:
printf("one or two\n");
break;
}
"#;
let rust_expected = r#"
match x {
1 => {
println!("one");
// No fall-through in Rust - each arm is isolated
},
2 => {
println!("two");
},
_ => {},
}
"#;
assert!(c_code.contains("// ERROR"));
assert!(rust_expected.contains("No fall-through"));
}
#[test]
fn test_switch_bool_to_if_else() {
let c_code = r#"
switch (flag) {
case 0:
result = false_value;
break;
case 1:
result = true_value;
break;
}
"#;
let rust_expected = r#"
let result = if flag != 0 {
true_value
} else {
false_value
};
"#;
assert!(c_code.contains("case 0:"));
assert!(c_code.contains("case 1:"));
assert!(rust_expected.contains("if flag"));
}
#[test]
fn test_switch_macros_to_match() {
let c_code = r#"
#define ERROR_OK 0
#define ERROR_FAIL 1
switch (error_code) {
case ERROR_OK:
return true;
case ERROR_FAIL:
return false;
default:
return false;
}
"#;
let rust_expected = r#"
const ERROR_OK: i32 = 0;
const ERROR_FAIL: i32 = 1;
match error_code {
ERROR_OK => true,
ERROR_FAIL => false,
_ => false,
}
"#;
assert!(c_code.contains("ERROR_OK"));
assert!(rust_expected.contains("ERROR_OK"));
}
#[test]
fn test_switch_transformation_summary() {
let c_code = r#"
// Rule 1: Basic switch → match
switch (x) { case 1: break; default: break; }
// Rule 2: switch with return → match expression
switch (x) { case 1: return 10; }
// Rule 3: Multiple cases → pattern alternatives
switch (x) { case 1: case 2: break; }
// Rule 4: No default → _ required
switch (x) { case 1: break; }
// Rule 5: Empty case → empty arm
switch (x) { case 1: break; }
// Rule 6: Character → character match
switch (c) { case 'a': break; }
// Rule 7: Nested switch → nested match
switch (x) { case 1: switch (y) { } break; }
// Rule 8: Range → range pattern
switch (x) { case 0: case 1: case 2: break; }
// Rule 9: Enum → enum match
switch (color) { case RED: break; }
// Rule 10: Fall-through prevented
switch (x) { case 1: /* no break */ case 2: break; }
"#;
let rust_expected = r#"
// Rule 1: Expression-based
match x { 1 => {}, _ => {} }
// Rule 2: Direct return
match x { 1 => 10, _ => 0 }
// Rule 3: | for alternatives
match x { 1 | 2 => {}, _ => {} }
// Rule 4: Exhaustiveness required
match x { 1 => {}, _ => {} }
// Rule 5: {} for no-op
match x { 1 => {}, _ => {} }
// Rule 6: byte literal
match c { b'a' => {}, _ => {} }
// Rule 7: Composition
match x { 1 => match y { _ => {} }, _ => {} }
// Rule 8: Inclusive range
match x { 0..=2 => {}, _ => {} }
// Rule 9: Variant syntax
match color { Color::Red => {}, _ => {} }
// Rule 10: No fall-through (safe)
match x { 1 => {}, 2 => {}, _ => {} }
"#;
assert!(c_code.contains("switch (x)"));
assert!(c_code.contains("case 1:"));
assert!(c_code.contains("default:"));
assert!(rust_expected.contains("match x"));
assert!(rust_expected.contains("_ =>"));
assert!(rust_expected.contains("0..=2"));
assert!(rust_expected.contains("1 | 2"));
}
}