pub mod surface;
pub mod wire;
use clap::CommandFactory;
use crate::__compat::surface::{ContractRecord, ContractSurface, LaunchArgument, LaunchValueShape};
use crate::participant::launch::Launch;
#[must_use]
pub fn contract_surface() -> String {
ContractSurface::new(contract_records()).canonical_json()
}
fn contract_records() -> Vec<ContractRecord> {
let mut records = vec![ContractRecord::launch(launch_arguments())];
crate::api::contract_records(&mut records);
crate::runtime::api::contract_records(&mut records);
crate::supervisor::api::contract_records(&mut records);
crate::bus::__compat::contract_records(&mut records);
crate::bundle::__compat::contract_records(&mut records);
crate::participant::metadata::__compat::contract_records(&mut records);
records
}
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 crate::__compat::surface::{
ContractRecord, ContractSurface, LaunchArgument, LaunchValueShape,
};
#[test]
fn the_launch_record_is_the_supervisor_owned_argv_contract() {
let expected = 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!(ContractRecord::launch(launch_arguments()), expected);
let rendered = ContractSurface::new([expected]).canonical_json();
let record = rendered
.strip_prefix(r#"{"records":["#)
.and_then(|rest| rest.strip_suffix("]}"))
.expect("one record renders inside its own surface");
assert!(contract_surface().contains(record), "{record}");
}
#[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);
for expected in [
r#""record":"launch""#,
r#""record":"endpoint""#,
r#""record":"envelope""#,
r#""record":"document""#,
r#""record":"identifier""#,
] {
assert!(rendered.contains(expected), "{expected} missing");
}
}
#[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"]);
}
}