use std::convert::TryFrom;
use serde::Serialize;
use widestring::U16CString;
use winapi::{shared::wtypes::VT_ARRAY, um::oaidl::VARIANT};
use crate::SafeArray;
use super::errors::VariantResultError;
use super::{BOOL_FALSE, BOOL_TRUE};
use winapi::shared::wtypes::{
VT_BOOL, VT_BSTR, VT_EMPTY, VT_I2, VT_I4, VT_I8, VT_INT, VT_R4, VT_R8, VT_SAFEARRAY, VT_UI2,
VT_UI4, VT_UI8, VT_UINT,
};
#[derive(Debug)]
pub enum VariantResult {
Empty,
BOOL(bool),
I16(i16),
U16(u16),
Int(i32),
Uint(u32),
I64(i64),
U64(u64),
F32(f32),
F64(f64),
String(String),
SafeArray(SafeArray),
}
impl TryFrom<VARIANT> for VariantResult {
type Error = VariantResultError;
fn try_from(value: VARIANT) -> Result<Self, Self::Error> {
unsafe {
let n2 = value.n1.n2();
let n3 = n2.n3;
match n2.vt as u32 {
VT_EMPTY => Ok(VariantResult::Empty),
VT_BOOL => match *n3.boolVal() {
BOOL_TRUE => Ok(VariantResult::BOOL(true)),
BOOL_FALSE => Ok(VariantResult::BOOL(false)),
v => Err(VariantResultError::InvalidBool(v)),
},
VT_BSTR => {
let p = match n3.bstrVal().as_ref() {
Some(p) => p,
None => return Err(VariantResultError::NullPointer),
};
let u16_str = U16CString::from_ptr_str(p);
Ok(VariantResult::String(u16_str.to_string()?))
}
VT_I2 => {
let v = n3.iVal();
Ok(VariantResult::I16(*v))
}
VT_UI2 => {
let v = n3.uiVal();
Ok(VariantResult::U16(*v))
}
VT_I4 => {
let v = n3.lVal();
Ok(VariantResult::Int(*v))
}
VT_UI4 => {
let v = n3.ulVal();
Ok(VariantResult::Uint(*v))
}
VT_INT => {
let v = n3.intVal();
Ok(VariantResult::Int(*v))
}
VT_UINT => {
let v = n3.uintVal();
Ok(VariantResult::Uint(*v))
}
VT_I8 => {
let v = n3.llVal();
Ok(VariantResult::I64(*v))
}
VT_UI8 => {
let v = n3.ullVal();
Ok(VariantResult::U64(*v))
}
VT_R4 => {
let v = n3.fltVal();
Ok(VariantResult::F32(*v))
}
VT_R8 => {
let v = n3.dblVal();
Ok(VariantResult::F64(*v))
}
VT_SAFEARRAY => {
let p = n3.parray();
Ok(VariantResult::SafeArray(SafeArray::from(*p)))
}
vt => {
if vt & VT_ARRAY != 0 {
let p = n3.parray();
Ok(VariantResult::SafeArray(SafeArray::from(*p)))
} else {
Err(VariantResultError::UnsupportedTypeConversion(n2.vt))
}
}
}
}
}
}
impl Serialize for VariantResult {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
match self {
VariantResult::BOOL(v) => serializer.serialize_bool(*v),
VariantResult::Empty => serializer.serialize_none(),
VariantResult::F32(v) => serializer.serialize_f32(*v),
VariantResult::F64(v) => serializer.serialize_f64(*v),
VariantResult::I16(v) => serializer.serialize_i16(*v),
VariantResult::I64(v) => serializer.serialize_i64(*v),
VariantResult::Int(v) => serializer.serialize_i32(*v),
VariantResult::String(v) => serializer.serialize_str(v),
VariantResult::U16(v) => serializer.serialize_u16(*v),
VariantResult::U64(v) => serializer.serialize_u64(*v),
VariantResult::Uint(v) => serializer.serialize_u32(*v),
VariantResult::SafeArray(sa) => sa.serialize(serializer),
}
}
}
#[test]
fn it_serializes() {
let _ = env_logger::builder().is_test(true).try_init();
let src2d = ndarray::arr2(&[
["hello", "world"],
["of", "multi"],
["dimensional", "arrays"],
])
.into_dyn();
let sa2d = SafeArray::try_from(&src2d).unwrap();
let src = vec!["hello", "world"];
let sa = SafeArray::try_from(&src).unwrap();
let scenarios = vec![
(VariantResult::BOOL(true), "true"),
(VariantResult::BOOL(false), "false"),
(VariantResult::I16(10), "10"),
(VariantResult::I64(-128), "-128"),
(VariantResult::Int(-12), "-12"),
(VariantResult::F32(10.01), "10.01"),
(VariantResult::F64(-128.95), "-128.95"),
(VariantResult::String("hello".to_owned()), "\"hello\""),
(VariantResult::U16(2), "2"),
(VariantResult::Uint(16), "16"),
(VariantResult::U64(128), "128"),
(VariantResult::Empty, "null"),
(VariantResult::SafeArray(sa), r#"["hello","world"]"#),
(
VariantResult::SafeArray(sa2d),
r#"{"v":1,"dim":[3,2],"data":["hello","world","of","multi","dimensional","arrays"]}"#,
),
];
for (test, expected) in scenarios {
assert_eq!(serde_json::to_string(&test).unwrap(), expected);
}
}