use clap::CommandFactory;
use phoxal_runtime_contract::contract_surface::{
ContractRecord, ContractSurface, LaunchArgument, LaunchValueShape,
};
use crate::participant::launch::Launch;
#[must_use]
pub fn contract_surface() -> String {
ContractSurface::new([ContractRecord::launch(launch_arguments())]).canonical_json()
}
fn launch_arguments() -> Vec<LaunchArgument> {
Launch::command()
.get_arguments()
.map(|argument| {
LaunchArgument::new(
argument
.get_long()
.map_or_else(|| argument.get_id().to_string(), ToString::to_string),
argument.is_required_set(),
matches!(argument.get_action(), clap::ArgAction::Append),
if argument.get_action().takes_values() {
LaunchValueShape::Text
} else {
LaunchValueShape::Flag
},
)
})
.collect()
}
#[cfg(test)]
mod tests {
use super::{contract_surface, launch_arguments};
use phoxal_runtime_contract::contract_surface::{
ContractRecord, ContractSurface, LaunchArgument, LaunchValueShape,
};
#[test]
fn the_launch_record_is_the_supervisor_owned_argv_contract() {
let expected = ContractSurface::new([ContractRecord::launch([
LaunchArgument::new("participant-id", true, false, LaunchValueShape::Text),
LaunchArgument::new("bundle-root", true, false, LaunchValueShape::Text),
LaunchArgument::new("connect", true, true, LaunchValueShape::Text),
LaunchArgument::new("simulation", false, false, LaunchValueShape::Flag),
])]);
assert_eq!(contract_surface(), expected.canonical_json());
}
#[test]
fn the_surface_is_deterministic_json() {
let rendered = contract_surface();
serde_json::from_str::<serde_json::Value>(&rendered).expect("the surface is JSON");
assert_eq!(contract_surface(), rendered);
assert!(rendered.contains(r#""record":"launch""#), "{rendered}");
}
#[test]
fn simulation_is_the_only_bare_switch() {
let flags = launch_arguments()
.into_iter()
.filter(|argument| argument.value == LaunchValueShape::Flag)
.map(|argument| argument.name)
.collect::<Vec<_>>();
assert_eq!(flags, ["simulation"]);
}
}