wdext 0.1.0

A DbgEng wrapper framework
// SPDX-FileCopyrightText: 2026 takubokudori
// SPDX-License-Identifier: MIT OR Apache-2.0
use crate::{
    DbgModelResult, WdResult, data::Variant, dbgmodel::*, dmerr,
    impl_debug_interface, wd::dbgmodel::WdModelObject,
};
use std::rc::Rc;
use windows::Win32::System::Diagnostics::Debug::Extensions::{
    IKeyStore, IModelMethod, IModelMethod_Impl, IModelObject,
};
use windows_core::{IUnknown, OutRef, Ref, imp::E_POINTER, implement};

pub trait ModelMethodHandler {
    fn call(
        &self,
        context_object: Option<ModelObjectRef>,
        arguments: &[ModelObjectRef],
    ) -> DbgModelResult<(ModelObject, Option<KeyStore>)>;
}

impl_debug_interface!(ModelMethod, ModelMethodRef, IModelMethod);

impl ModelMethodHandler for ModelMethod {
    fn call(
        &self,
        context_object: Option<ModelObjectRef>,
        arguments: &[ModelObjectRef],
    ) -> DbgModelResult<(ModelObject, Option<KeyStore>)> {
        let args: Vec<Option<IModelObject>> =
            arguments.iter().map(|x| Some(x.as_base())).collect();
        let mut result = None;
        let mut metadata = None;
        unsafe {
            dmerr!(
                result,
                self.0.Call(
                    context_object.as_ref().map(|x| x.as_raw()),
                    &args,
                    &mut result,
                    Some(&mut metadata)
                )
            );
        }
        Ok((result.unwrap().into(), metadata.map(Into::into)))
    }
}

#[implement(IModelMethod)]
pub struct ModelMethodAdapter(Rc<dyn ModelMethodHandler>);

impl ModelMethodAdapter {
    pub fn new(callbacks: Rc<dyn ModelMethodHandler>) -> Self {
        Self(callbacks)
    }

    pub fn as_handler(&self) -> &dyn ModelMethodHandler { self.0.as_ref() }

    pub fn into_object(
        self,
        manager: impl AsRef<DataModelManager>,
    ) -> WdResult<WdModelObject> {
        let unknown: IUnknown = self.into();
        let variant = Variant::from(unknown);
        Ok(manager
            .as_ref()
            .create_intrinsic_object(WdModelObjectKind::Method, &variant)?
            .into())
    }
}

#[allow(non_snake_case)]
impl IModelMethod_Impl for ModelMethodAdapter_Impl {
    #[allow(clippy::not_unsafe_ptr_arg_deref)]
    fn Call(
        &self,
        pContextObject: Ref<IModelObject>,
        argCount: u64,
        ppArguments: *const Option<IModelObject>,
        ppResult: OutRef<IModelObject>,
        ppMetadata: OutRef<IKeyStore>,
    ) -> windows_core::Result<()> {
        let argCount = argCount.try_into()?;

        let args = if argCount == 0 {
            &[]
        } else {
            if ppArguments.is_null() {
                return Err(E_POINTER.into());
            }
            unsafe { std::slice::from_raw_parts(ppArguments, argCount) }
        };

        let args: Vec<ModelObjectRef<'_>> = args
            .iter()
            .map(|argument| {
                argument
                    .as_ref()
                    .ok_or_else(|| windows_core::Error::from(E_POINTER))
                    .map(Into::into)
            })
            .collect::<windows_core::Result<_>>()?;
        let context_object = pContextObject.as_ref().map(Into::into);
        let (result, metadata) = self
            .0
            .call(context_object, &args)
            .map_err(|e| e.as_hresult())?;
        ppResult.write(Some(result.as_base()))?;
        // Ignore the error when `ppMetadata` is `NULL`.
        match ppMetadata.write(metadata.map(|x| x.as_base())) {
            Ok(_) => Ok(()),
            Err(e) if e.code() == E_POINTER => Ok(()),
            Err(e) => Err(e),
        }
    }
}