pub const DEBUG_UNIMPLEMENTED_EXIT_CODE: i32 = 2;
pub fn write_debug_unimplemented_message<W: std::io::Write>(
mut out: W,
subcommand: &str,
ticket: &str,
requested: &str,
) -> std::io::Result<()> {
writeln!(
out,
"error: pmat debug {subcommand} is not implemented ({ticket})"
)?;
writeln!(out, " requested: {requested}")?;
writeln!(
out,
"hint: no DAP socket is bound and no recording is read — nothing is running"
)?;
Ok(())
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "path_exists")]
pub async fn handle_debug_serve(
port: u16,
host: String,
record_dir: Option<std::path::PathBuf>,
) -> anyhow::Result<()> {
let _ = write_debug_unimplemented_message(
std::io::stderr(),
"serve",
"DEBUG-002",
&format!("host={host} port={port} record_dir={record_dir:?}"),
);
std::process::exit(DEBUG_UNIMPLEMENTED_EXIT_CODE);
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "path_exists")]
pub async fn handle_debug_replay(
recording: std::path::PathBuf,
position: Option<usize>,
interactive: bool,
) -> anyhow::Result<()> {
let _ = write_debug_unimplemented_message(
std::io::stderr(),
"replay",
"DEBUG-003",
&format!("recording={recording:?} position={position:?} interactive={interactive}"),
);
std::process::exit(DEBUG_UNIMPLEMENTED_EXIT_CODE);
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub async fn handle_debug_compare() -> anyhow::Result<()> {
anyhow::bail!("Debug compare command not yet implemented")
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub async fn handle_debug_timeline() -> anyhow::Result<()> {
anyhow::bail!("Debug timeline command not yet implemented")
}
#[cfg_attr(coverage_nightly, coverage(off))]
#[cfg(test)]
mod coverage_tests {
use super::*;
#[test]
fn serve_message_states_not_implemented_and_names_the_ticket() {
let mut buf = Vec::new();
write_debug_unimplemented_message(
&mut buf,
"serve",
"DEBUG-002",
"host=localhost port=8080 record_dir=None",
)
.unwrap();
let s = String::from_utf8(buf).unwrap();
assert!(s.contains("DEBUG-002"), "got: {s}");
assert!(s.contains("not implemented"), "got: {s}");
}
#[test]
fn serve_message_echoes_the_request() {
let mut buf = Vec::new();
write_debug_unimplemented_message(
&mut buf,
"serve",
"DEBUG-002",
"host=0.0.0.0 port=65535 record_dir=Some(\"/tmp/debug-recordings\")",
)
.unwrap();
let s = String::from_utf8(buf).unwrap();
assert!(s.contains("0.0.0.0"), "got: {s}");
assert!(s.contains("65535"), "got: {s}");
assert!(s.contains("/tmp/debug-recordings"), "got: {s}");
}
#[test]
fn replay_message_states_not_implemented_and_names_the_ticket() {
let mut buf = Vec::new();
write_debug_unimplemented_message(
&mut buf,
"replay",
"DEBUG-003",
"recording=\"/tmp/recording.dap\" position=Some(42) interactive=true",
)
.unwrap();
let s = String::from_utf8(buf).unwrap();
assert!(s.contains("DEBUG-003"), "got: {s}");
assert!(s.contains("not implemented"), "got: {s}");
assert!(s.contains("/tmp/recording.dap"), "got: {s}");
}
#[test]
fn unimplemented_exit_code_matches_serve() {
assert_eq!(DEBUG_UNIMPLEMENTED_EXIT_CODE, 2);
assert_eq!(
DEBUG_UNIMPLEMENTED_EXIT_CODE,
crate::cli::handlers::utility_serve_handlers::SERVE_UNIMPLEMENTED_EXIT_CODE
);
}
#[test]
fn serve_and_replay_are_labelled_not_implemented() {
use clap::Subcommand;
let abouts = crate::cli::commands::on_big_stack(|| {
let cmd = crate::cli::commands::DebugCommands::augment_subcommands(clap::Command::new(
"debug",
));
["serve", "replay"]
.into_iter()
.map(|name| {
cmd.get_subcommands()
.find(|s| s.get_name() == name)
.unwrap_or_else(|| panic!("{name} subcommand must exist"))
.get_about()
.map(std::string::ToString::to_string)
.unwrap_or_default()
})
.collect::<Vec<_>>()
});
for (name, about) in ["serve", "replay"].into_iter().zip(abouts) {
assert!(
about.contains("NOT IMPLEMENTED"),
"`debug {name}` must be labelled unimplemented in the command list, got: {about}"
);
}
}
#[tokio::test]
async fn test_handle_debug_compare_returns_not_implemented() {
let result = handle_debug_compare().await;
assert!(result.is_err());
let err = result.unwrap_err();
assert!(err.to_string().contains("not yet implemented"));
}
#[tokio::test]
async fn test_handle_debug_timeline_returns_not_implemented() {
let result = handle_debug_timeline().await;
assert!(result.is_err());
let err = result.unwrap_err();
assert!(err.to_string().contains("not yet implemented"));
}
}