use std::borrow::Cow;
use std::fmt;
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug)]
pub enum Error {
WindowsApi(WindowsApiError),
AccessDenied(AccessDeniedError),
NotFound(NotFoundError),
InvalidParameter(InvalidParameterError),
Registry(RegistryError),
Process(ProcessError),
Service(ServiceError),
Thread(ThreadError),
EventLog(EventLogError),
Etw(EtwError),
Mitigation(MitigationError),
Proxy(ProxyError),
Security(SecurityError),
FileOperation(FileOperationError),
Pipe(PipeError),
Desktop(DesktopError),
Other(OtherError),
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Error::WindowsApi(e) => write!(f, "{}", e),
Error::AccessDenied(e) => write!(f, "{}", e),
Error::NotFound(e) => write!(f, "{}", e),
Error::InvalidParameter(e) => write!(f, "{}", e),
Error::Registry(e) => write!(f, "{}", e),
Error::Process(e) => write!(f, "{}", e),
Error::Service(e) => write!(f, "{}", e),
Error::Thread(e) => write!(f, "{}", e),
Error::EventLog(e) => write!(f, "{}", e),
Error::Etw(e) => write!(f, "{}", e),
Error::Mitigation(e) => write!(f, "{}", e),
Error::Proxy(e) => write!(f, "{}", e),
Error::Security(e) => write!(f, "{}", e),
Error::FileOperation(e) => write!(f, "{}", e),
Error::Pipe(e) => write!(f, "{}", e),
Error::Desktop(e) => write!(f, "{}", e),
Error::Other(e) => write!(f, "{}", e),
}
}
}
impl std::error::Error for Error {}
impl From<windows::core::Error> for Error {
fn from(err: windows::core::Error) -> Self {
Error::WindowsApi(WindowsApiError {
inner: err,
context: None,
})
}
}
#[derive(Debug)]
pub struct WindowsApiError {
pub inner: windows::core::Error,
pub context: Option<Cow<'static, str>>,
}
impl WindowsApiError {
pub fn new(inner: windows::core::Error) -> Self {
WindowsApiError {
inner,
context: None,
}
}
pub fn with_context(
inner: windows::core::Error,
context: impl Into<Cow<'static, str>>,
) -> Self {
WindowsApiError {
inner,
context: Some(context.into()),
}
}
pub fn code(&self) -> i32 {
self.inner.code().0
}
}
impl fmt::Display for WindowsApiError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(context) = &self.context {
write!(f, "Windows API error in {}: {}", context, self.inner)
} else {
write!(f, "Windows API error: {}", self.inner)
}
}
}
impl std::error::Error for WindowsApiError {}
#[derive(Debug)]
pub struct AccessDeniedError {
pub resource: Cow<'static, str>,
pub operation: Cow<'static, str>,
pub reason: Option<Cow<'static, str>>,
}
impl AccessDeniedError {
pub fn new(
resource: impl Into<Cow<'static, str>>,
operation: impl Into<Cow<'static, str>>,
) -> Self {
AccessDeniedError {
resource: resource.into(),
operation: operation.into(),
reason: None,
}
}
pub fn with_reason(
resource: impl Into<Cow<'static, str>>,
operation: impl Into<Cow<'static, str>>,
reason: impl Into<Cow<'static, str>>,
) -> Self {
AccessDeniedError {
resource: resource.into(),
operation: operation.into(),
reason: Some(reason.into()),
}
}
}
impl fmt::Display for AccessDeniedError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(reason) = &self.reason {
write!(
f,
"Access denied: cannot {} '{}' ({})",
self.operation, self.resource, reason
)
} else {
write!(
f,
"Access denied: cannot {} '{}'",
self.operation, self.resource
)
}
}
}
impl std::error::Error for AccessDeniedError {}
#[derive(Debug)]
pub struct NotFoundError {
pub resource_type: Cow<'static, str>,
pub identifier: Cow<'static, str>,
}
impl NotFoundError {
pub fn new(
resource_type: impl Into<Cow<'static, str>>,
identifier: impl Into<Cow<'static, str>>,
) -> Self {
NotFoundError {
resource_type: resource_type.into(),
identifier: identifier.into(),
}
}
}
impl fmt::Display for NotFoundError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{} not found: {}", self.resource_type, self.identifier)
}
}
impl std::error::Error for NotFoundError {}
#[derive(Debug)]
pub struct InvalidParameterError {
pub parameter: Cow<'static, str>,
pub reason: Cow<'static, str>,
}
impl InvalidParameterError {
pub fn new(
parameter: impl Into<Cow<'static, str>>,
reason: impl Into<Cow<'static, str>>,
) -> Self {
InvalidParameterError {
parameter: parameter.into(),
reason: reason.into(),
}
}
}
impl fmt::Display for InvalidParameterError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Invalid parameter '{}': {}", self.parameter, self.reason)
}
}
impl std::error::Error for InvalidParameterError {}
#[derive(Debug)]
pub struct FileOperationError {
pub path: Cow<'static, str>,
pub operation: Cow<'static, str>,
pub error_code: Option<i32>,
}
impl FileOperationError {
pub fn new(
path: impl Into<Cow<'static, str>>,
operation: impl Into<Cow<'static, str>>,
) -> Self {
FileOperationError {
path: path.into(),
operation: operation.into(),
error_code: None,
}
}
pub fn with_code(
path: impl Into<Cow<'static, str>>,
operation: impl Into<Cow<'static, str>>,
error_code: i32,
) -> Self {
FileOperationError {
path: path.into(),
operation: operation.into(),
error_code: Some(error_code),
}
}
}
impl fmt::Display for FileOperationError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(code) = self.error_code {
write!(
f,
"File operation '{}' failed on '{}' (error code: 0x{:08X})",
self.operation, self.path, code
)
} else {
write!(
f,
"File operation '{}' failed on '{}'",
self.operation, self.path
)
}
}
}
impl std::error::Error for FileOperationError {}
#[derive(Debug)]
pub enum PipeError {
Create(PipeCreateError),
Connect(PipeConnectError),
Io(PipeIoError),
Timeout(PipeTimeoutError),
InvalidState(PipeInvalidStateError),
}
impl fmt::Display for PipeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
PipeError::Create(e) => write!(f, "{}", e),
PipeError::Connect(e) => write!(f, "{}", e),
PipeError::Io(e) => write!(f, "{}", e),
PipeError::Timeout(e) => write!(f, "{}", e),
PipeError::InvalidState(e) => write!(f, "{}", e),
}
}
}
impl std::error::Error for PipeError {}
#[derive(Debug)]
pub struct PipeCreateError {
pub pipe_name: Cow<'static, str>,
pub operation: Cow<'static, str>,
pub error_code: Option<i32>,
}
impl PipeCreateError {
pub fn new(
pipe_name: impl Into<Cow<'static, str>>,
operation: impl Into<Cow<'static, str>>,
) -> Self {
PipeCreateError {
pipe_name: pipe_name.into(),
operation: operation.into(),
error_code: None,
}
}
pub fn with_code(
pipe_name: impl Into<Cow<'static, str>>,
operation: impl Into<Cow<'static, str>>,
error_code: i32,
) -> Self {
PipeCreateError {
pipe_name: pipe_name.into(),
operation: operation.into(),
error_code: Some(error_code),
}
}
}
impl fmt::Display for PipeCreateError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(code) = self.error_code {
write!(
f,
"Pipe creation '{}' failed for '{}' (error code: 0x{:08X})",
self.operation, self.pipe_name, code
)
} else {
write!(
f,
"Pipe creation '{}' failed for '{}'",
self.operation, self.pipe_name
)
}
}
}
impl std::error::Error for PipeCreateError {}
#[derive(Debug)]
pub struct PipeConnectError {
pub pipe_name: Cow<'static, str>,
pub context: Option<Cow<'static, str>>,
pub error_code: Option<i32>,
}
impl PipeConnectError {
pub fn new(pipe_name: impl Into<Cow<'static, str>>) -> Self {
PipeConnectError {
pipe_name: pipe_name.into(),
context: None,
error_code: None,
}
}
pub fn with_context(mut self, context: impl Into<Cow<'static, str>>) -> Self {
self.context = Some(context.into());
self
}
pub fn with_code(mut self, error_code: i32) -> Self {
self.error_code = Some(error_code);
self
}
}
impl fmt::Display for PipeConnectError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match (&self.context, self.error_code) {
(Some(context), Some(code)) => write!(
f,
"Pipe connect failed for '{}' ({}, error code: 0x{:08X})",
self.pipe_name, context, code
),
(Some(context), None) => {
write!(
f,
"Pipe connect failed for '{}' ({})",
self.pipe_name, context
)
}
(None, Some(code)) => write!(
f,
"Pipe connect failed for '{}' (error code: 0x{:08X})",
self.pipe_name, code
),
(None, None) => write!(f, "Pipe connect failed for '{}'", self.pipe_name),
}
}
}
impl std::error::Error for PipeConnectError {}
#[derive(Debug)]
pub struct PipeIoError {
pub pipe_name: Cow<'static, str>,
pub operation: Cow<'static, str>,
pub error_code: Option<i32>,
}
impl PipeIoError {
pub fn new(
pipe_name: impl Into<Cow<'static, str>>,
operation: impl Into<Cow<'static, str>>,
) -> Self {
PipeIoError {
pipe_name: pipe_name.into(),
operation: operation.into(),
error_code: None,
}
}
pub fn with_code(
pipe_name: impl Into<Cow<'static, str>>,
operation: impl Into<Cow<'static, str>>,
error_code: i32,
) -> Self {
PipeIoError {
pipe_name: pipe_name.into(),
operation: operation.into(),
error_code: Some(error_code),
}
}
}
impl fmt::Display for PipeIoError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(code) = self.error_code {
write!(
f,
"Pipe I/O '{}' failed for '{}' (error code: 0x{:08X})",
self.operation, self.pipe_name, code
)
} else {
write!(
f,
"Pipe I/O '{}' failed for '{}'",
self.operation, self.pipe_name
)
}
}
}
impl std::error::Error for PipeIoError {}
#[derive(Debug)]
pub struct PipeTimeoutError {
pub pipe_name: Cow<'static, str>,
pub operation: Cow<'static, str>,
}
impl PipeTimeoutError {
pub fn new(
pipe_name: impl Into<Cow<'static, str>>,
operation: impl Into<Cow<'static, str>>,
) -> Self {
PipeTimeoutError {
pipe_name: pipe_name.into(),
operation: operation.into(),
}
}
}
impl fmt::Display for PipeTimeoutError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"Pipe operation '{}' timed out for '{}'",
self.operation, self.pipe_name
)
}
}
impl std::error::Error for PipeTimeoutError {}
#[derive(Debug)]
pub struct PipeInvalidStateError {
pub operation: Cow<'static, str>,
pub reason: Cow<'static, str>,
}
impl PipeInvalidStateError {
pub fn new(
operation: impl Into<Cow<'static, str>>,
reason: impl Into<Cow<'static, str>>,
) -> Self {
PipeInvalidStateError {
operation: operation.into(),
reason: reason.into(),
}
}
}
impl fmt::Display for PipeInvalidStateError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"Pipe operation '{}' is invalid in current state: {}",
self.operation, self.reason
)
}
}
impl std::error::Error for PipeInvalidStateError {}
#[derive(Debug)]
pub struct OtherError {
pub message: Cow<'static, str>,
}
impl OtherError {
pub fn new(message: impl Into<Cow<'static, str>>) -> Self {
OtherError {
message: message.into(),
}
}
}
impl fmt::Display for OtherError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.message)
}
}
impl std::error::Error for OtherError {}
#[derive(Debug)]
pub enum DesktopError {
OperationFailed(DesktopOperationError),
WindowNotFound(WindowNotFoundError),
}
impl fmt::Display for DesktopError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
DesktopError::OperationFailed(e) => write!(f, "{}", e),
DesktopError::WindowNotFound(e) => write!(f, "{}", e),
}
}
}
impl std::error::Error for DesktopError {}
#[derive(Debug)]
pub struct DesktopOperationError {
pub operation: Cow<'static, str>,
pub target: Cow<'static, str>,
pub error_code: Option<i32>,
}
impl DesktopOperationError {
pub fn new(
operation: impl Into<Cow<'static, str>>,
target: impl Into<Cow<'static, str>>,
) -> Self {
DesktopOperationError {
operation: operation.into(),
target: target.into(),
error_code: None,
}
}
pub fn with_code(
operation: impl Into<Cow<'static, str>>,
target: impl Into<Cow<'static, str>>,
error_code: i32,
) -> Self {
DesktopOperationError {
operation: operation.into(),
target: target.into(),
error_code: Some(error_code),
}
}
}
impl fmt::Display for DesktopOperationError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(code) = self.error_code {
write!(
f,
"Desktop operation '{}' failed for '{}' (error code: 0x{:08X})",
self.operation, self.target, code
)
} else {
write!(
f,
"Desktop operation '{}' failed for '{}'",
self.operation, self.target
)
}
}
}
impl std::error::Error for DesktopOperationError {}
#[derive(Debug)]
pub struct WindowNotFoundError {
pub identifier: Cow<'static, str>,
}
impl WindowNotFoundError {
pub fn new(identifier: impl Into<Cow<'static, str>>) -> Self {
WindowNotFoundError {
identifier: identifier.into(),
}
}
}
impl fmt::Display for WindowNotFoundError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Window not found: {}", self.identifier)
}
}
impl std::error::Error for WindowNotFoundError {}
#[derive(Debug)]
pub enum SecurityError {
SidParse(SidParseError),
PermissionEdit(PermissionEditError),
Unsupported(SecurityUnsupportedError),
}
impl fmt::Display for SecurityError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
SecurityError::SidParse(e) => write!(f, "{}", e),
SecurityError::PermissionEdit(e) => write!(f, "{}", e),
SecurityError::Unsupported(e) => write!(f, "{}", e),
}
}
}
impl std::error::Error for SecurityError {}
#[derive(Debug)]
pub struct SidParseError {
pub input: Cow<'static, str>,
pub reason: Cow<'static, str>,
}
impl SidParseError {
pub fn new(input: impl Into<Cow<'static, str>>, reason: impl Into<Cow<'static, str>>) -> Self {
SidParseError {
input: input.into(),
reason: reason.into(),
}
}
}
impl fmt::Display for SidParseError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "SID parse error for '{}': {}", self.input, self.reason)
}
}
impl std::error::Error for SidParseError {}
#[derive(Debug)]
pub struct PermissionEditError {
pub operation: Cow<'static, str>,
pub reason: Cow<'static, str>,
}
impl PermissionEditError {
pub fn new(
operation: impl Into<Cow<'static, str>>,
reason: impl Into<Cow<'static, str>>,
) -> Self {
PermissionEditError {
operation: operation.into(),
reason: reason.into(),
}
}
}
impl fmt::Display for PermissionEditError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"Permission edit '{}' failed: {}",
self.operation, self.reason
)
}
}
impl std::error::Error for PermissionEditError {}
#[derive(Debug)]
pub struct SecurityUnsupportedError {
pub target: Cow<'static, str>,
pub operation: Cow<'static, str>,
pub reason: Option<Cow<'static, str>>,
}
impl SecurityUnsupportedError {
pub fn new(
target: impl Into<Cow<'static, str>>,
operation: impl Into<Cow<'static, str>>,
) -> Self {
SecurityUnsupportedError {
target: target.into(),
operation: operation.into(),
reason: None,
}
}
pub fn with_reason(
target: impl Into<Cow<'static, str>>,
operation: impl Into<Cow<'static, str>>,
reason: impl Into<Cow<'static, str>>,
) -> Self {
SecurityUnsupportedError {
target: target.into(),
operation: operation.into(),
reason: Some(reason.into()),
}
}
}
impl fmt::Display for SecurityUnsupportedError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(reason) = &self.reason {
write!(
f,
"Security operation '{}' is not supported for '{}': {}",
self.operation, self.target, reason
)
} else {
write!(
f,
"Security operation '{}' is not supported for '{}'",
self.operation, self.target
)
}
}
}
impl std::error::Error for SecurityUnsupportedError {}
#[derive(Debug)]
pub enum ProxyError {
InvalidConfig(ProxyConfigError),
DiscoveryFailed(ProxyConfigError),
ResolutionFailed(ProxyResolutionError),
}
impl fmt::Display for ProxyError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ProxyError::InvalidConfig(e) => write!(f, "{}", e),
ProxyError::DiscoveryFailed(e) => write!(f, "{}", e),
ProxyError::ResolutionFailed(e) => write!(f, "{}", e),
}
}
}
impl std::error::Error for ProxyError {}
#[derive(Debug)]
pub struct ProxyConfigError {
pub setting: Cow<'static, str>,
pub reason: Cow<'static, str>,
}
impl ProxyConfigError {
pub fn new(
setting: impl Into<Cow<'static, str>>,
reason: impl Into<Cow<'static, str>>,
) -> Self {
ProxyConfigError {
setting: setting.into(),
reason: reason.into(),
}
}
}
impl fmt::Display for ProxyConfigError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"Proxy configuration error for '{}': {}",
self.setting, self.reason
)
}
}
impl std::error::Error for ProxyConfigError {}
#[derive(Debug)]
pub struct ProxyResolutionError {
pub url: Cow<'static, str>,
pub reason: Cow<'static, str>,
}
impl ProxyResolutionError {
pub fn new(url: impl Into<Cow<'static, str>>, reason: impl Into<Cow<'static, str>>) -> Self {
ProxyResolutionError {
url: url.into(),
reason: reason.into(),
}
}
}
impl fmt::Display for ProxyResolutionError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"Proxy resolution failed for '{}': {}",
self.url, self.reason
)
}
}
impl std::error::Error for ProxyResolutionError {}
#[derive(Debug)]
pub enum RegistryError {
KeyNotFound(RegistryKeyNotFoundError),
ValueNotFound(RegistryValueNotFoundError),
InvalidType(RegistryInvalidTypeError),
ConversionError(RegistryConversionError),
}
impl fmt::Display for RegistryError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
RegistryError::KeyNotFound(e) => write!(f, "{}", e),
RegistryError::ValueNotFound(e) => write!(f, "{}", e),
RegistryError::InvalidType(e) => write!(f, "{}", e),
RegistryError::ConversionError(e) => write!(f, "{}", e),
}
}
}
impl std::error::Error for RegistryError {}
#[derive(Debug)]
pub struct RegistryKeyNotFoundError {
pub key_path: Cow<'static, str>,
pub error_code: Option<i32>,
}
impl RegistryKeyNotFoundError {
pub fn new(key_path: impl Into<Cow<'static, str>>) -> Self {
RegistryKeyNotFoundError {
key_path: key_path.into(),
error_code: None,
}
}
pub fn with_code(key_path: impl Into<Cow<'static, str>>, error_code: i32) -> Self {
RegistryKeyNotFoundError {
key_path: key_path.into(),
error_code: Some(error_code),
}
}
}
impl fmt::Display for RegistryKeyNotFoundError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(code) = self.error_code {
write!(
f,
"Registry key not found: {} (error code: 0x{:08X})",
self.key_path, code
)
} else {
write!(f, "Registry key not found: {}", self.key_path)
}
}
}
impl std::error::Error for RegistryKeyNotFoundError {}
#[derive(Debug)]
pub struct RegistryValueNotFoundError {
pub value_name: Cow<'static, str>,
pub key_path: Option<Cow<'static, str>>,
}
impl RegistryValueNotFoundError {
pub fn new(value_name: impl Into<Cow<'static, str>>) -> Self {
RegistryValueNotFoundError {
value_name: value_name.into(),
key_path: None,
}
}
pub fn with_key(
value_name: impl Into<Cow<'static, str>>,
key_path: impl Into<Cow<'static, str>>,
) -> Self {
RegistryValueNotFoundError {
value_name: value_name.into(),
key_path: Some(key_path.into()),
}
}
}
impl fmt::Display for RegistryValueNotFoundError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(key) = &self.key_path {
write!(
f,
"Registry value '{}' not found in key '{}'",
self.value_name, key
)
} else {
write!(f, "Registry value not found: {}", self.value_name)
}
}
}
impl std::error::Error for RegistryValueNotFoundError {}
#[derive(Debug)]
pub struct RegistryInvalidTypeError {
pub expected: Cow<'static, str>,
pub found: Cow<'static, str>,
pub value_name: Option<Cow<'static, str>>,
}
impl RegistryInvalidTypeError {
pub fn new(
expected: impl Into<Cow<'static, str>>,
found: impl Into<Cow<'static, str>>,
) -> Self {
RegistryInvalidTypeError {
expected: expected.into(),
found: found.into(),
value_name: None,
}
}
pub fn with_name(
expected: impl Into<Cow<'static, str>>,
found: impl Into<Cow<'static, str>>,
value_name: impl Into<Cow<'static, str>>,
) -> Self {
RegistryInvalidTypeError {
expected: expected.into(),
found: found.into(),
value_name: Some(value_name.into()),
}
}
}
impl fmt::Display for RegistryInvalidTypeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(name) = &self.value_name {
write!(
f,
"Invalid registry type for '{}': expected {}, found {}",
name, self.expected, self.found
)
} else {
write!(
f,
"Invalid registry type: expected {}, found {}",
self.expected, self.found
)
}
}
}
impl std::error::Error for RegistryInvalidTypeError {}
#[derive(Debug)]
pub struct RegistryConversionError {
pub conversion: Cow<'static, str>,
pub reason: Cow<'static, str>,
}
impl RegistryConversionError {
pub fn new(
conversion: impl Into<Cow<'static, str>>,
reason: impl Into<Cow<'static, str>>,
) -> Self {
RegistryConversionError {
conversion: conversion.into(),
reason: reason.into(),
}
}
}
impl fmt::Display for RegistryConversionError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"Registry conversion error ({}): {}",
self.conversion, self.reason
)
}
}
impl std::error::Error for RegistryConversionError {}
#[derive(Debug)]
pub enum ServiceError {
ManagerError(ServiceManagerError),
NotFound(ServiceNotFoundError),
OperationFailed(ServiceOperationError),
InvalidState(ServiceInvalidStateError),
}
impl fmt::Display for ServiceError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ServiceError::ManagerError(e) => write!(f, "{}", e),
ServiceError::NotFound(e) => write!(f, "{}", e),
ServiceError::OperationFailed(e) => write!(f, "{}", e),
ServiceError::InvalidState(e) => write!(f, "{}", e),
}
}
}
impl std::error::Error for ServiceError {}
#[derive(Debug)]
pub struct ServiceManagerError {
pub operation: Cow<'static, str>,
pub reason: Cow<'static, str>,
pub error_code: Option<i32>,
}
impl ServiceManagerError {
pub fn new(
operation: impl Into<Cow<'static, str>>,
reason: impl Into<Cow<'static, str>>,
) -> Self {
ServiceManagerError {
operation: operation.into(),
reason: reason.into(),
error_code: None,
}
}
pub fn with_code(
operation: impl Into<Cow<'static, str>>,
reason: impl Into<Cow<'static, str>>,
error_code: i32,
) -> Self {
ServiceManagerError {
operation: operation.into(),
reason: reason.into(),
error_code: Some(error_code),
}
}
}
impl fmt::Display for ServiceManagerError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(code) = self.error_code {
write!(
f,
"Service manager operation '{}' failed: {} (error code: 0x{:08X})",
self.operation, self.reason, code
)
} else {
write!(
f,
"Service manager operation '{}' failed: {}",
self.operation, self.reason
)
}
}
}
impl std::error::Error for ServiceManagerError {}
#[derive(Debug)]
pub struct ServiceNotFoundError {
pub name: Cow<'static, str>,
pub error_code: Option<i32>,
}
impl ServiceNotFoundError {
pub fn new(name: impl Into<Cow<'static, str>>) -> Self {
ServiceNotFoundError {
name: name.into(),
error_code: None,
}
}
pub fn with_code(name: impl Into<Cow<'static, str>>, error_code: i32) -> Self {
ServiceNotFoundError {
name: name.into(),
error_code: Some(error_code),
}
}
}
impl fmt::Display for ServiceNotFoundError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(code) = self.error_code {
write!(
f,
"Service '{}' not found (error code: 0x{:08X})",
self.name, code
)
} else {
write!(f, "Service '{}' not found", self.name)
}
}
}
impl std::error::Error for ServiceNotFoundError {}
#[derive(Debug)]
pub struct ServiceOperationError {
pub name: Cow<'static, str>,
pub operation: Cow<'static, str>,
pub reason: Cow<'static, str>,
pub error_code: Option<i32>,
}
impl ServiceOperationError {
pub fn new(
name: impl Into<Cow<'static, str>>,
operation: impl Into<Cow<'static, str>>,
reason: impl Into<Cow<'static, str>>,
) -> Self {
ServiceOperationError {
name: name.into(),
operation: operation.into(),
reason: reason.into(),
error_code: None,
}
}
pub fn with_code(
name: impl Into<Cow<'static, str>>,
operation: impl Into<Cow<'static, str>>,
reason: impl Into<Cow<'static, str>>,
error_code: i32,
) -> Self {
ServiceOperationError {
name: name.into(),
operation: operation.into(),
reason: reason.into(),
error_code: Some(error_code),
}
}
}
impl fmt::Display for ServiceOperationError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(code) = self.error_code {
write!(
f,
"Service '{}' operation '{}' failed: {} (error code: 0x{:08X})",
self.name, self.operation, self.reason, code
)
} else {
write!(
f,
"Service '{}' operation '{}' failed: {}",
self.name, self.operation, self.reason
)
}
}
}
impl std::error::Error for ServiceOperationError {}
#[derive(Debug)]
pub struct ServiceInvalidStateError {
pub name: Cow<'static, str>,
pub expected: Cow<'static, str>,
pub reason: Cow<'static, str>,
}
impl ServiceInvalidStateError {
pub fn new(
name: impl Into<Cow<'static, str>>,
expected: impl Into<Cow<'static, str>>,
reason: impl Into<Cow<'static, str>>,
) -> Self {
ServiceInvalidStateError {
name: name.into(),
expected: expected.into(),
reason: reason.into(),
}
}
}
impl fmt::Display for ServiceInvalidStateError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"Service '{}' is not in expected state '{}': {}",
self.name, self.expected, self.reason
)
}
}
impl std::error::Error for ServiceInvalidStateError {}
#[derive(Debug)]
pub enum ProcessError {
NotFound(ProcessNotFoundError),
AlreadyTerminated(ProcessTerminatedError),
OpenFailed(ProcessOpenError),
SpawnFailed(ProcessSpawnError),
InvalidProcessId,
InjectionFailed(InjectionFailedError),
AlreadyInjected(AlreadyInjectedError),
}
impl fmt::Display for ProcessError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ProcessError::NotFound(e) => write!(f, "{}", e),
ProcessError::AlreadyTerminated(e) => write!(f, "{}", e),
ProcessError::OpenFailed(e) => write!(f, "{}", e),
ProcessError::SpawnFailed(e) => write!(f, "{}", e),
ProcessError::InvalidProcessId => write!(f, "Invalid process ID"),
ProcessError::InjectionFailed(e) => write!(f, "{}", e),
ProcessError::AlreadyInjected(e) => write!(f, "{}", e),
}
}
}
impl std::error::Error for ProcessError {}
#[derive(Debug)]
pub struct ProcessNotFoundError {
pub pid: u32,
}
impl ProcessNotFoundError {
pub fn new(pid: u32) -> Self {
ProcessNotFoundError { pid }
}
}
impl fmt::Display for ProcessNotFoundError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Process {} not found", self.pid)
}
}
impl std::error::Error for ProcessNotFoundError {}
#[derive(Debug)]
pub struct ProcessTerminatedError {
pub pid: u32,
pub exit_code: Option<u32>,
}
impl ProcessTerminatedError {
pub fn new(pid: u32) -> Self {
ProcessTerminatedError {
pid,
exit_code: None,
}
}
pub fn with_exit_code(pid: u32, exit_code: u32) -> Self {
ProcessTerminatedError {
pid,
exit_code: Some(exit_code),
}
}
}
impl fmt::Display for ProcessTerminatedError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(code) = self.exit_code {
write!(
f,
"Process {} already terminated (exit code: {})",
self.pid, code
)
} else {
write!(f, "Process {} already terminated", self.pid)
}
}
}
impl std::error::Error for ProcessTerminatedError {}
#[derive(Debug)]
pub struct ProcessOpenError {
pub pid: u32,
pub reason: Cow<'static, str>,
pub error_code: Option<i32>,
}
impl ProcessOpenError {
pub fn new(pid: u32, reason: impl Into<Cow<'static, str>>) -> Self {
ProcessOpenError {
pid,
reason: reason.into(),
error_code: None,
}
}
pub fn with_code(pid: u32, reason: impl Into<Cow<'static, str>>, error_code: i32) -> Self {
ProcessOpenError {
pid,
reason: reason.into(),
error_code: Some(error_code),
}
}
}
impl fmt::Display for ProcessOpenError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(code) = self.error_code {
write!(
f,
"Failed to open process {}: {} (error code: 0x{:08X})",
self.pid, self.reason, code
)
} else {
write!(f, "Failed to open process {}: {}", self.pid, self.reason)
}
}
}
impl std::error::Error for ProcessOpenError {}
#[derive(Debug)]
pub struct ProcessSpawnError {
pub command: Cow<'static, str>,
pub reason: Cow<'static, str>,
pub error_code: Option<i32>,
}
impl ProcessSpawnError {
pub fn new(
command: impl Into<Cow<'static, str>>,
reason: impl Into<Cow<'static, str>>,
) -> Self {
ProcessSpawnError {
command: command.into(),
reason: reason.into(),
error_code: None,
}
}
pub fn with_code(
command: impl Into<Cow<'static, str>>,
reason: impl Into<Cow<'static, str>>,
error_code: i32,
) -> Self {
ProcessSpawnError {
command: command.into(),
reason: reason.into(),
error_code: Some(error_code),
}
}
}
impl fmt::Display for ProcessSpawnError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(code) = self.error_code {
write!(
f,
"Failed to spawn process '{}': {} (error code: 0x{:08X})",
self.command, self.reason, code
)
} else {
write!(
f,
"Failed to spawn process '{}': {}",
self.command, self.reason
)
}
}
}
impl std::error::Error for ProcessSpawnError {}
#[derive(Debug)]
pub struct InjectionFailedError {
pub pid: u32,
pub reason: Cow<'static, str>,
pub error_code: Option<i32>,
}
impl InjectionFailedError {
pub fn new(pid: u32, reason: impl Into<Cow<'static, str>>) -> Self {
InjectionFailedError {
pid,
reason: reason.into(),
error_code: None,
}
}
pub fn with_code(pid: u32, reason: impl Into<Cow<'static, str>>, error_code: i32) -> Self {
InjectionFailedError {
pid,
reason: reason.into(),
error_code: Some(error_code),
}
}
}
impl fmt::Display for InjectionFailedError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(code) = self.error_code {
write!(
f,
"DLL injection into process {} failed: {} (error code: 0x{:08X})",
self.pid, self.reason, code
)
} else {
write!(
f,
"DLL injection into process {} failed: {}",
self.pid, self.reason
)
}
}
}
impl std::error::Error for InjectionFailedError {}
#[derive(Debug)]
pub struct AlreadyInjectedError {
pub pid: u32,
pub dll_name: Cow<'static, str>,
}
impl AlreadyInjectedError {
pub fn new(pid: u32, dll_name: impl Into<Cow<'static, str>>) -> Self {
AlreadyInjectedError {
pid,
dll_name: dll_name.into(),
}
}
}
impl fmt::Display for AlreadyInjectedError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"DLL '{}' is already loaded in process {}",
self.dll_name, self.pid
)
}
}
impl std::error::Error for AlreadyInjectedError {}
#[derive(Debug)]
pub enum ThreadError {
NotFound(ThreadNotFoundError),
OpenFailed(ThreadOpenError),
}
impl fmt::Display for ThreadError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ThreadError::NotFound(e) => write!(f, "{}", e),
ThreadError::OpenFailed(e) => write!(f, "{}", e),
}
}
}
impl std::error::Error for ThreadError {}
#[derive(Debug)]
pub struct ThreadNotFoundError {
pub tid: u32,
}
impl ThreadNotFoundError {
pub fn new(tid: u32) -> Self {
ThreadNotFoundError { tid }
}
}
impl fmt::Display for ThreadNotFoundError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Thread {} not found", self.tid)
}
}
impl std::error::Error for ThreadNotFoundError {}
#[derive(Debug)]
pub struct ThreadOpenError {
pub tid: u32,
pub reason: Cow<'static, str>,
pub error_code: Option<i32>,
}
impl ThreadOpenError {
pub fn new(tid: u32, reason: impl Into<Cow<'static, str>>) -> Self {
ThreadOpenError {
tid,
reason: reason.into(),
error_code: None,
}
}
pub fn with_code(tid: u32, reason: impl Into<Cow<'static, str>>, error_code: i32) -> Self {
ThreadOpenError {
tid,
reason: reason.into(),
error_code: Some(error_code),
}
}
}
impl fmt::Display for ThreadOpenError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(code) = self.error_code {
write!(
f,
"Failed to open thread {}: {} (error code: 0x{:08X})",
self.tid, self.reason, code
)
} else {
write!(f, "Failed to open thread {}: {}", self.tid, self.reason)
}
}
}
impl std::error::Error for ThreadOpenError {}
#[derive(Debug)]
pub enum EventLogError {
LogNotFound(EventLogNotFoundError),
QueryFailed(EventLogQueryError),
ParseFailed(EventLogParseError),
}
impl fmt::Display for EventLogError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
EventLogError::LogNotFound(e) => write!(f, "{}", e),
EventLogError::QueryFailed(e) => write!(f, "{}", e),
EventLogError::ParseFailed(e) => write!(f, "{}", e),
}
}
}
impl std::error::Error for EventLogError {}
#[derive(Debug)]
pub struct EventLogNotFoundError {
pub log_name: Cow<'static, str>,
}
impl EventLogNotFoundError {
pub fn new(log_name: impl Into<Cow<'static, str>>) -> Self {
EventLogNotFoundError {
log_name: log_name.into(),
}
}
}
impl fmt::Display for EventLogNotFoundError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Event log not found: {}", self.log_name)
}
}
impl std::error::Error for EventLogNotFoundError {}
#[derive(Debug)]
pub struct EventLogQueryError {
pub log_name: Cow<'static, str>,
pub reason: Cow<'static, str>,
pub error_code: Option<i32>,
}
impl EventLogQueryError {
pub fn new(
log_name: impl Into<Cow<'static, str>>,
reason: impl Into<Cow<'static, str>>,
) -> Self {
EventLogQueryError {
log_name: log_name.into(),
reason: reason.into(),
error_code: None,
}
}
pub fn with_code(
log_name: impl Into<Cow<'static, str>>,
reason: impl Into<Cow<'static, str>>,
error_code: i32,
) -> Self {
EventLogQueryError {
log_name: log_name.into(),
reason: reason.into(),
error_code: Some(error_code),
}
}
}
impl fmt::Display for EventLogQueryError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(code) = self.error_code {
write!(
f,
"Failed to query event log '{}': {} (error code: 0x{:08X})",
self.log_name, self.reason, code
)
} else {
write!(
f,
"Failed to query event log '{}': {}",
self.log_name, self.reason
)
}
}
}
impl std::error::Error for EventLogQueryError {}
#[derive(Debug)]
pub struct EventLogParseError {
pub component: Cow<'static, str>,
pub reason: Cow<'static, str>,
}
impl EventLogParseError {
pub fn new(
component: impl Into<Cow<'static, str>>,
reason: impl Into<Cow<'static, str>>,
) -> Self {
EventLogParseError {
component: component.into(),
reason: reason.into(),
}
}
}
impl fmt::Display for EventLogParseError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Failed to parse {}: {}", self.component, self.reason)
}
}
impl std::error::Error for EventLogParseError {}
#[derive(Debug)]
pub enum EtwError {
SessionStartFailed(EtwSessionError),
ProviderEnableFailed(EtwProviderError),
ConsumeFailed(EtwConsumeError),
}
impl fmt::Display for EtwError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
EtwError::SessionStartFailed(e) => write!(f, "{}", e),
EtwError::ProviderEnableFailed(e) => write!(f, "{}", e),
EtwError::ConsumeFailed(e) => write!(f, "{}", e),
}
}
}
impl std::error::Error for EtwError {}
#[derive(Debug)]
pub struct EtwSessionError {
pub session_name: Cow<'static, str>,
pub reason: Cow<'static, str>,
pub error_code: Option<i32>,
}
impl EtwSessionError {
pub fn new(
session_name: impl Into<Cow<'static, str>>,
reason: impl Into<Cow<'static, str>>,
) -> Self {
EtwSessionError {
session_name: session_name.into(),
reason: reason.into(),
error_code: None,
}
}
pub fn with_code(
session_name: impl Into<Cow<'static, str>>,
reason: impl Into<Cow<'static, str>>,
error_code: i32,
) -> Self {
EtwSessionError {
session_name: session_name.into(),
reason: reason.into(),
error_code: Some(error_code),
}
}
pub fn already_exists(session_name: impl Into<Cow<'static, str>>) -> Self {
EtwSessionError {
session_name: session_name.into(),
reason: Cow::Borrowed("Session already exists"),
error_code: Some(-2147024713), }
}
pub fn invalid_config(
session_name: impl Into<Cow<'static, str>>,
field: &'static str,
issue: impl Into<Cow<'static, str>>,
) -> Self {
EtwSessionError {
session_name: session_name.into(),
reason: Cow::Owned(format!("Invalid {}: {}", field, issue.into())),
error_code: None,
}
}
}
impl fmt::Display for EtwSessionError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(code) = self.error_code {
write!(
f,
"Failed to start ETW session '{}': {} (error code: 0x{:08X})",
self.session_name, self.reason, code
)
} else {
write!(
f,
"Failed to start ETW session '{}': {}",
self.session_name, self.reason
)
}
}
}
impl std::error::Error for EtwSessionError {}
#[derive(Debug)]
pub struct EtwProviderError {
pub provider: Cow<'static, str>,
pub reason: Cow<'static, str>,
pub error_code: Option<i32>,
}
impl EtwProviderError {
pub fn new(
provider: impl Into<Cow<'static, str>>,
reason: impl Into<Cow<'static, str>>,
) -> Self {
EtwProviderError {
provider: provider.into(),
reason: reason.into(),
error_code: None,
}
}
pub fn with_code(
provider: impl Into<Cow<'static, str>>,
reason: impl Into<Cow<'static, str>>,
error_code: i32,
) -> Self {
EtwProviderError {
provider: provider.into(),
reason: reason.into(),
error_code: Some(error_code),
}
}
pub fn not_found(provider: impl Into<Cow<'static, str>>) -> Self {
EtwProviderError {
provider: provider.into(),
reason: Cow::Borrowed("Provider not registered"),
error_code: Some(0x00000490), }
}
}
impl fmt::Display for EtwProviderError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(code) = self.error_code {
write!(
f,
"Failed to enable ETW provider '{}': {} (error code: 0x{:08X})",
self.provider, self.reason, code
)
} else {
write!(
f,
"Failed to enable ETW provider '{}': {}",
self.provider, self.reason
)
}
}
}
impl std::error::Error for EtwProviderError {}
#[derive(Debug)]
pub struct EtwConsumeError {
pub reason: Cow<'static, str>,
pub error_code: Option<i32>,
}
impl EtwConsumeError {
pub fn new(reason: impl Into<Cow<'static, str>>) -> Self {
EtwConsumeError {
reason: reason.into(),
error_code: None,
}
}
pub fn with_code(reason: impl Into<Cow<'static, str>>, error_code: i32) -> Self {
EtwConsumeError {
reason: reason.into(),
error_code: Some(error_code),
}
}
}
impl fmt::Display for EtwConsumeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(code) = self.error_code {
write!(
f,
"Failed to consume ETW events: {} (error code: 0x{:08X})",
self.reason, code
)
} else {
write!(f, "Failed to consume ETW events: {}", self.reason)
}
}
}
impl std::error::Error for EtwConsumeError {}
#[derive(Debug)]
pub enum MitigationError {
ApplyFailed(MitigationOperationError),
QueryFailed(MitigationOperationError),
}
impl fmt::Display for MitigationError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
MitigationError::ApplyFailed(e) => write!(f, "{}", e),
MitigationError::QueryFailed(e) => write!(f, "{}", e),
}
}
}
impl std::error::Error for MitigationError {}
#[derive(Debug)]
pub struct MitigationOperationError {
pub operation: Cow<'static, str>,
pub policy: Cow<'static, str>,
pub process_id: Option<u32>,
pub reason: Option<Cow<'static, str>>,
pub error_code: Option<i32>,
}
impl MitigationOperationError {
pub fn new(
operation: impl Into<Cow<'static, str>>,
policy: impl Into<Cow<'static, str>>,
) -> Self {
MitigationOperationError {
operation: operation.into(),
policy: policy.into(),
process_id: None,
reason: None,
error_code: None,
}
}
pub fn with_process_id(mut self, process_id: u32) -> Self {
self.process_id = Some(process_id);
self
}
pub fn with_reason(mut self, reason: impl Into<Cow<'static, str>>) -> Self {
self.reason = Some(reason.into());
self
}
pub fn with_code(mut self, error_code: i32) -> Self {
self.error_code = Some(error_code);
self
}
}
impl fmt::Display for MitigationOperationError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match (self.process_id, &self.reason, self.error_code) {
(Some(pid), Some(reason), Some(code)) => write!(
f,
"Mitigation {} failed for policy '{}' on process {}: {} (error code: 0x{:08X})",
self.operation, self.policy, pid, reason, code
),
(Some(pid), Some(reason), None) => write!(
f,
"Mitigation {} failed for policy '{}' on process {}: {}",
self.operation, self.policy, pid, reason
),
(Some(pid), None, Some(code)) => write!(
f,
"Mitigation {} failed for policy '{}' on process {} (error code: 0x{:08X})",
self.operation, self.policy, pid, code
),
(Some(pid), None, None) => write!(
f,
"Mitigation {} failed for policy '{}' on process {}",
self.operation, self.policy, pid
),
(None, Some(reason), Some(code)) => write!(
f,
"Mitigation {} failed for policy '{}': {} (error code: 0x{:08X})",
self.operation, self.policy, reason, code
),
(None, Some(reason), None) => write!(
f,
"Mitigation {} failed for policy '{}': {}",
self.operation, self.policy, reason
),
(None, None, Some(code)) => write!(
f,
"Mitigation {} failed for policy '{}' (error code: 0x{:08X})",
self.operation, self.policy, code
),
(None, None, None) => write!(
f,
"Mitigation {} failed for policy '{}'",
self.operation, self.policy
),
}
}
}
impl std::error::Error for MitigationOperationError {}
#[cfg(test)]
mod tests {
use super::{EtwConsumeError, EtwProviderError, EtwSessionError, MitigationOperationError};
use std::borrow::Cow;
#[test]
fn etw_session_error_invalid_config_message_contains_field() {
let err = EtwSessionError::invalid_config(
Cow::Borrowed("MySession"),
"providers",
Cow::Borrowed("cannot mix kernel and user providers"),
);
assert_eq!(err.session_name, "MySession");
assert!(err.reason.contains("Invalid providers"));
assert!(err.reason.contains("cannot mix kernel and user providers"));
assert!(err.error_code.is_none());
}
#[test]
fn etw_provider_error_not_found_has_expected_code() {
let err = EtwProviderError::not_found(Cow::Borrowed("{provider-guid}"));
assert_eq!(err.provider, "{provider-guid}");
assert_eq!(err.error_code, Some(0x00000490));
assert!(err.reason.contains("Provider not registered"));
}
#[test]
fn etw_consume_error_display_with_code_contains_hex() {
let err = EtwConsumeError::with_code(Cow::Borrowed("OpenTraceW failed"), 0x57);
let rendered = err.to_string();
assert!(rendered.contains("OpenTraceW failed"));
assert!(rendered.contains("0x00000057"));
}
#[test]
fn mitigation_operation_error_display_includes_policy_pid_and_code() {
let err = MitigationOperationError::new("apply", "dynamic_code")
.with_process_id(1234)
.with_reason("Access denied")
.with_code(5);
let rendered = err.to_string();
assert!(rendered.contains("dynamic_code"));
assert!(rendered.contains("1234"));
assert!(rendered.contains("0x00000005"));
}
}