use crate::{AsIUnknown, Rc};
use crate::errors::MethodHResult;
use winapi::Interface;
use winapi::um::combaseapi::{AGILEREFERENCE_DEFAULT, AGILEREFERENCE_DELAYEDMARSHAL, AgileReferenceOptions, RoGetAgileReference};
use winapi::um::objidlbase::IAgileReference;
use core::convert::TryFrom;
use core::fmt::{self, Debug, Formatter};
use core::marker::PhantomData;
use core::ptr::null_mut;
pub struct Agile<I: Interface + AsIUnknown> {
agile: Rc<IAgileReference>,
phantom: PhantomData<*const I>,
}
impl<I: Interface + AsIUnknown> Agile<I> {
pub fn try_from_eager(unk: impl AsRef<Rc<I>>) -> Result<Self, MethodHResult> {
Self::ro_get_agile_reference(ReferenceOptions::DEFAULT, unk)
}
pub fn try_from_lazy(unk: impl AsRef<Rc<I>>) -> Result<Self, MethodHResult> {
Self::ro_get_agile_reference(ReferenceOptions::DELAYED_MARSHAL, unk)
}
fn ro_get_agile_reference(ro: impl Into<ReferenceOptions>, unk: impl AsRef<Rc<I>>) -> Result<Self, MethodHResult> {
let ro = ro.into().0;
let unk = unk.as_ref();
let unk = unk.as_iunknown_ptr();
let mut agile = null_mut();
let hr = unsafe { RoGetAgileReference(ro, &I::uuidof(), unk, &mut agile) };
MethodHResult::check("RoGetAgileReference", hr)?;
let agile = unsafe { Rc::from_raw_opt(agile) }.ok_or(MethodHResult::unchecked("RoGetAgileReference", hr))?;
Ok(Self { agile, phantom: PhantomData })
}
pub fn resolve(&self) -> Result<Rc<I>, MethodHResult> {
let mut pv = null_mut();
let hr = unsafe { self.agile.Resolve(&I::uuidof(), &mut pv) };
MethodHResult::check("IAgileReference::Resolve", hr)?;
let rc = unsafe { Rc::from_raw(pv.cast()) };
Ok(rc)
}
}
unsafe impl<I: Interface + AsIUnknown> Send for Agile<I> {}
unsafe impl<I: Interface + AsIUnknown> Sync for Agile<I> {}
impl<I: Interface + AsIUnknown> Clone for Agile<I> {
fn clone(&self) -> Self { Self { agile: self.agile.clone(), phantom: PhantomData } }
}
impl<I: Interface + AsIUnknown> TryFrom<Rc<I>> for Agile<I> {
type Error = MethodHResult;
fn try_from(value: Rc<I>) -> Result<Self, Self::Error> { Self::try_from_eager(value) }
}
impl<I: Interface + AsIUnknown> TryFrom<&Rc<I>> for Agile<I> {
type Error = MethodHResult;
fn try_from(value: &Rc<I>) -> Result<Self, Self::Error> { Self::try_from_eager(value) }
}
impl<I: Interface + AsIUnknown> TryFrom<Agile<I>> for Rc<I> {
type Error = MethodHResult;
fn try_from(value: Agile<I>) -> Result<Self, Self::Error> { value.resolve() }
}
impl<I: Interface + AsIUnknown> TryFrom<&Agile<I>> for Rc<I> {
type Error = MethodHResult;
fn try_from(value: &Agile<I>) -> Result<Self, Self::Error> { value.resolve() }
}
impl<I: Interface + AsIUnknown> AsRef<Agile<I>> for Agile<I> {
fn as_ref(&self) -> &Self { self }
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ReferenceOptions(u32);
impl ReferenceOptions {
pub const DEFAULT : ReferenceOptions = ReferenceOptions(AGILEREFERENCE_DEFAULT);
pub const DELAYED_MARSHAL : ReferenceOptions = ReferenceOptions(AGILEREFERENCE_DELAYEDMARSHAL);
}
impl Debug for ReferenceOptions {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match *self {
ReferenceOptions::DEFAULT => write!(f, "ReferenceOptions::DEFAULT"),
ReferenceOptions::DELAYED_MARSHAL => write!(f, "ReferenceOptions::DELAYED_MARSHAL"),
other => write!(f, "ReferenceOptions(0x{:08x})", other.0),
}
}
}
impl Default for ReferenceOptions {
fn default() -> Self { ReferenceOptions::DEFAULT }
}
impl From<ReferenceOptions> for AgileReferenceOptions {
fn from(ro: ReferenceOptions) -> Self { ro.0 }
}
impl From<()> for ReferenceOptions {
fn from(_: ()) -> Self { ReferenceOptions::DEFAULT }
}