Skip to main content

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}