rs_teststand_sys/value.rs
1//! A safe, owned representation of a COM `VARIANT`.
2//!
3//! Wrappers work in terms of [`Value`] and never touch a raw `VARIANT`. The
4//! dispatch layer converts `VARIANT` → `Value` on the way out (copying strings
5//! and add-refing interfaces) so that ownership is unambiguous on the Rust side.
6
7use crate::dispatch::Dispatch;
8use crate::error::ComError;
9
10/// An owned value crossing the COM boundary, mapped from a `VARIANT`.
11#[derive(Debug)]
12pub enum Value {
13 /// `VT_EMPTY`, an uninitialized VARIANT.
14 Empty,
15 /// `VT_NULL`, SQL-style null.
16 Null,
17 /// `VT_BOOL`.
18 Bool(bool),
19 /// `VT_I4`, a 32-bit signed integer.
20 I32(i32),
21 /// `VT_I8`, a 64-bit signed integer (handles, large counts).
22 I64(i64),
23 /// A null *object reference* (`VT_DISPATCH` holding no pointer).
24 ///
25 /// Distinct from [`Self::Null`], which is `VT_NULL`. A member that takes an
26 /// object and documents "pass a null reference" wants this: `VT_NULL` is a
27 /// different type and is refused with `DISP_E_TYPEMISMATCH`.
28 NullObject,
29 /// An unsigned 64-bit integer (`VT_UI8`).
30 ///
31 /// Distinct from [`Self::I64`] because the engine matches numeric
32 /// representation strictly: a property stored as unsigned rejects a signed
33 /// variant rather than coercing it.
34 U64(u64),
35 /// `VT_R8`, a 64-bit float.
36 F64(f64),
37 /// `VT_BSTR`, an owned UTF-8 copy of the BSTR.
38 Str(String),
39 /// `VT_DISPATCH`, a nested COM object, ready to wrap.
40 Object(Box<dyn Dispatch>),
41}
42
43impl Value {
44 /// The variant name, used in type-mismatch diagnostics.
45 #[must_use]
46 pub const fn kind(&self) -> &'static str {
47 match self {
48 Self::Empty => "Empty",
49 Self::Null => "Null",
50 Self::Bool(_) => "Bool",
51 Self::I32(_) => "I32",
52 Self::I64(_) => "I64",
53 Self::F64(_) => "F64",
54 Self::Str(_) => "Str",
55 Self::NullObject => "NullObject",
56 Self::U64(_) => "U64",
57 Self::Object(_) => "Object",
58 }
59 }
60
61 /// Reads the value as an `i32` (`VT_I4`).
62 ///
63 /// # Errors
64 /// [`ComError::UnexpectedType`] if the value is not an [`Value::I32`].
65 pub const fn as_i32(&self) -> Result<i32, ComError> {
66 match self {
67 Self::I32(value) => Ok(*value),
68 other => Err(ComError::UnexpectedType {
69 expected: "I32",
70 actual: other.kind(),
71 }),
72 }
73 }
74
75 /// Reads the value as an `i64` (`VT_I8`).
76 ///
77 /// # Errors
78 /// [`ComError::UnexpectedType`] if the value is not an [`Value::I64`].
79 pub const fn as_i64(&self) -> Result<i64, ComError> {
80 match self {
81 Self::I64(value) => Ok(*value),
82 other => Err(ComError::UnexpectedType {
83 expected: "I64",
84 actual: other.kind(),
85 }),
86 }
87 }
88
89 /// Reads the value as a `f64` (`VT_R8`).
90 ///
91 /// # Errors
92 /// [`ComError::UnexpectedType`] if the value is not an [`Value::F64`].
93 pub const fn as_f64(&self) -> Result<f64, ComError> {
94 match self {
95 Self::F64(value) => Ok(*value),
96 other => Err(ComError::UnexpectedType {
97 expected: "F64",
98 actual: other.kind(),
99 }),
100 }
101 }
102
103 /// Reads the value as a `bool` (`VT_BOOL`).
104 ///
105 /// # Errors
106 /// [`ComError::UnexpectedType`] if the value is not a [`Value::Bool`].
107 pub const fn as_bool(&self) -> Result<bool, ComError> {
108 match self {
109 Self::Bool(value) => Ok(*value),
110 other => Err(ComError::UnexpectedType {
111 expected: "Bool",
112 actual: other.kind(),
113 }),
114 }
115 }
116
117 /// Consumes the value as an owned `String` (`VT_BSTR`).
118 ///
119 /// # Errors
120 /// [`ComError::UnexpectedType`] if the value is not a [`Value::Str`].
121 pub fn into_string(self) -> Result<String, ComError> {
122 match self {
123 Self::Str(value) => Ok(value),
124 other => Err(ComError::UnexpectedType {
125 expected: "Str",
126 actual: other.kind(),
127 }),
128 }
129 }
130
131 /// Consumes the value as a nested COM object (`VT_DISPATCH`).
132 ///
133 /// # Errors
134 /// [`ComError::UnexpectedType`] if the value is not a [`Value::Object`].
135 pub fn into_object(self) -> Result<Box<dyn Dispatch>, ComError> {
136 match self {
137 Self::Object(dispatch) => Ok(dispatch),
138 other => Err(ComError::UnexpectedType {
139 expected: "Object",
140 actual: other.kind(),
141 }),
142 }
143 }
144}