feat(windows-drivers): vendor wdk 0.5.1 + add ApiSubset::Iddcx (M1 spike)
windows-drivers / probe-and-proto (push) Successful in 24s
apple / swift (push) Successful in 1m8s
windows-drivers / driver-build (push) Failing after 43s
ci / rust (push) Successful in 1m31s
ci / web (push) Successful in 1m5s
ci / docs-site (push) Successful in 52s
apple / screenshots (push) Failing after 2m35s
windows-host / package (push) Successful in 5m23s
ci / bench (push) Successful in 4m48s
android / android (push) Successful in 10m1s
decky / build-publish (push) Successful in 26s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 4s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 4s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 4s
deb / build-publish (push) Successful in 3m29s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 4s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 2m21s
rpm / build-publish (bazzite, punktfunk-fedora-rpm) (push) Successful in 8m23s
rpm / build-publish (fedora-44, punktfunk-fedora44-rpm) (push) Successful in 8m18s
docker / deploy-docs (push) Successful in 21s

Vendor the published, self-contained windows-drivers-rs 0.5.1 crates
(wdk-build, wdk-sys) under vendor/ and add a first-class ApiSubset::Iddcx that
bindgens iddcx/1.10/IddCx.h in an extra pass reusing bindgen::Builder::wdk_default
(allowlist_file (?i).*iddcx.* — emits only IddCx items; WDF/DXGI types resolve to
the shared base/wdf bindings, type-identity by construction). Mirrors the existing
gpio/hid/spb subsets exactly: wdk-build gets the enum variant + iddcx_headers()
(UMDF-only), wdk-sys gets generate_iddcx + the iddcx feature + pub mod iddcx.
[patch.crates-io] redirects all wdk-sys/wdk-build (incl. wdk 0.4.1 transitive) to
the patched copies. wdk-probe enables the iddcx feature.

