msft_typelib/types.rs
1//! Shared enumerations and helper functions for MSFT type libraries.
2//!
3//! [`TypeKind`] mirrors the `TYPEKIND` enum from OLE Automation.
4//! [`ConstValue`] decodes the variant-tagged constant values stored in
5//! the custom-data table (segment 11) or packed inline in `OffsValue`
6//! fields. [`vt_name`] maps `VARTYPE` codes to human-readable names.
7//!
8//! `VARTYPE` codes (the `VT_*` constants) identify the data type of a
9//! variant value. Not all VT codes appear as `ConstValue` variants --
10//! codes without an explicit variant are captured as [`ConstValue::Raw`].
11
12use std::fmt;
13
14/// The kind of a TypeInfo entry (low 4 bits of the `typekind` field).
15///
16/// Mirrors the `TYPEKIND` enum from the OLE Automation specification.
17#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18pub enum TypeKind {
19 /// `TKIND_ENUM` (0) -- An enumeration of named constants.
20 Enum,
21 /// `TKIND_RECORD` (1) -- A C-style struct / UDT.
22 Record,
23 /// `TKIND_MODULE` (2) -- A module containing global functions and constants.
24 Module,
25 /// `TKIND_INTERFACE` (3) -- A COM vtable-based interface.
26 Interface,
27 /// `TKIND_DISPATCH` (4) -- A COM `IDispatch`-based interface.
28 Dispatch,
29 /// `TKIND_COCLASS` (5) -- A COM coclass aggregating one or more interfaces.
30 CoClass,
31 /// `TKIND_ALIAS` (6) -- A typedef alias for another type.
32 Alias,
33 /// `TKIND_UNION` (7) -- A C-style union.
34 Union,
35 /// An unrecognised type kind value.
36 Unknown(u8),
37}
38
39impl TypeKind {
40 /// Converts a raw `u8` value (low 4 bits of `typekind`) to a [`TypeKind`].
41 pub fn from_raw(raw: u8) -> Self {
42 match raw {
43 0 => TypeKind::Enum,
44 1 => TypeKind::Record,
45 2 => TypeKind::Module,
46 3 => TypeKind::Interface,
47 4 => TypeKind::Dispatch,
48 5 => TypeKind::CoClass,
49 6 => TypeKind::Alias,
50 7 => TypeKind::Union,
51 n => TypeKind::Unknown(n),
52 }
53 }
54}
55
56impl fmt::Display for TypeKind {
57 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
58 match self {
59 TypeKind::Enum => write!(f, "ENUM"),
60 TypeKind::Record => write!(f, "RECORD"),
61 TypeKind::Module => write!(f, "MODULE"),
62 TypeKind::Interface => write!(f, "INTERFACE"),
63 TypeKind::Dispatch => write!(f, "DISPATCH"),
64 TypeKind::CoClass => write!(f, "COCLASS"),
65 TypeKind::Alias => write!(f, "ALIAS"),
66 TypeKind::Union => write!(f, "UNION"),
67 TypeKind::Unknown(n) => write!(f, "UNKNOWN({n})"),
68 }
69 }
70}
71
72/// A decoded constant value from a `VAR_CONST` variable record or custom data.
73///
74/// Values are either packed inline in the `OffsValue` field (small
75/// positive integers) or stored in the custom data segment.
76#[derive(Debug, Clone)]
77#[non_exhaustive]
78pub enum ConstValue<'a> {
79 /// No value / empty (`VT_EMPTY`, 0).
80 Empty,
81 /// Null (`VT_NULL`, 1).
82 Null,
83 /// A 16-bit signed integer (`VT_I2`, 2).
84 I2(i16),
85 /// A 32-bit signed integer (`VT_I4`, 3).
86 I4(i32),
87 /// A 32-bit float (`VT_R4`, 4).
88 R4(f32),
89 /// A 64-bit float (`VT_R8`, 5).
90 R8(f64),
91 /// A currency value (`VT_CY`, 6) stored as a 64-bit integer scaled by 10000.
92 Cy(i64),
93 /// A date (`VT_DATE`, 7) stored as a 64-bit float (OLE Automation date).
94 Date(f64),
95 /// A byte-string (`VT_BSTR`, 8), borrowed from the file data.
96 Bstr(&'a [u8]),
97 /// An OLE error code (`VT_ERROR`, 10).
98 Error(i32),
99 /// A boolean (`VT_BOOL`, 11).
100 Bool(bool),
101 /// A signed byte (`VT_I1`, 16).
102 I1(i8),
103 /// An unsigned byte (`VT_UI1`, 17).
104 UI1(u8),
105 /// An unsigned 16-bit integer (`VT_UI2`, 18).
106 UI2(u16),
107 /// An unsigned 32-bit integer (`VT_UI4`, 19).
108 UI4(u32),
109 /// A 64-bit signed integer (`VT_I8`, 20).
110 I8(i64),
111 /// A 64-bit unsigned integer (`VT_UI8`, 21).
112 UI8(u64),
113 /// A signed integer (`VT_INT`, 22).
114 Int(i32),
115 /// An unsigned integer (`VT_UINT`, 23).
116 UInt(u32),
117 /// Void (`VT_VOID`, 24).
118 Void,
119 /// An HRESULT (`VT_HRESULT`, 25).
120 Hresult(i32),
121 /// A decimal value (`VT_DECIMAL`, 14), raw 16 bytes.
122 Decimal(&'a [u8]),
123 /// A FILETIME value (`VT_FILETIME`, 64).
124 Filetime(u64),
125 /// A value whose `VARTYPE` code has no dedicated variant above.
126 ///
127 /// The `vt` field is the 16-bit `VARTYPE` code (e.g. `VT_DISPATCH` = 9,
128 /// `VT_VARIANT` = 12). The `bits` field is the first 4 bytes of the
129 /// value payload; for wider types the remaining bytes are not captured.
130 Raw {
131 /// The `VARTYPE` code.
132 vt: u16,
133 /// The first 4 bytes of the value payload, read as a little-endian `i32`.
134 bits: i32,
135 },
136}
137
138impl fmt::Display for ConstValue<'_> {
139 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
140 match self {
141 ConstValue::Empty => write!(f, "<empty>"),
142 ConstValue::Null => write!(f, "<null>"),
143 ConstValue::I2(v) => write!(f, "{v}"),
144 ConstValue::I4(v) => write!(f, "{v}"),
145 ConstValue::R4(v) => write!(f, "{v}"),
146 ConstValue::R8(v) => write!(f, "{v}"),
147 ConstValue::Cy(v) => {
148 let whole = v / 10000;
149 let frac = (v % 10000).unsigned_abs();
150 write!(f, "{whole}.{frac:04}")
151 }
152 ConstValue::Date(v) => write!(f, "DATE({v})"),
153 ConstValue::Bstr(v) => write!(f, "\"{}\"", String::from_utf8_lossy(v)),
154 ConstValue::Error(v) => write!(f, "ERROR(0x{v:08X})"),
155 ConstValue::Bool(true) => write!(f, "True"),
156 ConstValue::Bool(false) => write!(f, "False"),
157 ConstValue::I1(v) => write!(f, "{v}"),
158 ConstValue::UI1(v) => write!(f, "{v}"),
159 ConstValue::UI2(v) => write!(f, "{v}"),
160 ConstValue::UI4(v) => write!(f, "{v}"),
161 ConstValue::I8(v) => write!(f, "{v}"),
162 ConstValue::UI8(v) => write!(f, "{v}"),
163 ConstValue::Int(v) => write!(f, "{v}"),
164 ConstValue::UInt(v) => write!(f, "{v}"),
165 ConstValue::Void => write!(f, "<void>"),
166 ConstValue::Hresult(v) => write!(f, "0x{v:08X}"),
167 ConstValue::Decimal(v) => {
168 write!(f, "DECIMAL(")?;
169 for (i, b) in v.iter().enumerate() {
170 if i > 0 {
171 write!(f, " ")?;
172 }
173 write!(f, "{b:02X}")?;
174 }
175 write!(f, ")")
176 }
177 ConstValue::Filetime(v) => write!(f, "FILETIME(0x{v:016X})"),
178 ConstValue::Raw { vt, bits } => write!(f, "raw(vt={vt}, 0x{bits:08X})"),
179 }
180 }
181}
182
183/// Returns a human-readable VB-style name for a `VARTYPE` code.
184///
185/// Common mappings: 2 = `"Integer"`, 3 = `"Long"`, 8 = `"String"`, etc.
186/// Returns `"Unknown"` for unrecognised codes.
187pub fn vt_name(vt: u16) -> &'static str {
188 match vt {
189 0 => "void",
190 2 => "Integer",
191 3 => "Long",
192 4 => "Single",
193 5 => "Double",
194 6 => "Currency",
195 7 => "Date",
196 8 => "String",
197 9 => "Object",
198 10 => "SCODE",
199 11 => "Boolean",
200 12 => "Variant",
201 13 => "IUnknown",
202 16 => "SignedByte",
203 17 => "Byte",
204 18 => "UShort",
205 19 => "ULong",
206 20 => "LongLong",
207 21 => "ULongLong",
208 22 => "Int",
209 23 => "UInt",
210 24 => "void",
211 25 => "HRESULT",
212 26 => "Ptr",
213 27 => "SafeArray",
214 28 => "CArray",
215 29 => "UserDefined",
216 _ => "Unknown",
217 }
218}