2026-03-24 14:34:09 +01:00
|
|
|
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> {
|
2026-03-24 14:34:09 +01:00
|
|
|
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
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-24 14:34:09 +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]
|
2026-03-24 14:34:09 +01:00
|
|
|
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]
|
2026-03-24 14:34:09 +01:00
|
|
|
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
|
|
|
}
|
|
|
|
|
}
|