138 lines
3.8 KiB
Rust
Raw Normal View History

use crate::haskell::{run_haskell_demo, DemoArgs};
2026-03-24 09:50:06 +01:00
use std::ffi::OsString;
use tracing::error;
pub fn run(args: impl IntoIterator<Item = OsString>) -> Result<(), i32> {
let args: Vec<String> = args
.into_iter()
.map(|arg| arg.to_string_lossy().into_owned())
.collect();
match parse_command(&args) {
Ok(Command::Help) => {
print_usage(args.first().map(String::as_str).unwrap_or("integrations"));
Ok(())
}
Ok(Command::RustCallsHaskell(demo_args)) => match run_haskell_demo(&demo_args) {
Ok(output) => {
println!("{output}");
Ok(())
}
Err(message) => {
error!("{message}");
Err(1)
}
},
Err(message) => {
error!("{message}");
print_usage(args.first().map(String::as_str).unwrap_or("integrations"));
Err(1)
}
}
}
#[derive(Debug, PartialEq, Eq)]
enum Command {
Help,
RustCallsHaskell(DemoArgs),
}
fn parse_command(args: &[String]) -> Result<Command, String> {
match args {
[_program] => Ok(Command::Help),
[_program, command] if command == "help" || command == "--help" || command == "-h" => {
Ok(Command::Help)
}
[_program, command, rest @ ..] if command == "rust-calls-haskell" => {
parse_demo_args(rest).map(Command::RustCallsHaskell)
}
[_program, command, ..] => Err(format!("unknown command: {command}")),
[] => Ok(Command::Help),
}
}
fn parse_demo_args(args: &[String]) -> Result<DemoArgs, String> {
let name = args
.first()
.cloned()
.unwrap_or_else(|| "Ada".to_string());
let left = parse_i32_arg(args.get(1), "left operand", 7)?;
let right = parse_i32_arg(args.get(2), "right operand", 5)?;
let library_path = args.get(3).cloned();
Ok(DemoArgs {
name,
left,
right,
library_path,
})
}
fn parse_i32_arg(raw: Option<&String>, label: &str, default: i32) -> Result<i32, String> {
match raw {
Some(value) => value
.parse::<i32>()
.map_err(|_| format!("invalid {label}: {value}")),
None => Ok(default),
2026-03-24 09:50:06 +01:00
}
}
fn print_usage(program: &str) {
println!("Usage:");
println!(" {program} rust-calls-haskell [name] [left] [right] [haskell-lib-path]");
println!();
println!("Examples:");
println!(" {program} rust-calls-haskell");
println!(" {program} rust-calls-haskell Grace 8 3");
}
2026-03-24 09:50:06 +01:00
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parse_demo_args_uses_defaults() {
let args: Vec<String> = Vec::new();
let parsed = parse_demo_args(&args).expect("defaults should parse");
assert_eq!(
parsed,
DemoArgs {
name: "Ada".to_string(),
left: 7,
right: 5,
library_path: None,
}
);
}
#[test]
fn parse_demo_args_accepts_explicit_values() {
let args = vec![
"Linus".to_string(),
"9".to_string(),
"4".to_string(),
"haskell/dist-newstyle/demo/libinterop_hs.so".to_string(),
];
let parsed = parse_demo_args(&args).expect("explicit args should parse");
assert_eq!(
parsed,
DemoArgs {
name: "Linus".to_string(),
left: 9,
right: 4,
library_path: Some("haskell/dist-newstyle/demo/libinterop_hs.so".to_string()),
}
);
2026-03-24 09:50:06 +01:00
}
#[test]
fn parse_command_rejects_unknown_commands() {
let args = vec!["integrations".to_string(), "wat".to_string()];
let parsed = parse_command(&args);
assert!(parsed.is_err());
2026-03-24 09:50:06 +01:00
}
}