#[derive(thiserror::Error, Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum HarnessError {
#[error("capability not supported: {primitive}")]
CapabilityNotSupported {
primitive: String,
},
#[error("stale target: {detail}")]
StaleTarget {
detail: String,
},
#[error("transport error: {detail}")]
Transport {
detail: String,
},
#[error("protocol error: {detail}")]
Protocol {
detail: String,
},
#[error("harness reported failure: {detail}")]
Harness {
detail: String,
},
}
impl HarnessError {
#[must_use]
pub fn capability_not_supported(primitive: impl Into<String>) -> Self {
Self::CapabilityNotSupported {
primitive: primitive.into(),
}
}
#[must_use]
pub fn stale_target(detail: impl Into<String>) -> Self {
Self::StaleTarget {
detail: detail.into(),
}
}
#[must_use]
pub fn transport(detail: impl Into<String>) -> Self {
Self::Transport {
detail: detail.into(),
}
}
#[must_use]
pub fn protocol(detail: impl Into<String>) -> Self {
Self::Protocol {
detail: detail.into(),
}
}
#[must_use]
pub fn harness(detail: impl Into<String>) -> Self {
Self::Harness {
detail: detail.into(),
}
}
}
#[cfg(test)]
mod tests {
use super::HarnessError;
fn assert_send_sync_static<T: Send + Sync + 'static>() {}
#[test]
fn harness_error_is_send_sync_static() {
assert_send_sync_static::<HarnessError>();
}
#[test]
fn capability_not_supported_names_the_primitive() {
let error = HarnessError::capability_not_supported("pause_resume");
assert_eq!(error.to_string(), "capability not supported: pause_resume");
assert!(matches!(error, HarnessError::CapabilityNotSupported { .. }));
}
#[test]
fn each_constructor_renders_its_class() {
assert_eq!(
HarnessError::stale_target("attempt 2 superseded").to_string(),
"stale target: attempt 2 superseded"
);
assert_eq!(
HarnessError::transport("broken pipe").to_string(),
"transport error: broken pipe"
);
assert_eq!(
HarnessError::protocol("no matching id").to_string(),
"protocol error: no matching id"
);
assert_eq!(
HarnessError::harness("exit code 1").to_string(),
"harness reported failure: exit code 1"
);
}
}