wdext 0.1.0

A DbgEng wrapper framework
// SPDX-FileCopyrightText: 2026 takubokudori
// SPDX-License-Identifier: MIT OR Apache-2.0
use crate::{
    DebuggeeOffset, WdResult, WideStrArg,
    data::{DebugValueType, VarType},
    dbgmodel::{DataModelManager, ModelObject},
    wd::dbgmodel::WdModelErrorObject,
};
use std::{
    fmt,
    num::{ParseIntError, TryFromIntError},
};
use thiserror::Error;
use windows::{
    Win32::Foundation::{
        E_FAIL, E_INVALIDARG, E_NOINTERFACE, E_UNEXPECTED, ERROR_READ_FAULT,
        ERROR_TIMEOUT, ERROR_WRITE_FAULT,
    },
    core::HRESULT,
};
use windy::ConvertError;

pub const E_WRITE_FAULT: HRESULT = HRESULT::from_win32(ERROR_WRITE_FAULT.0);
pub const E_READ_FAULT: HRESULT = HRESULT::from_win32(ERROR_READ_FAULT.0);
pub const E_PENDING: HRESULT = HRESULT(0x8000000a_u32 as i32);
pub const E_INTERRUPTED: HRESULT = HRESULT(0xD000013A_u32 as i32);

#[derive(Error, Debug, Clone, Eq, PartialEq)]
pub enum WdErrorKind {
    #[error("debug value coercion from {from:?} to {to:?} failed")]
    ValueCoercionFailed {
        from: DebugValueType,
        to: DebugValueType,
    },
    #[error("debug value type mismatch: expected {expected:?}, got {actual:?}")]
    DebugValueTypeMismatch {
        expected: DebugValueType,
        actual: DebugValueType,
    },
    #[error("variant type mismatch: expected {expected:?}, got {actual:?}")]
    VariantTypeMismatch { expected: VarType, actual: VarType },
    #[error("string conversion failed: {0}")]
    Convert(#[from] ConvertError),
    #[error("integer parsing failed: {0}")]
    ParseInt(#[from] ParseIntError),
    #[error("integer casting failed: {0}")]
    TryFromInt(#[from] TryFromIntError),
    #[error("the operation timed out")]
    Timeout,
    #[error("interrupted")]
    Interrupted,
    #[error(
        "memory read starting at {address:#x} failed after reading \
         {bytes_read} bytes"
    )]
    ReadMemoryError {
        address: DebuggeeOffset,
        bytes_read: usize,
    },
    #[error(
        "memory write starting at {address:#x} failed after writing \
         {bytes_write} bytes"
    )]
    WriteMemoryError {
        address: DebuggeeOffset,
        bytes_write: usize,
    },
    #[error("invalid flags: {0:#x}")]
    InvalidFlags(u32),
    #[error("invalid argument")]
    InvalidArgument,
    #[error("specific ID required")]
    SpecificIdRequired,
    #[error("the requested interface is not supported")]
    InterfaceNotSupported,
    #[error("callbacks were not found")]
    CallbacksNotFound,
    #[error("a cycle was detected in the linked list")]
    ListEntryCycleDetected,
    #[error("waiting for a debugger event was interrupted")]
    EventWaitInterrupted,
    #[error("the debugger is not in a state where it can wait for an event")]
    EventWaitInvalidState,
    #[error("another debugger event wait is already in progress")]
    EventWaitAlreadyInProgress,
    #[error("access to the target or object was denied")]
    AccessDenied,
    #[error("failed to evaluate expression {expression:?} as {desired_type:?}")]
    Evaluate {
        expression: String,
        desired_type: DebugValueType,
    },
    #[error("register not found")]
    RegisterNotFound,
    #[error("the target or register is not currently accessible")]
    RegisterUnavailable,
    #[error("breakpoint not found")]
    BreakpointNotFound,
    #[error("process not found")]
    ProcessNotFound,
    #[error("thread not found")]
    ThreadNotFound,
    #[error("system not found")]
    SystemNotFound,
    #[error("symbol not found")]
    SymbolNotFound,
    #[error("symbol type not found")]
    TypeNotFound,
    #[error("symbol field not found")]
    FieldNotFound,
    #[error("constant not found")]
    ConstantNotFound,
    #[error("module not found")]
    ModuleNotFound,
    #[error("an unexpected debugger error occurred")]
    Other,
}

impl WdErrorKind {
    pub(crate) fn into_error(self) -> WdError { WdError::from_kind(self) }

