#![allow(clippy::expect_used)]
#![allow(clippy::unwrap_used)]
use anyhow::Result;
use std::fs;
use std::process::Command;
use tempfile::TempDir;
mod colors {
pub(crate) const RESET: &str = "\x1b[0m";
pub(crate) const BOLD: &str = "\x1b[1m";
pub(crate) const GREEN: &str = "\x1b[32m";
pub(crate) const BLUE: &str = "\x1b[34m";
pub(crate) const YELLOW: &str = "\x1b[33m";
pub(crate) const CYAN: &str = "\x1b[36m";
pub(crate) const DIM: &str = "\x1b[2m";
}
fn print_header(text: &str) {
println!(
"\n{}{}======================================================================{}",
colors::BOLD,
colors::BLUE,
colors::RESET
);
println!(
"{}{} {} {}",
colors::BOLD,
colors::BLUE,
text,
colors::RESET
);
println!(
"{}{}======================================================================{}",
colors::BOLD,
colors::BLUE,
colors::RESET
);
}
fn print_section(num: u32, text: &str) {
println!(
"\n{}{}--- {}. {} ---{}",
colors::BOLD,
colors::CYAN,
num,
text,
colors::RESET
);
}
fn transpile_and_run(rust_code: &str, temp_dir: &std::path::Path) -> Result<(String, String)> {
let input_path = temp_dir.join("input.rs");
let output_path = temp_dir.join("output.sh");
fs::write(&input_path, rust_code)?;
let result = Command::new("cargo")
.arg("run")
.arg("--quiet")
.arg("-p")
.arg("bashrs")
.arg("--bin")
.arg("bashrs")
.arg("--")
.arg("build")
.arg(input_path.to_str().unwrap())
.arg("-o")
.arg(output_path.to_str().unwrap())
.output()?;
if !result.status.success() {
let stderr = String::from_utf8_lossy(&result.stderr);
return Err(anyhow::anyhow!("Transpilation failed: {}", stderr));
}
let shell_code = fs::read_to_string(&output_path)?;
let run_result = Command::new("sh").arg(&output_path).output()?;
let output = String::from_utf8_lossy(&run_result.stdout)
.trim()
.to_string();
Ok((shell_code, output))
}
const BOILERPLATE: &[&str] = &[
"#!/bin/sh",
"# Generated by",
"# POSIX-compliant",
"set -euf",
"IFS='",
"'",
"export LC_ALL",
"# Rash runtime",
"rash_println()",
"printf '%s\\n'",
"# Cleanup on exit",
"trap ",
"# Execute main",
"main \"$@\"",
];
fn is_boilerplate(line: &str) -> bool {
let trimmed = line.trim();
trimmed.is_empty() || BOILERPLATE.iter().any(|bp| trimmed.starts_with(bp))
}
fn show_result(rust_code: &str, shell_code: &str, output: &str) {
println!("\n{}Rust input:{}", colors::BOLD, colors::RESET);
for line in rust_code.lines() {
println!(" {}{}{}", colors::YELLOW, line, colors::RESET);
}
println!("\n{}Generated POSIX shell:{}", colors::BOLD, colors::RESET);
for line in shell_code.lines().filter(|l| !is_boilerplate(l)) {
println!(" {}{}{}", colors::GREEN, line, colors::RESET);
}
println!("\n{}Output:{} {}", colors::BOLD, colors::RESET, output);
}
fn demo_basic_function(temp_dir: &std::path::Path) -> Result<()> {
print_section(1, "Basic Function with Return Value");
let rust_code = r#"fn double(x: u32) -> u32 {
return x * 2;
}
fn main() {
let result = double(21);
println!("{}", result);
}"#;
let (shell, output) = transpile_and_run(rust_code, temp_dir)?;
show_result(rust_code, &shell, &output);
assert_eq!(output, "42", "Expected 42");
println!(
" {}{}Correct: double(21) = 42{}",
colors::BOLD,
colors::GREEN,
colors::RESET
);
Ok(())
}
fn demo_nested_calls(temp_dir: &std::path::Path) -> Result<()> {
print_section(2, "Nested Function Calls: f(g(h(x)))");
let rust_code = r#"fn square(x: u32) -> u32 {
return x * x;
}
fn add_ten(x: u32) -> u32 {
return x + 10;
}
fn double(x: u32) -> u32 {
return x + x;
}
fn main() {
let result = double(add_ten(square(3)));
println!("{}", result);
}"#;
let (shell, output) = transpile_and_run(rust_code, temp_dir)?;
show_result(rust_code, &shell, &output);
assert_eq!(
output, "38",
"Expected 38: double(add_ten(square(3))) = double(add_ten(9)) = double(19) = 38"
);
println!(
" {}{}Correct: double(add_ten(square(3))) = 38{}",
colors::BOLD,
colors::GREEN,
colors::RESET
);
Ok(())
}
fn demo_match_expression(temp_dir: &std::path::Path) -> Result<()> {
print_section(3, "Match Expression in Let Binding");
let rust_code = r#"fn classify(n: u32) -> u32 {
let tier = match n % 4 {
0 => n * 10,
1 => n * 5,
2 => n + 100,
_ => n,
};
return tier;
}
fn main() {
let a = classify(8);
let b = classify(9);
let c = classify(10);
println!("{}", a + b + c);
}"#;
let (shell, output) = transpile_and_run(rust_code, temp_dir)?;
show_result(rust_code, &shell, &output);
assert_eq!(output, "235", "Expected 235");
println!(
" {}{}Correct: classify(8)+classify(9)+classify(10) = 80+45+110 = 235{}",
colors::BOLD,
colors::GREEN,
colors::RESET
);
Ok(())
}
fn demo_loop_with_return(temp_dir: &std::path::Path) -> Result<()> {
print_section(4, "While Loop with Early Return");
let rust_code = r#"fn find_divisible(n: u32) -> u32 {
let mut i = 1;
while i < n {
if (i * i) % 7 == 0 {
return i;
}
i = i + 1;
}
return 0;
}
fn main() {
let result = find_divisible(100);
println!("{}", result);
}"#;
let (shell, output) = transpile_and_run(rust_code, temp_dir)?;
show_result(rust_code, &shell, &output);
assert_eq!(output, "7", "Expected 7");
println!(
" {}{}Correct: first i where i*i%%7==0 is 7{}",
colors::BOLD,
colors::GREEN,
colors::RESET
);
Ok(())
}
fn demo_match_in_loop(temp_dir: &std::path::Path) -> Result<()> {
print_section(5, "Match Inside While Loop (Combined Pattern)");
let rust_code = r#"fn weighted_sum(n: u32) -> u32 {
let mut total = 0;
let mut i = 0;
while i < n {
let weight = match i % 3 {
0 => 1,
1 => 3,
_ => 5,
};
total = total + i * weight;
i = i + 1;
}
return total;
}
fn main() {
let result = weighted_sum(6);
println!("{}", result);
}"#;
let (shell, output) = transpile_and_run(rust_code, temp_dir)?;
show_result(rust_code, &shell, &output);
assert_eq!(output, "53", "Expected 53");
println!(
" {}{}Correct: weighted_sum(6) = 0+3+10+3+12+25 = 53{}",
colors::BOLD,
colors::GREEN,
colors::RESET
);
Ok(())
}
fn demo_recursion(temp_dir: &std::path::Path) -> Result<()> {
print_section(6, "Recursive Function (Fibonacci)");
let rust_code = r#"fn fib(n: u32) -> u32 {
if n < 2 {
return n;
}
let a = fib(n - 1);
let b = fib(n - 2);
return a + b;
}
fn main() {
let result = fib(10);
println!("{}", result);
}"#;
let (shell, output) = transpile_and_run(rust_code, temp_dir)?;
show_result(rust_code, &shell, &output);
assert_eq!(output, "55", "Expected fib(10) = 55");
println!(
" {}{}Correct: fib(10) = 55{}",
colors::BOLD,
colors::GREEN,
colors::RESET
);
Ok(())
}
fn demo_multi_function(temp_dir: &std::path::Path) -> Result<()> {
print_section(7, "Multi-Function Program with Call Chain");
let rust_code = r#"fn gcd(a: u32, b: u32) -> u32 {
let mut x = a;
let mut y = b;
while y > 0 {
let temp = y;
y = x % y;
x = temp;
}
return x;
}
fn lcm(a: u32, b: u32) -> u32 {
let g = gcd(a, b);
return a / g * b;
}
fn main() {
let result = lcm(12, 18);
println!("{}", result);
}"#;
let (shell, output) = transpile_and_run(rust_code, temp_dir)?;
show_result(rust_code, &shell, &output);
assert_eq!(output, "36", "Expected lcm(12,18) = 36");
println!(
" {}{}Correct: lcm(12, 18) = 36{}",
colors::BOLD,
colors::GREEN,
colors::RESET
);
Ok(())
}
fn main() -> Result<()> {
print_header("Rust-to-Shell Transpiler Demo");
println!(
"\n{}Demonstrating bashrs transpilation: write Rust, get safe POSIX shell.{}",
colors::DIM,
colors::RESET
);
println!(
"{}Every generated script uses set -euf, proper quoting, and passes shellcheck.{}\n",
colors::DIM,
colors::RESET
);
let temp_dir = TempDir::new()?;
let temp_path = temp_dir.path();
demo_basic_function(temp_path)?;
demo_nested_calls(temp_path)?;
demo_match_expression(temp_path)?;
demo_loop_with_return(temp_path)?;
demo_match_in_loop(temp_path)?;
demo_recursion(temp_path)?;
demo_multi_function(temp_path)?;
print_header("All 7 Demos Passed");
println!(
"\n{}Supported Rust constructs:{}",
colors::BOLD,
colors::RESET
);
println!(" - Functions with parameters and return values");
println!(" - Nested function calls: f(g(h(x)))");
println!(" - match expressions (let x = match y {{ ... }})");
println!(" - while loops with early return");
println!(" - match inside loops (combined patterns)");
println!(" - Recursive functions");
println!(" - Multi-function programs with call chains");
println!(" - Arithmetic: +, -, *, /, %%, bitwise ops");
println!(" - Comparisons: ==, !=, <, >, <=, >=");
println!(" - Boolean logic: &&, ||, !");
println!(" - for loops (range and iterator)");
println!(" - if/else with elif chains");
println!(" - Environment variables: env_var_or()");
println!(" - String interpolation: println!(\"{{}}\"...)");
println!(
"\n{}Try it:{} bashrs build your_code.rs -o output.sh",
colors::BOLD,
colors::RESET
);
Ok(())
}