MAKE-OR-BREAK: does IddCx.h bindgen in wdk-sys config without a header conflict
(issue #515) + does the generated module compile (type-identity)? CI answers it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-24 14:12:43 +00:00
parent 6975691f7d
commit 9fd19b90a9
38 changed files with 13208 additions and 5 deletions
@@ -0,0 +1,57 @@
// Copyright (c) Microsoft Corporation
// License: MIT OR Apache-2.0
use serde::ser::{self};
use thiserror::Error;
/// A specialized [`Result`] type for [`metadata`](crate::metadata)
/// serialization and deserialization operations.
pub type Result<T> = std::result::Result<T, Error>;
/// This type represents all possible errors that can occur when serializing
/// or deserializing [`metadata::Wdk`](crate::metadata::Wdk).
#[derive(Debug, Error)]
pub enum Error {
/// catch-all error emitted during serialization, when a more specific
/// error type is not available. This type of error is commonly
/// generated from [`serde`]'s `derive` feature's generated `Serialize`
/// impls.
#[error("custom serialization error: {message}")]
CustomSerialization {
/// Message describing the error
message: String,
},
/// error emitted when an empty key name is encountered during
/// serialization. Serialization of values always requires a non-empty
/// key name
#[error("empty key name encountered during serialization of value: {value_being_serialized}")]
EmptySerializationKeyName {
/// Value being serialized
value_being_serialized: String,
},
/// error emitted when duplicate key names are found during
/// serialization. Serializing into a
/// [`metadata::Map`](crate::metadata::Map) requires unique key names
#[error(
"duplicate keys found during serialization:\nkey: {key}\nvalue 1: {value_1}\nvalue 2: \
{value_2}"
)]
DuplicateSerializationKeys {
/// Key name
key: String,
/// One of the conflicting values
value_1: String,
/// One of the conflicting values
value_2: String,
},
}
impl ser::Error for Error {
fn custom<T: std::fmt::Display>(msg: T) -> Self {
Self::CustomSerialization {
message: msg.to_string(),
}
}
}
@@ -0,0 +1,63 @@
// Copyright (c) Microsoft Corporation
// License: MIT OR Apache-2.0
use std::{
collections::{BTreeMap, HashMap, btree_map, hash_map},
hash::{BuildHasher, Hash},
};
/// Trait for map-like type that is returned by
/// [`metadata::to_map`](crate::metadata::to_map)
/// and [`metadata::to_map_with_prefix`](crate::metadata::to_map_with_prefix).
pub trait Map<K, V>: Default {
/// Creates a new, empty map
#[must_use]
fn new() -> Self {
Self::default()
}
/// Inserts a new key-value pair into the map, or calls a function/closure
/// if the key already exists.
///
/// The function/closure is called with the existing key, the existing
/// value, and the new value it tried to insert. The closure can decide
/// whether the function will return an `Err` or if it will still return a
/// `Ok` despite not inserting the value.
///
/// # Errors
/// This function returns an error if the key already exists and `f` returns
/// an `Err` value
fn insert_or_else<F, E>(&mut self, key: K, value: V, f: F) -> Result<(), E>
where
F: FnMut(&K, &V, V) -> Result<(), E>;
}
impl<K: Eq + Hash, V, S: BuildHasher + Default> Map<K, V> for HashMap<K, V, S> {
fn insert_or_else<F, E>(&mut self, key: K, value: V, mut f: F) -> Result<(), E>
where
F: FnMut(&K, &V, V) -> Result<(), E>,
{
match self.entry(key) {
hash_map::Entry::Occupied(entry) => f(entry.key(), entry.get(), value),
hash_map::Entry::Vacant(entry) => {
entry.insert(value);
Ok(())
}
}
}
}
impl<K: Ord, V> Map<K, V> for BTreeMap<K, V> {
fn insert_or_else<F, E>(&mut self, key: K, value: V, mut f: F) -> Result<(), E>
where
F: FnMut(&K, &V, V) -> Result<(), E>,
{
match self.entry(key) {
btree_map::Entry::Occupied(entry) => f(entry.key(), entry.get(), value),
btree_map::Entry::Vacant(entry) => {
entry.insert(value);
Ok(())
}
}
}
}
@@ -0,0 +1,175 @@
// Copyright (c) Microsoft Corporation
// License: MIT OR Apache-2.0
//! Parsing and serializing metadata about WDK projects
//!
//! This module provides a [`Wdk`] struct that represents the cargo metadata
//! specified in the `metadata.wdk` section any `Cargo.toml`. This corresponds
//! with the settings in the `Driver Settings` property pages for WDK projects
//! in Visual Studio. This module also also provides [`serde`]-compatible
//! serialization and deserialization for the metadata.
pub use error::{Error, Result};
pub use map::Map;
pub use ser::{Serializer, to_map, to_map_with_prefix};
pub(crate) mod ser;
mod error;
mod map;
use std::collections::HashSet;
use camino::Utf8PathBuf;
use cargo_metadata::Metadata;
use serde::{Deserialize, Serialize};
use thiserror::Error;
use crate::DriverConfig;
/// Metadata specified in the `metadata.wdk` section of the `Cargo.toml`
/// of a crate that depends on the WDK, or in a cargo workspace.
///
/// This corresponds with the settings in the `Driver Settings` property pages
/// for WDK projects in Visual Studio
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
#[serde(
deny_unknown_fields,
rename_all(serialize = "SCREAMING_SNAKE_CASE", deserialize = "kebab-case")
)]
pub struct Wdk {
/// Metadata corresponding to the `Driver Model` property page in the WDK
pub driver_model: DriverConfig,
}
/// Errors that could result from trying to construct a
/// [`metadata::Wdk`](crate::metadata::Wdk) from information parsed by `cargo
/// metadata`
#[derive(Debug, Error)]
pub enum TryFromCargoMetadataError {
/// Error returned when no WDK configuration metadata is detected in the
/// dependency graph
#[error(
"no WDK configuration metadata is detected in the dependency graph. This could happen \
when building WDR itself, or building library crates that depend on the WDK but defer \
WDK configuration to their consumers"
)]
NoWdkConfigurationsDetected,
/// Error returned when multiple configurations of the WDK are detected
/// across the dependency graph
#[error(
"multiple configurations of the WDK are detected across the dependency graph, but only \
one configuration is allowed: {wdk_metadata_configurations:#?}"
)]
MultipleWdkConfigurationsDetected {
/// [`HashSet`] of unique [`metadata::Wdk`](crate::metadata::Wdk)
/// derived from detected WDK metadata
wdk_metadata_configurations: HashSet<Wdk>,
},
/// Error returned when [`crate::metadata::Wdk`] fails to be deserialized
/// from [`cargo_metadata::Metadata`] output
#[error("failed to deserialize metadata::Wdk from {metadata_source}")]
WdkMetadataDeserialization {
/// `String` that describes what part of
/// `cargo_metadata::Metadata` was used as the source for
/// deserialization
metadata_source: String,
/// [`serde_json::Error`] that caused the deserialization to fail
#[source]
error_source: serde_json::Error,
},
}
impl TryFrom<&Metadata> for Wdk {
type Error = TryFromCargoMetadataError;
fn try_from(metadata: &Metadata) -> std::result::Result<Self, Self::Error> {
let wdk_metadata_configurations = {
// Parse WDK metadata from workspace and all packages
let mut configs = parse_packages_wdk_metadata(&metadata.packages)?;
if let Some(workspace_metadata) =
parse_workspace_wdk_metadata(&metadata.workspace_metadata)?
{
configs.insert(workspace_metadata);
}
configs
};
// Ensure that only one configuration of WDK is allowed per dependency graph
match wdk_metadata_configurations.len() {
1 => Ok(wdk_metadata_configurations.into_iter().next().expect(
"wdk_metadata_configurations should have exactly one element because of the \
.len() check above",
)),
0 => Err(TryFromCargoMetadataError::NoWdkConfigurationsDetected),
_ => Err(
TryFromCargoMetadataError::MultipleWdkConfigurationsDetected {
wdk_metadata_configurations,
},
),
}
}
}
fn parse_packages_wdk_metadata(
packages: &[cargo_metadata::Package],
) -> std::result::Result<HashSet<Wdk>, TryFromCargoMetadataError> {
let wdk_metadata_configurations = packages
.iter()
.filter_map(|package| match &package.metadata["wdk"] {
serde_json::Value::Null => None,
// When wdk section is empty, treat it as if it wasn't there. This is to allow for using
// empty wdk metadata sections to mark the package as a driver (ex. for detection in
// `package_driver_flow_condition_script`)
serde_json::Value::Object(map) if map.is_empty() => None,
wdk_metadata => Some(Wdk::deserialize(wdk_metadata).map_err(|err| {
TryFromCargoMetadataError::WdkMetadataDeserialization {
metadata_source: format!(
"{} for {} package",
stringify!(package.metadata["wdk"]),
package.name
),
error_source: err,
}
})),
})
.collect::<std::result::Result<HashSet<_>, _>>()?;
Ok(wdk_metadata_configurations)
}
fn parse_workspace_wdk_metadata(
workspace_metadata: &serde_json::Value,
) -> std::result::Result<Option<Wdk>, TryFromCargoMetadataError> {
Ok(match &workspace_metadata["wdk"] {
serde_json::Value::Null => None,
wdk_metadata => Some(Wdk::deserialize(wdk_metadata).map_err(|err| {
TryFromCargoMetadataError::WdkMetadataDeserialization {
metadata_source: stringify!(workspace_metadata["wdk"]).to_string(),
error_source: err,
}
})?),
})
}
pub(crate) fn iter_manifest_paths(metadata: Metadata) -> impl IntoIterator<Item = Utf8PathBuf> {
let mut cargo_manifest_paths = HashSet::new();
// Add all package manifest paths
for package in metadata.packages {
cargo_manifest_paths.insert(package.manifest_path);
}
// Add workspace manifest path
let workspace_manifest_path: Utf8PathBuf = {
let mut path = metadata.workspace_root;
path.push("Cargo.toml");
path
};
cargo_manifest_paths.insert(workspace_manifest_path);
cargo_manifest_paths
}
@@ -0,0 +1,746 @@
// Copyright (c) Microsoft Corporation
// License: MIT OR Apache-2.0
use serde::{
Serialize,
ser::{self, Impossible},
};
use super::{
error::{Error, Result},
map::Map,
};
/// delimiter used to separate the names of the different nodes encoded into a
/// key name. Since `-` is not valid in Rust identifiers, it is used
/// as a separator between different node names.
pub const KEY_NAME_SEPARATOR: char = '-';
/// Serialize a value into a [`Map`] where the keys represent a
/// `KEY_NAME_SEPARATOR`-separated list of field names.
///
/// # Errors
///
/// This function will return an error if the type being serialized:
/// * results in duplicate key names
/// * results in an empty key name
/// * otherwise fails to be parsed and correctly serialized into a [`Map`]
///
/// # Example
/// ```rust
/// use std::collections::BTreeMap;
///
/// use wdk_build::{
/// DriverConfig,
/// KmdfConfig,
/// metadata::{self, to_map},
/// };
///
/// let wdk_metadata = metadata::Wdk {
/// driver_model: DriverConfig::Kmdf(KmdfConfig {
/// kmdf_version_major: 1,
/// target_kmdf_version_minor: 23,
/// minimum_kmdf_version_minor: None,
/// }),
/// };
///
/// let output = to_map::<BTreeMap<_, _>>(&wdk_metadata).unwrap();
///
/// assert_eq!(output["DRIVER_MODEL-DRIVER_TYPE"], "KMDF");
/// assert_eq!(output["DRIVER_MODEL-KMDF_VERSION_MAJOR"], "1");
/// assert_eq!(output["DRIVER_MODEL-TARGET_KMDF_VERSION_MINOR"], "23");
///
/// // `None` values are not serialized
/// assert_eq!(output.get("DRIVER_MODEL-MINIMUM_KMDF_VERSION_MINOR"), None);
/// ```
pub fn to_map<M>(value: &impl Serialize) -> Result<M>
where
M: Map<String, String>,
{
let mut serialization_buffer: Vec<(String, String)> = Vec::new();
value.serialize(&mut Serializer::new(&mut serialization_buffer))?;
convert_serialized_output_to_map(serialization_buffer)
}
/// Serialize a value into a [`Map`] where the keys represent a
/// `KEY_NAME_SEPARATOR`-separated list of field names prepended with a
/// prefix.
///
/// # Errors
///
/// This function will return an error if the type being serialized:
/// * results in duplicate key names
/// * results in an empty key name
/// * otherwise fails to be parsed and correctly serialized into a [`Map`]
///
/// # Example
/// ```rust
/// use std::collections::BTreeMap;
///
/// use wdk_build::{
/// DriverConfig,
/// KmdfConfig,
/// metadata::{self, to_map_with_prefix},
/// };
///
/// let wdk_metadata = metadata::Wdk {
/// driver_model: DriverConfig::Kmdf(KmdfConfig {
/// kmdf_version_major: 1,
/// target_kmdf_version_minor: 33,
/// minimum_kmdf_version_minor: Some(31),
/// }),
/// };
///
/// let output = to_map_with_prefix::<BTreeMap<_, _>>("WDK_BUILD_METADATA", &wdk_metadata).unwrap();
///
/// assert_eq!(
/// output["WDK_BUILD_METADATA-DRIVER_MODEL-DRIVER_TYPE"],
/// "KMDF"
/// );
/// assert_eq!(
/// output["WDK_BUILD_METADATA-DRIVER_MODEL-KMDF_VERSION_MAJOR"],
/// "1"
/// );
/// assert_eq!(
/// output["WDK_BUILD_METADATA-DRIVER_MODEL-TARGET_KMDF_VERSION_MINOR"],
/// "33"
/// );
/// assert_eq!(
/// output["WDK_BUILD_METADATA-DRIVER_MODEL-MINIMUM_KMDF_VERSION_MINOR"],
/// "31"
/// );
/// ```
pub fn to_map_with_prefix<M>(prefix: impl Into<String>, value: &impl Serialize) -> Result<M>
where
M: Map<String, String>,
{
let mut serialization_buffer: Vec<(String, String)> = Vec::new();
value.serialize(&mut Serializer::with_prefix(
prefix.into(),
&mut serialization_buffer,
))?;
convert_serialized_output_to_map(serialization_buffer)
}
fn convert_serialized_output_to_map<M>(serialization_buffer: Vec<(String, String)>) -> Result<M>
where
M: Map<String, String>,
{
let mut output_map = M::new();
for (key, value) in serialization_buffer {
output_map.insert_or_else(key, value, |key, existing_value, new_value| {
Err(Error::DuplicateSerializationKeys {
key: key.clone(),
value_1: existing_value.clone(),
value_2: new_value,
})
})?;
}
Ok(output_map)
}
/// [`serde`] serializer that serializes values into a [`Vec`] of key-value
/// pairs.
///
/// This serializer is useful when you want to have more granular control of the
/// output of the serializer. Most usecases should already be covered by the
/// [`to_map`] and [`to_map_with_prefix`] functions.
pub struct Serializer<'a> {
root_key_name: Option<String>,
dst: &'a mut Vec<(String, String)>,
}
impl<'a> ser::Serializer for &'a mut Serializer<'a> {
type Error = Error;
type Ok = ();
type SerializeMap = Impossible<Self::Ok, Self::Error>;
type SerializeSeq = Impossible<Self::Ok, Self::Error>;
type SerializeStruct = Self;
type SerializeStructVariant = Impossible<Self::Ok, Self::Error>;
type SerializeTuple = Impossible<Self::Ok, Self::Error>;
type SerializeTupleStruct = Impossible<Self::Ok, Self::Error>;
type SerializeTupleVariant = Impossible<Self::Ok, Self::Error>;
unsupported_serde_serialize_method! {
// simple types
bytes newtype_struct newtype_variant unit_struct unit_variant
// complex types (returns SerializeXYZ types)
map seq struct_variant tuple tuple_struct tuple_variant
}
fn serialize_str(self, value: &str) -> Result<Self::Ok> {
self.dst.push((
self.root_key_name
.clone()
.ok_or_else(|| Error::EmptySerializationKeyName {
value_being_serialized: value.to_string(),
})?,
value.to_string(),
));
Ok(())
}
fn serialize_bool(self, value: bool) -> Result<Self::Ok> {
self.dst.push((
self.root_key_name
.clone()
.ok_or_else(|| Error::EmptySerializationKeyName {
value_being_serialized: value.to_string(),
})?,
value.to_string(),
));
Ok(())
}
fn serialize_char(self, value: char) -> Result<Self::Ok> {
self.dst.push((
self.root_key_name
.clone()
.ok_or_else(|| Error::EmptySerializationKeyName {
value_being_serialized: value.to_string(),
})?,
value.to_string(),
));
Ok(())
}
fn serialize_i8(self, value: i8) -> Result<Self::Ok> {
self.dst.push((
self.root_key_name
.clone()
.ok_or_else(|| Error::EmptySerializationKeyName {
value_being_serialized: value.to_string(),
})?,
value.to_string(),
));
Ok(())
}
fn serialize_i16(self, value: i16) -> Result<Self::Ok> {
self.dst.push((
self.root_key_name
.clone()
.ok_or_else(|| Error::EmptySerializationKeyName {
value_being_serialized: value.to_string(),
})?,
value.to_string(),
));
Ok(())
}
fn serialize_i32(self, value: i32) -> Result<Self::Ok> {
self.dst.push((
self.root_key_name
.clone()
.ok_or_else(|| Error::EmptySerializationKeyName {
value_being_serialized: value.to_string(),
})?,
value.to_string(),
));
Ok(())
}
fn serialize_i64(self, value: i64) -> Result<Self::Ok> {
self.dst.push((
self.root_key_name
.clone()
.ok_or_else(|| Error::EmptySerializationKeyName {
value_being_serialized: value.to_string(),
})?,
value.to_string(),
));
Ok(())
}
fn serialize_f32(self, value: f32) -> Result<Self::Ok> {
self.dst.push((
self.root_key_name
.clone()
.ok_or_else(|| Error::EmptySerializationKeyName {
value_being_serialized: value.to_string(),
})?,
value.to_string(),
));
Ok(())
}
fn serialize_f64(self, value: f64) -> Result<Self::Ok> {
self.dst.push((
self.root_key_name
.clone()
.ok_or_else(|| Error::EmptySerializationKeyName {
value_being_serialized: value.to_string(),
})?,
value.to_string(),
));
Ok(())
}
fn serialize_none(self) -> Result<Self::Ok> {
self.serialize_unit()
}
fn serialize_some<T>(self, value: &T) -> Result<Self::Ok>
where
T: ?Sized + Serialize,
{
value.serialize(self)
}
fn serialize_unit(self) -> Result<Self::Ok> {
Ok(())
}
fn serialize_u8(self, value: u8) -> Result<Self::Ok> {
self.dst.push((
self.root_key_name
.clone()
.ok_or_else(|| Error::EmptySerializationKeyName {
value_being_serialized: value.to_string(),
})?,
value.to_string(),
));
Ok(())
}
fn serialize_u16(self, value: u16) -> Result<Self::Ok> {
self.dst.push((
self.root_key_name
.clone()
.ok_or_else(|| Error::EmptySerializationKeyName {
value_being_serialized: value.to_string(),
})?,
value.to_string(),
));
Ok(())
}
fn serialize_u32(self, value: u32) -> Result<Self::Ok> {
self.dst.push((
self.root_key_name
.clone()
.ok_or_else(|| Error::EmptySerializationKeyName {
value_being_serialized: value.to_string(),
})?,
value.to_string(),
));
Ok(())
}
fn serialize_u64(self, value: u64) -> Result<Self::Ok> {
self.dst.push((
self.root_key_name
.clone()
.ok_or_else(|| Error::EmptySerializationKeyName {
value_being_serialized: value.to_string(),
})?,
value.to_string(),
));
Ok(())
}
fn serialize_struct(self, _name: &'static str, _len: usize) -> Result<Self::SerializeStruct> {
Ok(self)
}
}
impl<'a> ser::SerializeStruct for &'a mut Serializer<'a> {
type Error = Error;
type Ok = ();
fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<Self::Ok>
where
T: ?Sized + Serialize,
{
value.serialize(&mut Serializer::with_prefix(
self.root_key_name.as_ref().map_or_else(
|| key.to_string(),
|root_key_name| format!("{root_key_name}{KEY_NAME_SEPARATOR}{key}"),
),
self.dst,
))?;
Ok(())
}
fn end(self) -> Result<Self::Ok> {
Ok(())
}
}
impl<'a> Serializer<'a> {
/// Create a new instance of the `Serializer` struct
pub const fn new(dst: &'a mut Vec<(String, String)>) -> Self {
Self {
root_key_name: None,
dst,
}
}
/// Create a new instance of the `Serializer` struct with a prefix used as
/// the root for all keys
pub const fn with_prefix(prefix: String, dst: &'a mut Vec<(String, String)>) -> Self {
Self {
root_key_name: Some(prefix),
dst,
}
}
}
#[doc(hidden)]
/// Helper macro when implementing the `Serializer` part of a new data
/// format for Serde.
///
/// Generates [`serde::ser::Serializer`] trait methods for serde data model
/// types that aren't supported by this serializer. This generates a
/// method that calls [`unimplemented!`].
macro_rules! unsupported_serde_serialize_method {
($($method_type:ident)*) => {
$(unsupported_serde_serialize_method_helper! {$method_type})*
};
}
#[doc(hidden)]
pub(crate) use unsupported_serde_serialize_method;
#[doc(hidden)]
macro_rules! unsupported_serde_serialize_method_helper {
// methods for simple types (returns Ok)
(bytes) => {
unsupported_serde_serialize_method_definition! {
serialize_bytes(_v: &[u8]) -> std::result::Result<
<Self as serde::ser::Serializer>::Ok,
<Self as serde::ser::Serializer>::Error,
>
}
};
(newtype_struct) => {
unsupported_serde_serialize_method_definition! {
serialize_newtype_struct<T>(_name: &'static str, _value: &T) -> std::result::Result<
<Self as serde::ser::Serializer>::Ok,
<Self as serde::ser::Serializer>::Error,
>
}
};
(newtype_variant) => {
unsupported_serde_serialize_method_definition! {
serialize_newtype_variant<T>(_name: &'static str, _variant_index: u32, _variant: &'static str, _value: &T) -> std::result::Result<
<Self as serde::ser::Serializer>::Ok,
<Self as serde::ser::Serializer>::Error,
>
}
};
(none) => {
unsupported_serde_serialize_method_definition! {
serialize_none() -> std::result::Result<
<Self as serde::ser::Serializer>::Ok,
<Self as serde::ser::Serializer>::Error,
>
}
};
(some) => {
unsupported_serde_serialize_method_definition! {
serialize_some<T>(_value: &T) -> std::result::Result<
<Self as serde::ser::Serializer>::Ok,
<Self as serde::ser::Serializer>::Error,
>
}
};
(str) => {
unsupported_serde_serialize_method_definition! {
serialize_str(_v: &str) -> std::result::Result<
<Self as serde::ser::Serializer>::Ok,
<Self as serde::ser::Serializer>::Error,
>
}
};
(unit) => {
unsupported_serde_serialize_method_definition! {
serialize_unit() -> std::result::Result<
<Self as serde::ser::Serializer>::Ok,
<Self as serde::ser::Serializer>::Error,
>
}
};
(unit_struct) => {
unsupported_serde_serialize_method_definition! {
serialize_unit_struct(_name: &'static str) -> std::result::Result<
<Self as serde::ser::Serializer>::Ok,
<Self as serde::ser::Serializer>::Error,
>
}
};
(unit_variant) => {
unsupported_serde_serialize_method_definition! {
serialize_unit_variant(_name: &'static str, _variant_index: u32, _variant: &'static str) -> std::result::Result<
<Self as serde::ser::Serializer>::Ok,
<Self as serde::ser::Serializer>::Error,
>
}
};
// methods for complex types (returns SerializeXYZ types)
(map) => {
unsupported_serde_serialize_method_definition! {
serialize_map(_len: Option<usize>) -> std::result::Result<
<Self as serde::ser::Serializer>::SerializeMap,
<Self as serde::ser::Serializer>::Error,
>
}
};
(struct) => {
unsupported_serde_serialize_method_definition! {
serialize_struct(_name: &'static str, _len: usize) -> std::result::Result<
<Self as serde::ser::Serializer>::SerializeStruct,
<Self as serde::ser::Serializer>::Error,
>
}
};
(struct_variant) => {
unsupported_serde_serialize_method_definition! {
serialize_struct_variant(_name: &'static str, _variant_index: u32, _variant: &'static str, _len: usize) -> std::result::Result<
<Self as serde::ser::Serializer>::SerializeStructVariant,
<Self as serde::ser::Serializer>::Error,
>
}
};
(seq) => {
unsupported_serde_serialize_method_definition! {
serialize_seq(_len: Option<usize>) -> std::result::Result<
<Self as serde::ser::Serializer>::SerializeSeq,
<Self as serde::ser::Serializer>::Error,
>
}
};
(tuple) => {
unsupported_serde_serialize_method_definition! {
serialize_tuple(_len: usize) -> std::result::Result<
<Self as serde::ser::Serializer>::SerializeTuple,
<Self as serde::ser::Serializer>::Error,
>
}
};
(tuple_struct) => {
unsupported_serde_serialize_method_definition! {
serialize_tuple_struct(_name: &'static str, _len: usize) -> std::result::Result<
<Self as serde::ser::Serializer>::SerializeTupleStruct,
<Self as serde::ser::Serializer>::Error,
>
}
};
(tuple_variant) => {
unsupported_serde_serialize_method_definition! {
serialize_tuple_variant(_name: &'static str, _variant_index: u32, _variant: &'static str, _len: usize) -> std::result::Result<
<Self as serde::ser::Serializer>::SerializeTupleVariant,
<Self as serde::ser::Serializer>::Error,
>
}
};
// every other method has no extra arguments and is for simple types
($method_type:ident) => {
paste::paste! {
unsupported_serde_serialize_method_definition! {
[<serialize_ $method_type>](_v: $method_type) -> std::result::Result<
<Self as serde::ser::Serializer>::Ok,
<Self as serde::ser::Serializer>::Error,
>
}
}
};
}
#[doc(hidden)]
pub(crate) use unsupported_serde_serialize_method_helper;
#[doc(hidden)]
macro_rules! unsupported_serde_serialize_method_definition {
// methods with generic argument
($func:ident <$generic_arg:ident> ($($arg:ident : $ty:ty),*) -> std::result::Result<$ok:ty, $err:ty$(,)?>) => {
#[inline]
fn $func <$generic_arg> (self, $($arg: $ty,)*) -> std::result::Result<$ok, $err>
where
$generic_arg: ?Sized + Serialize {
unimplemented!(
"{} is not implemented for {} since it is currently not needed to serialize the metadata::Wdk struct",
stringify!($func),
std::any::type_name::<Self>(),
)
}
};
// methods without generic argument
($func:ident ($($arg:ident : $ty:ty),*) -> std::result::Result<$ok:ty, $err:ty$(,)?>) => {
#[inline]
fn $func (self, $($arg: $ty,)*) -> std::result::Result<$ok, $err> {
unimplemented!(
"{} is not implemented for {} since it is currently not needed to serialize the metadata::Wdk struct",
stringify!($func),
std::any::type_name::<Self>(),
)
}
};
}
#[doc(hidden)]
pub(crate) use unsupported_serde_serialize_method_definition;
#[cfg(test)]
mod tests {
use std::{
collections::{BTreeMap, HashMap},
vec,
};
use super::*;
use crate::{DriverConfig, KmdfConfig, UmdfConfig, metadata};
#[test]
fn test_kmdf() {
let wdk_metadata = metadata::Wdk {
driver_model: DriverConfig::Kmdf(KmdfConfig {
kmdf_version_major: 1,
target_kmdf_version_minor: 23,
minimum_kmdf_version_minor: Some(21),
}),
};
let output = to_map::<BTreeMap<_, _>>(&wdk_metadata).unwrap();
assert_eq!(output["DRIVER_MODEL-DRIVER_TYPE"], "KMDF");
assert_eq!(output["DRIVER_MODEL-KMDF_VERSION_MAJOR"], "1");
assert_eq!(output["DRIVER_MODEL-TARGET_KMDF_VERSION_MINOR"], "23");
assert_eq!(output["DRIVER_MODEL-MINIMUM_KMDF_VERSION_MINOR"], "21");
}
#[test]
fn test_kmdf_no_minimum() {
let wdk_metadata = metadata::Wdk {
driver_model: DriverConfig::Kmdf(KmdfConfig {
kmdf_version_major: 1,
target_kmdf_version_minor: 23,
minimum_kmdf_version_minor: None,
}),
};
let output = to_map::<BTreeMap<_, _>>(&wdk_metadata).unwrap();
assert_eq!(output["DRIVER_MODEL-DRIVER_TYPE"], "KMDF");
assert_eq!(output["DRIVER_MODEL-KMDF_VERSION_MAJOR"], "1");
assert_eq!(output["DRIVER_MODEL-TARGET_KMDF_VERSION_MINOR"], "23");
// `None` values are not serialized
assert_eq!(output.get("DRIVER_MODEL-MINIMUM_KMDF_VERSION_MINOR"), None);
}
#[test]
fn test_kmdf_with_prefix() {
let wdk_metadata = metadata::Wdk {
driver_model: DriverConfig::Kmdf(KmdfConfig {
kmdf_version_major: 1,
target_kmdf_version_minor: 33,
minimum_kmdf_version_minor: Some(31),
}),
};
let output =
to_map_with_prefix::<BTreeMap<_, _>>("WDK_BUILD_METADATA", &wdk_metadata).unwrap();
assert_eq!(
output["WDK_BUILD_METADATA-DRIVER_MODEL-DRIVER_TYPE"],
"KMDF"
);
assert_eq!(
output["WDK_BUILD_METADATA-DRIVER_MODEL-KMDF_VERSION_MAJOR"],
"1"
);
assert_eq!(
output["WDK_BUILD_METADATA-DRIVER_MODEL-TARGET_KMDF_VERSION_MINOR"],
"33"
);
assert_eq!(
output["WDK_BUILD_METADATA-DRIVER_MODEL-MINIMUM_KMDF_VERSION_MINOR"],
"31"
);
}
#[test]
fn test_kmdf_with_hashmap() {
let wdk_metadata = metadata::Wdk {
driver_model: DriverConfig::Kmdf(KmdfConfig {
kmdf_version_major: 1,
target_kmdf_version_minor: 33,
minimum_kmdf_version_minor: Some(31),
}),
};
let output = to_map::<HashMap<_, _>>(&wdk_metadata).unwrap();
assert_eq!(output["DRIVER_MODEL-DRIVER_TYPE"], "KMDF");
assert_eq!(output["DRIVER_MODEL-KMDF_VERSION_MAJOR"], "1");
assert_eq!(output["DRIVER_MODEL-TARGET_KMDF_VERSION_MINOR"], "33");
assert_eq!(output["DRIVER_MODEL-MINIMUM_KMDF_VERSION_MINOR"], "31");
}
#[test]
fn test_umdf() {
let wdk_metadata = metadata::Wdk {
driver_model: DriverConfig::Umdf(UmdfConfig {
umdf_version_major: 1,
target_umdf_version_minor: 23,
minimum_umdf_version_minor: Some(21),
}),
};
let output = to_map::<BTreeMap<_, _>>(&wdk_metadata).unwrap();
assert_eq!(output["DRIVER_MODEL-DRIVER_TYPE"], "UMDF");
assert_eq!(output["DRIVER_MODEL-UMDF_VERSION_MAJOR"], "1");
assert_eq!(output["DRIVER_MODEL-TARGET_UMDF_VERSION_MINOR"], "23");
assert_eq!(output["DRIVER_MODEL-MINIMUM_UMDF_VERSION_MINOR"], "21");
}
#[test]
fn test_umdf_no_minimum() {
let wdk_metadata = metadata::Wdk {
driver_model: DriverConfig::Umdf(UmdfConfig {
umdf_version_major: 1,
target_umdf_version_minor: 23,
minimum_umdf_version_minor: None,
}),
};
let output = to_map::<BTreeMap<_, _>>(&wdk_metadata).unwrap();
assert_eq!(output["DRIVER_MODEL-DRIVER_TYPE"], "UMDF");
assert_eq!(output["DRIVER_MODEL-UMDF_VERSION_MAJOR"], "1");
assert_eq!(output["DRIVER_MODEL-TARGET_UMDF_VERSION_MINOR"], "23");
// `None` values are not serialized
assert_eq!(output.get("DRIVER_MODEL-MINIMUM_UMDF_VERSION_MINOR"), None);
}
#[test]
fn test_wdm() {
let wdk_metadata = metadata::Wdk {
driver_model: DriverConfig::Wdm,
};
let output = to_map::<BTreeMap<_, _>>(&wdk_metadata).unwrap();
assert_eq!(output["DRIVER_MODEL-DRIVER_TYPE"], "WDM");
}
#[test]
fn test_conflicting_keys_in_convert_serialized_output_to_map() {
let input = vec![("KEY_NAME", "VALUE_1"), ("KEY_NAME", "VALUE_2")]
.into_iter()
.map(|(k, v)| (k.to_string(), v.to_string()))
.collect();
let err = convert_serialized_output_to_map::<BTreeMap<_, _>>(input).unwrap_err();
assert!(matches!(
err,
Error::DuplicateSerializationKeys {
key,
value_1,
value_2,
} if key == "KEY_NAME" && value_1 == "VALUE_1" && value_2 == "VALUE_2"
));
}
}