    fn default_hresult(&self) -> HRESULT {
        match self {
            WdErrorKind::ValueCoercionFailed { .. } => E_INVALIDARG,
            WdErrorKind::DebugValueTypeMismatch { .. } => E_INVALIDARG,
            WdErrorKind::VariantTypeMismatch { .. } => E_INVALIDARG,
            WdErrorKind::Convert(_) => E_INVALIDARG,
            WdErrorKind::ParseInt(_) => E_INVALIDARG,
            WdErrorKind::TryFromInt(_) => E_INVALIDARG,
            WdErrorKind::Timeout => HRESULT::from_win32(ERROR_TIMEOUT.0),
            WdErrorKind::Interrupted => E_INTERRUPTED,
            WdErrorKind::AccessDenied => E_FAIL,
            WdErrorKind::Evaluate { .. } => E_FAIL,
            WdErrorKind::RegisterNotFound => E_NOINTERFACE,
            WdErrorKind::BreakpointNotFound => E_NOINTERFACE,
            WdErrorKind::ProcessNotFound => E_NOINTERFACE,
            WdErrorKind::ThreadNotFound => E_NOINTERFACE,
            WdErrorKind::SystemNotFound => E_NOINTERFACE,
            WdErrorKind::SymbolNotFound => E_NOINTERFACE,
            WdErrorKind::TypeNotFound => E_NOINTERFACE,
            WdErrorKind::FieldNotFound => E_NOINTERFACE,
            WdErrorKind::ConstantNotFound => E_NOINTERFACE,
            WdErrorKind::ModuleNotFound => E_NOINTERFACE,
            WdErrorKind::ReadMemoryError { .. } => E_READ_FAULT,
            WdErrorKind::WriteMemoryError { .. } => E_WRITE_FAULT,
            WdErrorKind::InvalidFlags(_) => E_INVALIDARG,
            WdErrorKind::InterfaceNotSupported => E_NOINTERFACE,
            WdErrorKind::CallbacksNotFound => E_NOINTERFACE,
            WdErrorKind::InvalidArgument => E_INVALIDARG,
            WdErrorKind::SpecificIdRequired => E_INVALIDARG,
            WdErrorKind::ListEntryCycleDetected => E_FAIL,
            WdErrorKind::Other => E_UNEXPECTED,
            WdErrorKind::EventWaitInterrupted => E_PENDING,
            WdErrorKind::EventWaitInvalidState => E_UNEXPECTED,
            WdErrorKind::EventWaitAlreadyInProgress => E_FAIL,
            WdErrorKind::RegisterUnavailable => E_UNEXPECTED,
        }
    }
}

#[derive(Error, Debug, Clone, PartialEq, Eq)]
#[error(
    "{kind}: {message} ({hresult:?})",
    message = hresult.message().trim()
)]
pub struct WdError {
    #[source]
    kind: WdErrorKind,
    hresult: HRESULT,
}

impl WdError {
    pub fn kind(&self) -> WdErrorKind { self.kind.clone() }

    pub fn hresult(&self) -> HRESULT { self.hresult }

    pub fn from_kind(kind: WdErrorKind) -> Self {
        let hresult = WdErrorKind::default_hresult(&kind);
        Self { kind, hresult }
    }

    pub fn from_kind_with_hr(kind: WdErrorKind, hresult: HRESULT) -> Self {
        if hresult == E_INTERRUPTED {
            return Self::from_kind(WdErrorKind::Interrupted);
        }
        Self { kind, hresult }
    }
}

impl From<WdErrorKind> for WdError {
    fn from(value: WdErrorKind) -> Self { Self::from_kind(value) }
}

impl From<HRESULT> for WdError {
    fn from(e: HRESULT) -> Self {
        Self::from_kind_with_hr(WdErrorKind::Other, e)
    }
}

impl From<(WdErrorKind, HRESULT)> for WdError {
    fn from(value: (WdErrorKind, HRESULT)) -> Self {
        Self::from_kind_with_hr(value.0, value.1)
    }
}

impl From<windows::core::Error> for WdError {
    fn from(e: windows::core::Error) -> Self {
        Self::from_kind_with_hr(WdErrorKind::Other, e.code())
    }
}

impl From<ConvertError> for WdError {
    fn from(e: ConvertError) -> Self {
        Self::from_kind(WdErrorKind::Convert(e))
    }
}

impl From<ParseIntError> for WdError {
    fn from(e: ParseIntError) -> Self {
        Self::from_kind(WdErrorKind::ParseInt(e))
    }
}

impl From<TryFromIntError> for WdError {
    fn from(e: TryFromIntError) -> Self {
        Self::from_kind(WdErrorKind::TryFromInt(e))
    }
}

impl From<WdError> for HRESULT {
    fn from(e: WdError) -> Self { e.hresult }
}

impl From<WdError> for windows::core::Error {
    fn from(e: WdError) -> Self { e.hresult.into() }
}

#[derive(Clone, Eq, PartialEq)]
pub struct DbgModelError {
    hr: HRESULT,
    object: Option<WdModelErrorObject>,
}

impl fmt::Display for DbgModelError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{:?}", self.hr)
    }
}

impl DbgModelError {
    pub fn new(
        hr: impl Into<HRESULT>,
        object: Option<impl Into<ModelObject>>,
    ) -> Self {
        match object {
            None => Self {
                hr: hr.into(),
                object: None,
            },
            Some(object) => Self {
                hr: hr.into(),
                object: Some(WdModelErrorObject(object.into().into())),
            },
        }
    }

    pub fn new2<'a>(
        manager: impl AsRef<DataModelManager>,
        hr: impl Into<HRESULT>,
        message: Option<impl Into<WideStrArg<'a>>>,
    ) -> WdResult<Self> {
        let hr = hr.into();

        Ok(match message {
            None => Self { hr, object: None },
            Some(message) => Self {
                hr,
                object: Some(WdModelErrorObject::create(manager, hr, message)?),
            },
        })
    }

    pub fn as_hresult(&self) -> HRESULT { self.hr }
}

impl From<DbgModelError> for HRESULT {
    fn from(e: DbgModelError) -> Self { e.as_hresult() }
}

impl From<DbgModelError> for windows::core::Error {
    fn from(e: DbgModelError) -> Self { e.as_hresult().into() }
}

impl From<DbgModelError> for WdError {
    fn from(e: DbgModelError) -> Self { e.as_hresult().into() }
}