use thiserror::Error;
use windows::Win32::Foundation::WIN32_ERROR;
#[derive(Error, Debug)]
pub enum WfpError {
#[error("Failed to open WFP engine session")]
EngineOpenFailed,
#[error("Failed to close WFP engine session")]
EngineCloseFailed,
#[error("Failed to add filter: {0}")]
FilterAddFailed(String),
#[error("Failed to delete filter: {0}")]
FilterDeleteFailed(String),
#[error("Failed to begin WFP transaction")]
TransactionBeginFailed,
#[error("Failed to commit WFP transaction")]
TransactionCommitFailed,
#[error("Failed to abort WFP transaction")]
TransactionAbortFailed,
#[error("Application path not found or invalid: {0}")]
AppPathNotFound(String),
#[error("Insufficient permissions - administrator privileges required")]
InsufficientPermissions,
#[error("Windows Filtering Platform service not available")]
ServiceNotAvailable,
#[error("Win32 error {code}: {message}")]
Win32Error { code: u32, message: String },
#[error("{0}")]
Other(String),
}
impl From<WIN32_ERROR> for WfpError {
fn from(error: WIN32_ERROR) -> Self {
let code = error.0;
let message = match code {
5 => "Access denied".to_string(),
1062 => "Service not started".to_string(),
1075 => "Service dependency does not exist".to_string(),
_ => format!("Unknown Win32 error: {}", code),
};
WfpError::Win32Error { code, message }
}
}
pub type WfpResult<T> = std::result::Result<T, WfpError>;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_error_display_messages() {
assert_eq!(
WfpError::EngineOpenFailed.to_string(),
"Failed to open WFP engine session"
);
assert_eq!(
WfpError::InsufficientPermissions.to_string(),
"Insufficient permissions - administrator privileges required"
);
assert_eq!(
WfpError::ServiceNotAvailable.to_string(),
"Windows Filtering Platform service not available"
);
assert_eq!(
WfpError::TransactionBeginFailed.to_string(),
"Failed to begin WFP transaction"
);
assert_eq!(
WfpError::TransactionCommitFailed.to_string(),
"Failed to commit WFP transaction"
);
assert_eq!(
WfpError::TransactionAbortFailed.to_string(),
"Failed to abort WFP transaction"
);
assert_eq!(
WfpError::AppPathNotFound(r"C:\missing.exe".into()).to_string(),
r"Application path not found or invalid: C:\missing.exe"
);
}
#[test]
fn test_error_display_with_details() {
let err = WfpError::FilterAddFailed("test error".into());
assert_eq!(err.to_string(), "Failed to add filter: test error");
let err = WfpError::FilterDeleteFailed("filter 42".into());
assert_eq!(err.to_string(), "Failed to delete filter: filter 42");
let err = WfpError::Other("something happened".into());
assert_eq!(err.to_string(), "something happened");
}
#[test]
fn test_win32_error_display() {
let err = WfpError::Win32Error {
code: 5,
message: "Access denied".into(),
};
assert_eq!(err.to_string(), "Win32 error 5: Access denied");
}
#[test]
fn test_win32_error_conversion_access_denied() {
let err: WfpError = WIN32_ERROR(5).into();
match err {
WfpError::Win32Error { code, message } => {
assert_eq!(code, 5);
assert_eq!(message, "Access denied");
}
_ => panic!("Expected Win32Error"),
}
}
#[test]
fn test_win32_error_conversion_service_not_started() {
let err: WfpError = WIN32_ERROR(1062).into();
match err {
WfpError::Win32Error { code, message } => {
assert_eq!(code, 1062);
assert_eq!(message, "Service not started");
}
_ => panic!("Expected Win32Error"),
}
}
#[test]
fn test_win32_error_conversion_dependency_missing() {
let err: WfpError = WIN32_ERROR(1075).into();
match err {
WfpError::Win32Error { code, message } => {
assert_eq!(code, 1075);
assert_eq!(message, "Service dependency does not exist");
}
_ => panic!("Expected Win32Error"),
}
}
#[test]
fn test_win32_error_conversion_unknown() {
let err: WfpError = WIN32_ERROR(9999).into();
match err {
WfpError::Win32Error { code, message } => {
assert_eq!(code, 9999);
assert!(message.contains("Unknown"));
}
_ => panic!("Expected Win32Error"),
}
}
}