-
Notifications
You must be signed in to change notification settings - Fork 513
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add JSON validator sample for WinRT (#2824)
- Loading branch information
Showing
14 changed files
with
578 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
[package] | ||
name = "sample_component_json_validator_winrt" | ||
version = "0.0.0" | ||
edition = "2021" | ||
publish = false | ||
|
||
[lib] | ||
name = "sample" | ||
crate-type = ["cdylib"] | ||
|
||
[dependencies] | ||
jsonschema = "0.17" | ||
serde_json = "1.0" | ||
|
||
[dependencies.windows] | ||
path = "../../../libs/windows" | ||
features = [ | ||
"implement", | ||
"Win32_Foundation", | ||
"Win32_System_WinRT", | ||
] | ||
|
||
[dependencies.windows-core] | ||
path = "../../../libs/core" | ||
|
||
[build-dependencies.windows-bindgen] | ||
path = "../../../libs/bindgen" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
fn main() { | ||
println!("cargo:rerun-if-changed=src/sample.idl"); | ||
let metadata_dir = format!("{}\\System32\\WinMetadata", env!("windir")); | ||
let mut command = std::process::Command::new("midlrt.exe"); | ||
|
||
command.args([ | ||
"/winrt", | ||
"/nomidl", | ||
"/h", | ||
"nul", | ||
"/metadata_dir", | ||
&metadata_dir, | ||
"/reference", | ||
&format!("{metadata_dir}\\Windows.Foundation.winmd"), | ||
"/winmd", | ||
"sample.winmd", | ||
"src/sample.idl", | ||
]); | ||
|
||
if !command.status().unwrap().success() { | ||
panic!("Failed to run midlrt"); | ||
} | ||
|
||
if let Err(error) = windows_bindgen::bindgen([ | ||
"--in", | ||
"sample.winmd", | ||
&metadata_dir, | ||
"--out", | ||
"src/bindings.rs", | ||
"--filter", | ||
"Sample", | ||
"--config", | ||
"implement", | ||
]) { | ||
panic!("{error}"); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
Sample for upcoming entry in [Getting Started with Rust](https://kennykerr.ca/rust-getting-started). |
190 changes: 190 additions & 0 deletions
190
crates/samples/components/json_validator_winrt/src/bindings.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,190 @@ | ||
// Bindings generated by `windows-bindgen` 0.52.0 | ||
|
||
#![allow( | ||
non_snake_case, | ||
non_upper_case_globals, | ||
non_camel_case_types, | ||
dead_code, | ||
clippy::all | ||
)] | ||
::windows_core::imp::com_interface!( | ||
IJsonValidator, | ||
IJsonValidator_Vtbl, | ||
0xe09cb12b_b13c_5139_8c99_6140bf80deb9 | ||
); | ||
#[repr(C)] | ||
#[doc(hidden)] | ||
pub struct IJsonValidator_Vtbl { | ||
pub base__: ::windows_core::IInspectable_Vtbl, | ||
pub Validate: unsafe extern "system" fn( | ||
*mut ::core::ffi::c_void, | ||
::std::mem::MaybeUninit<::windows_core::HSTRING>, | ||
*mut ::std::mem::MaybeUninit<::windows_core::HSTRING>, | ||
) -> ::windows_core::HRESULT, | ||
} | ||
::windows_core::imp::com_interface!( | ||
IJsonValidatorFactory, | ||
IJsonValidatorFactory_Vtbl, | ||
0x1cf4464e_ae9e_55d5_9539_0af4d8fc35aa | ||
); | ||
#[repr(C)] | ||
#[doc(hidden)] | ||
pub struct IJsonValidatorFactory_Vtbl { | ||
pub base__: ::windows_core::IInspectable_Vtbl, | ||
pub CreateInstance: unsafe extern "system" fn( | ||
*mut ::core::ffi::c_void, | ||
::std::mem::MaybeUninit<::windows_core::HSTRING>, | ||
*mut *mut ::core::ffi::c_void, | ||
) -> ::windows_core::HRESULT, | ||
} | ||
#[repr(transparent)] | ||
#[derive(::core::cmp::PartialEq, ::core::cmp::Eq, ::core::fmt::Debug, ::core::clone::Clone)] | ||
pub struct JsonValidator(::windows_core::IUnknown); | ||
::windows_core::imp::interface_hierarchy!( | ||
JsonValidator, | ||
::windows_core::IUnknown, | ||
::windows_core::IInspectable | ||
); | ||
impl JsonValidator { | ||
pub fn Validate( | ||
&self, | ||
value: &::windows_core::HSTRING, | ||
) -> ::windows_core::Result<::windows_core::HSTRING> { | ||
let this = self; | ||
unsafe { | ||
let mut result__ = ::std::mem::zeroed(); | ||
(::windows_core::Interface::vtable(this).Validate)( | ||
::windows_core::Interface::as_raw(this), | ||
::core::mem::transmute_copy(value), | ||
&mut result__, | ||
) | ||
.from_abi(result__) | ||
} | ||
} | ||
pub fn CreateInstance( | ||
schema: &::windows_core::HSTRING, | ||
) -> ::windows_core::Result<JsonValidator> { | ||
Self::IJsonValidatorFactory(|this| unsafe { | ||
let mut result__ = ::std::mem::zeroed(); | ||
(::windows_core::Interface::vtable(this).CreateInstance)( | ||
::windows_core::Interface::as_raw(this), | ||
::core::mem::transmute_copy(schema), | ||
&mut result__, | ||
) | ||
.from_abi(result__) | ||
}) | ||
} | ||
#[doc(hidden)] | ||
pub fn IJsonValidatorFactory< | ||
R, | ||
F: FnOnce(&IJsonValidatorFactory) -> ::windows_core::Result<R>, | ||
>( | ||
callback: F, | ||
) -> ::windows_core::Result<R> { | ||
static SHARED: ::windows_core::imp::FactoryCache<JsonValidator, IJsonValidatorFactory> = | ||
::windows_core::imp::FactoryCache::new(); | ||
SHARED.call(callback) | ||
} | ||
} | ||
impl ::windows_core::RuntimeType for JsonValidator { | ||
const SIGNATURE: ::windows_core::imp::ConstBuffer = | ||
::windows_core::imp::ConstBuffer::for_class::<Self>(); | ||
} | ||
unsafe impl ::windows_core::Interface for JsonValidator { | ||
type Vtable = IJsonValidator_Vtbl; | ||
const IID: ::windows_core::GUID = <IJsonValidator as ::windows_core::Interface>::IID; | ||
} | ||
impl ::windows_core::RuntimeName for JsonValidator { | ||
const NAME: &'static str = "Sample.JsonValidator"; | ||
} | ||
unsafe impl ::core::marker::Send for JsonValidator {} | ||
unsafe impl ::core::marker::Sync for JsonValidator {} | ||
pub trait IJsonValidator_Impl: Sized { | ||
fn Validate( | ||
&self, | ||
value: &::windows_core::HSTRING, | ||
) -> ::windows_core::Result<::windows_core::HSTRING>; | ||
} | ||
impl ::windows_core::RuntimeName for IJsonValidator { | ||
const NAME: &'static str = "Sample.IJsonValidator"; | ||
} | ||
impl IJsonValidator_Vtbl { | ||
pub const fn new< | ||
Identity: ::windows_core::IUnknownImpl<Impl = Impl>, | ||
Impl: IJsonValidator_Impl, | ||
const OFFSET: isize, | ||
>() -> IJsonValidator_Vtbl { | ||
unsafe extern "system" fn Validate< | ||
Identity: ::windows_core::IUnknownImpl<Impl = Impl>, | ||
Impl: IJsonValidator_Impl, | ||
const OFFSET: isize, | ||
>( | ||
this: *mut ::core::ffi::c_void, | ||
value: ::std::mem::MaybeUninit<::windows_core::HSTRING>, | ||
result__: *mut ::std::mem::MaybeUninit<::windows_core::HSTRING>, | ||
) -> ::windows_core::HRESULT { | ||
let this = (this as *const *const ()).offset(OFFSET) as *const Identity; | ||
let this = (*this).get_impl(); | ||
match this.Validate(::core::mem::transmute(&value)) { | ||
::core::result::Result::Ok(ok__) => { | ||
::core::ptr::write(result__, ::core::mem::transmute_copy(&ok__)); | ||
::core::mem::forget(ok__); | ||
::windows_core::HRESULT(0) | ||
} | ||
::core::result::Result::Err(err) => err.into(), | ||
} | ||
} | ||
Self { | ||
base__: ::windows_core::IInspectable_Vtbl::new::<Identity, IJsonValidator, OFFSET>(), | ||
Validate: Validate::<Identity, Impl, OFFSET>, | ||
} | ||
} | ||
pub fn matches(iid: &::windows_core::GUID) -> bool { | ||
iid == &<IJsonValidator as ::windows_core::Interface>::IID | ||
} | ||
} | ||
pub trait IJsonValidatorFactory_Impl: Sized { | ||
fn CreateInstance( | ||
&self, | ||
schema: &::windows_core::HSTRING, | ||
) -> ::windows_core::Result<JsonValidator>; | ||
} | ||
impl ::windows_core::RuntimeName for IJsonValidatorFactory { | ||
const NAME: &'static str = "Sample.IJsonValidatorFactory"; | ||
} | ||
impl IJsonValidatorFactory_Vtbl { | ||
pub const fn new< | ||
Identity: ::windows_core::IUnknownImpl<Impl = Impl>, | ||
Impl: IJsonValidatorFactory_Impl, | ||
const OFFSET: isize, | ||
>() -> IJsonValidatorFactory_Vtbl { | ||
unsafe extern "system" fn CreateInstance< | ||
Identity: ::windows_core::IUnknownImpl<Impl = Impl>, | ||
Impl: IJsonValidatorFactory_Impl, | ||
const OFFSET: isize, | ||
>( | ||
this: *mut ::core::ffi::c_void, | ||
schema: ::std::mem::MaybeUninit<::windows_core::HSTRING>, | ||
result__: *mut *mut ::core::ffi::c_void, | ||
) -> ::windows_core::HRESULT { | ||
let this = (this as *const *const ()).offset(OFFSET) as *const Identity; | ||
let this = (*this).get_impl(); | ||
match this.CreateInstance(::core::mem::transmute(&schema)) { | ||
::core::result::Result::Ok(ok__) => { | ||
::core::ptr::write(result__, ::core::mem::transmute_copy(&ok__)); | ||
::core::mem::forget(ok__); | ||
::windows_core::HRESULT(0) | ||
} | ||
::core::result::Result::Err(err) => err.into(), | ||
} | ||
} | ||
Self { | ||
base__: ::windows_core::IInspectable_Vtbl::new::<Identity, IJsonValidatorFactory, OFFSET>( | ||
), | ||
CreateInstance: CreateInstance::<Identity, Impl, OFFSET>, | ||
} | ||
} | ||
pub fn matches(iid: &::windows_core::GUID) -> bool { | ||
iid == &<IJsonValidatorFactory as ::windows_core::Interface>::IID | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
mod bindings; | ||
use jsonschema::JSONSchema; | ||
use windows::{core::*, Win32::Foundation::*, Win32::System::WinRT::*}; | ||
|
||
// The `JsonValidator` struct represents the implementation of the `JsonValidator` class. | ||
// The `implement` attribute provides the boilerplate COM and WinRT implementation support. | ||
#[implement(bindings::JsonValidator)] | ||
struct JsonValidator { | ||
schema: JSONSchema, | ||
} | ||
|
||
// Implement the `IJsonValidator` interface. | ||
impl bindings::IJsonValidator_Impl for JsonValidator { | ||
fn Validate(&self, value: &HSTRING) -> Result<HSTRING> { | ||
let value = json_from_hstring(value)?; | ||
|
||
if self.schema.is_valid(&value) { | ||
// Return the sanitized value. | ||
Ok(value.to_string().into()) | ||
} else { | ||
// The `validate` method returns a collection of errors. We'll just return the first | ||
// for simplicity. | ||
let message = self | ||
.schema | ||
.validate(&value) | ||
.unwrap_err() | ||
.next() | ||
.map_or(String::new(), |error| error.to_string()); | ||
|
||
Err(Error::new(E_INVALIDARG, message.into())) | ||
} | ||
} | ||
} | ||
|
||
// The `JsonValidatorFactory` struct represents the implementation of the `JsonValidator` class factory. | ||
#[implement(IActivationFactory, bindings::IJsonValidatorFactory)] | ||
struct JsonValidatorFactory; | ||
|
||
// The JsonValidator class doesn't provide a default constructor but WinRT still requires an | ||
// implementation of `IActivationFactory`. | ||
impl IActivationFactory_Impl for JsonValidatorFactory { | ||
fn ActivateInstance(&self) -> Result<IInspectable> { | ||
Err(E_NOTIMPL.into()) | ||
} | ||
} | ||
|
||
// Implement the `IJsonValidatorFactory` interface. | ||
impl bindings::IJsonValidatorFactory_Impl for JsonValidatorFactory { | ||
fn CreateInstance(&self, schema: &HSTRING) -> Result<bindings::JsonValidator> { | ||
let schema = json_from_hstring(schema)?; | ||
|
||
let schema = JSONSchema::compile(&schema) | ||
.map_err(|error| Error::new(E_INVALIDARG, error.to_string().into()))?; | ||
|
||
Ok(JsonValidator { schema }.into()) | ||
} | ||
} | ||
|
||
// Takes care of all the JSON parsing and parameter validation. | ||
fn json_from_hstring(value: &HSTRING) -> Result<serde_json::Value> { | ||
let value = String::try_from(value)?; | ||
|
||
serde_json::from_str(&value) | ||
.map_err(|error| Error::new(E_INVALIDARG, format!("{error}").into())) | ||
} | ||
|
||
#[no_mangle] | ||
extern "system" fn DllGetActivationFactory( | ||
name: std::mem::ManuallyDrop<HSTRING>, | ||
result: *mut *mut std::ffi::c_void, | ||
) -> HRESULT { | ||
if result.is_null() { | ||
return E_POINTER; | ||
} | ||
|
||
let mut factory: Option<IActivationFactory> = None; | ||
|
||
if *name == "Sample.JsonValidator" { | ||
factory = Some(JsonValidatorFactory.into()); | ||
} | ||
|
||
// Dereferencing `result` is safe because we've validated that the pointer is not null and | ||
// transmuting `factory` is safe because `DllGetActivationFactory` is expected to return an | ||
// `IActivationFactory` pointer and that's what `factory` contains. | ||
unsafe { | ||
if let Some(factory) = factory { | ||
*result = std::mem::transmute(factory); | ||
S_OK | ||
} else { | ||
*result = std::ptr::null_mut(); | ||
CLASS_E_CLASSNOTAVAILABLE | ||
} | ||
} | ||
} |
8 changes: 8 additions & 0 deletions
8
crates/samples/components/json_validator_winrt/src/sample.idl
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
namespace Sample | ||
{ | ||
runtimeclass JsonValidator | ||
{ | ||
JsonValidator(String schema); | ||
String Validate(String value); | ||
} | ||
} |
Oops, something went wrong.