From 9b7e3b97db6ee5f68f6c17897c7f5430bbaa572c Mon Sep 17 00:00:00 2001 From: Mihai Dinculescu Date: Mon, 10 Oct 2022 19:33:49 +0100 Subject: [PATCH] Add the derive proc macro --- Cargo.lock | 59 +++ README.md | 1 + examples/Cargo.toml | 9 +- examples/src/basic.rs | 83 ++--- examples/src/basic_no_macros.rs | 108 ++++++ simconnect-sdk-derive/Cargo.toml | 11 + simconnect-sdk-derive/src/lib.rs | 342 +++++++++++++++++- simconnect-sdk-derive/tests/01-parse.rs | 31 ++ .../tests/02-struct-attr-errors.rs | 39 ++ .../tests/02-struct-attr-errors.stderr | 54 +++ .../tests/03-field-attr-errors.rs | 73 ++++ .../tests/03-field-attr-errors.stderr | 59 +++ .../tests/04-invalid-values.rs | 27 ++ .../tests/04-invalid-values.stderr | 30 ++ simconnect-sdk-derive/tests/run.rs | 8 + simconnect-sdk/Cargo.toml | 6 +- simconnect-sdk/src/domain/airport_data.rs | 1 + simconnect-sdk/src/domain/condition.rs | 5 +- simconnect-sdk/src/domain/data_type.rs | 3 +- simconnect-sdk/src/domain/event.rs | 2 + simconnect-sdk/src/domain/group.rs | 5 - simconnect-sdk/src/domain/mod.rs | 4 +- simconnect-sdk/src/domain/notification.rs | 19 +- .../src/domain/notification_group.rs | 6 + simconnect-sdk/src/domain/period.rs | 15 +- simconnect-sdk/src/errors.rs | 9 + simconnect-sdk/src/lib.rs | 7 + simconnect-sdk/src/simconnect.rs | 30 +- simconnect-sdk/src/simconnect_object_ext.rs | 1 + 29 files changed, 951 insertions(+), 96 deletions(-) create mode 100644 examples/src/basic_no_macros.rs create mode 100644 simconnect-sdk-derive/tests/01-parse.rs create mode 100644 simconnect-sdk-derive/tests/02-struct-attr-errors.rs create mode 100644 simconnect-sdk-derive/tests/02-struct-attr-errors.stderr create mode 100644 simconnect-sdk-derive/tests/03-field-attr-errors.rs create mode 100644 simconnect-sdk-derive/tests/03-field-attr-errors.stderr create mode 100644 simconnect-sdk-derive/tests/04-invalid-values.rs create mode 100644 simconnect-sdk-derive/tests/04-invalid-values.stderr create mode 100644 simconnect-sdk-derive/tests/run.rs delete mode 100644 simconnect-sdk/src/domain/group.rs create mode 100644 simconnect-sdk/src/domain/notification_group.rs diff --git a/Cargo.lock b/Cargo.lock index c137029..beae9f7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -107,6 +107,12 @@ dependencies = [ "os_str_bytes", ] +[[package]] +name = "dissimilar" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c97b9233581d84b8e1e689cdd3a47b6f69770084fc246e86a7f78b0d9c1d4a5" + [[package]] name = "either" version = "1.8.0" @@ -163,6 +169,12 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "itoa" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc" + [[package]] name = "lazy_static" version = "1.4.0" @@ -353,12 +365,40 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +[[package]] +name = "ryu" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" + [[package]] name = "serde" version = "1.0.145" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b" +[[package]] +name = "serde_derive" +version = "1.0.145" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81fa1584d3d1bcacd84c277a0dfe21f5b0f6accf4a23d04d4c6d61f1af522b4c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.85" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e55a28e3aaef9d5ce0506d0a14dbba8054ddc7e499ef522dd8b26859ec9d4a44" +dependencies = [ + "itoa", + "ryu", + "serde", +] + [[package]] name = "sharded-slab" version = "0.1.4" @@ -389,9 +429,12 @@ dependencies = [ name = "simconnect-sdk-derive" version = "0.1.0" dependencies = [ + "once_cell", "proc-macro2", "quote", + "simconnect-sdk", "syn", + "trybuild", ] [[package]] @@ -541,6 +584,22 @@ dependencies = [ "tracing-log", ] +[[package]] +name = "trybuild" +version = "1.0.71" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea496675d71016e9bc76aa42d87f16aefd95447cc5818e671e12b2d7e269075d" +dependencies = [ + "dissimilar", + "glob", + "once_cell", + "serde", + "serde_derive", + "serde_json", + "termcolor", + "toml", +] + [[package]] name = "unicode-ident" version = "1.0.4" diff --git a/README.md b/README.md index 44d6d64..7a11f18 100644 --- a/README.md +++ b/README.md @@ -6,4 +6,5 @@ git clone git@github.com:mihai-dinculescu/simconnect-sdk.git cd simconnect-sdk RUST_LOG=info cargo run --bin basic +RUST_LOG=info cargo run --bin basic_no_macros ``` diff --git a/examples/Cargo.toml b/examples/Cargo.toml index c2fcf17..5257977 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -5,13 +5,18 @@ authors = ["Mihai Dinculescu "] edition = "2021" description = "SimConnect SDK Examples" license = "MIT" +publish = false [[bin]] name = "basic" path = "src/basic.rs" +[[bin]] +name = "basic_no_macros" +path = "src/basic_no_macros.rs" + [dependencies] tracing = "0.1" -tracing-subscriber = { version = "0.3", features = [ "env-filter" ] } +tracing-subscriber = { version = "0.3", features = ["env-filter"] } -simconnect-sdk = { path = "../simconnect-sdk" } +simconnect-sdk = { path = "../simconnect-sdk", features = ["derive"] } diff --git a/examples/src/basic.rs b/examples/src/basic.rs index 2054db1..29e68f0 100644 --- a/examples/src/basic.rs +++ b/examples/src/basic.rs @@ -1,66 +1,29 @@ -use simconnect_sdk::{ - ConditionEnum, DataType, Notification, NotificationData, PeriodEnum, SimConnect, - SimConnectError, -}; +#![allow(dead_code)] + +use simconnect_sdk::{Notification, SimConnect, SimConnectObject}; +use simconnect_sdk_examples::setup_logging; use tracing::{error, info}; -use simconnect_sdk_examples::setup_logging; - -#[derive(Debug, Clone)] -pub struct GpsData { - pub lat: f64, - pub lon: f64, - pub alt: f64, - pub gps_ground_magnetic_track: f64, - pub gps_ground_speed: f64, +#[derive(Debug, Clone, SimConnectObject)] +#[simconnect(period = "second")] +struct GpsData { + #[simconnect(name = "PLANE LATITUDE", unit = "degrees")] + lat: f64, + #[simconnect(name = "PLANE LONGITUDE", unit = "degrees")] + lon: f64, + #[simconnect(name = "PLANE LONGITUDE", unit = "degrees")] + alt: f64, + #[simconnect(name = "GPS GROUND MAGNETIC TRACK", unit = "degrees")] + gps_ground_magnetic_track: f64, + #[simconnect(name = "GPS GROUND SPEED", unit = "Meters per second")] + gps_ground_speed: f64, } -impl simconnect_sdk::SimConnectObjectExt for GpsData { - fn register(client: &mut SimConnect, id: u32) -> Result<(), SimConnectError> { - client.add_to_data_definition(id, "PLANE LATITUDE", "degrees", DataType::F64)?; - client.add_to_data_definition(id, "PLANE LONGITUDE", "degrees", DataType::F64)?; - client.add_to_data_definition(id, "PLANE ALTITUDE", "meters", DataType::F64)?; - client.add_to_data_definition(id, "GPS GROUND MAGNETIC TRACK", "degrees", DataType::F64)?; - client.add_to_data_definition( - id, - "GPS GROUND SPEED", - "Meters per second", - DataType::F64, - )?; - client.request_data_on_sim_object(id, PeriodEnum::Second, ConditionEnum::None)?; - - Ok(()) - } -} - -impl TryFrom<&NotificationData> for GpsData { - type Error = (); - - fn try_from(value: &NotificationData) -> Result { - value.try_into::().ok_or(()) - } -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, SimConnectObject)] +#[simconnect(period = "second", condition = "changed")] pub struct OnGround { - pub sim_on_ground: bool, -} - -impl simconnect_sdk::SimConnectObjectExt for OnGround { - fn register(client: &mut SimConnect, id: u32) -> Result<(), SimConnectError> { - client.add_to_data_definition(id, "SIM ON GROUND", "bool", DataType::Bool)?; - client.request_data_on_sim_object(id, PeriodEnum::Second, ConditionEnum::None)?; - - Ok(()) - } -} - -impl TryFrom<&NotificationData> for OnGround { - type Error = (); - - fn try_from(value: &NotificationData) -> Result { - value.try_into::().ok_or(()) - } + #[simconnect(name = "SIM ON GROUND", unit = "bool")] + sim_on_ground: bool, } fn main() -> Result<(), Box> { @@ -81,11 +44,11 @@ fn main() -> Result<(), Box> { } Some(Notification::Data(data)) => { if let Ok(gps_data) = GpsData::try_from(&data) { - info!("GPS Data: {:?}", gps_data); + info!("GPS Data: {gps_data:?}"); continue; } if let Ok(on_ground) = OnGround::try_from(&data) { - info!("On Ground data: {:?}", on_ground); + info!("On Ground data: {on_ground:?}"); continue; } } diff --git a/examples/src/basic_no_macros.rs b/examples/src/basic_no_macros.rs new file mode 100644 index 0000000..6aa5103 --- /dev/null +++ b/examples/src/basic_no_macros.rs @@ -0,0 +1,108 @@ +#![allow(dead_code)] + +use simconnect_sdk::{ + Condition, DataType, Notification, NotificationData, Period, SimConnect, SimConnectError, + SimConnectObjectExt, +}; +use tracing::{error, info}; + +use simconnect_sdk_examples::setup_logging; + +#[derive(Debug, Clone)] +pub struct GpsData { + lat: f64, + lon: f64, + alt: f64, + gps_ground_magnetic_track: f64, + gps_ground_speed: f64, +} + +impl SimConnectObjectExt for GpsData { + fn register(client: &mut SimConnect, id: u32) -> Result<(), SimConnectError> { + client.add_to_data_definition(id, "PLANE LATITUDE", "degrees", DataType::Float64)?; + client.add_to_data_definition(id, "PLANE LONGITUDE", "degrees", DataType::Float64)?; + client.add_to_data_definition(id, "PLANE ALTITUDE", "meters", DataType::Float64)?; + client.add_to_data_definition( + id, + "GPS GROUND MAGNETIC TRACK", + "degrees", + DataType::Float64, + )?; + client.add_to_data_definition( + id, + "GPS GROUND SPEED", + "Meters per second", + DataType::Float64, + )?; + client.request_data_on_sim_object(id, Period::Second, Condition::None, 0)?; + + Ok(()) + } +} + +impl TryFrom<&NotificationData> for GpsData { + type Error = SimConnectError; + + fn try_from(value: &NotificationData) -> Result { + value.try_transmute::() + } +} + +#[derive(Debug, Clone)] +pub struct OnGround { + sim_on_ground: bool, +} + +impl SimConnectObjectExt for OnGround { + fn register(client: &mut SimConnect, id: u32) -> Result<(), SimConnectError> { + client.add_to_data_definition(id, "SIM ON GROUND", "bool", DataType::Bool)?; + client.request_data_on_sim_object(id, Period::Second, Condition::Changed, 0)?; + + Ok(()) + } +} + +impl TryFrom<&NotificationData> for OnGround { + type Error = SimConnectError; + + fn try_from(value: &NotificationData) -> Result { + value.try_transmute::() + } +} + +fn main() -> Result<(), Box> { + setup_logging()?; + + let client = SimConnect::new("Simple Program"); + + match client { + Ok(mut client) => loop { + let notification = client.get_next_dispatch()?; + + match notification { + Some(Notification::Open) => { + info!("Open"); + + client.register_object::()?; + client.register_object::()?; + } + Some(Notification::Data(data)) => { + if let Ok(gps_data) = GpsData::try_from(&data) { + info!("GPS Data: {gps_data:?}"); + continue; + } + if let Ok(on_ground) = OnGround::try_from(&data) { + info!("On Ground data: {on_ground:?}"); + continue; + } + } + _ => (), + } + }, + Err(e) => { + error!("{e:?}") + } + } + + Ok(()) +} diff --git a/simconnect-sdk-derive/Cargo.toml b/simconnect-sdk-derive/Cargo.toml index 688e8ae..e34b23e 100644 --- a/simconnect-sdk-derive/Cargo.toml +++ b/simconnect-sdk-derive/Cargo.toml @@ -5,11 +5,22 @@ authors = ["Mihai Dinculescu "] edition = "2021" description = "Macros of SimConnect SDK for Rust" license = "MIT" +autotests = false +publish = false [lib] proc-macro = true +[[test]] +name = "tests" +path = "tests/run.rs" + [dependencies] proc-macro2 = "1.0" quote = "1.0" syn = "1.0" +once_cell = "1.7" + +[dev-dependencies] +simconnect-sdk = { path = "../simconnect-sdk" } +trybuild = { version = "1.0", features = ["diff"] } diff --git a/simconnect-sdk-derive/src/lib.rs b/simconnect-sdk-derive/src/lib.rs index bc58ea8..f676667 100644 --- a/simconnect-sdk-derive/src/lib.rs +++ b/simconnect-sdk-derive/src/lib.rs @@ -1,7 +1,341 @@ extern crate proc_macro; -use proc_macro::TokenStream; +use std::collections::HashMap; -#[proc_macro_derive(SimConnectObject, attributes(field))] -pub fn derive(_: TokenStream) -> TokenStream { - TokenStream::new() +use once_cell::sync::Lazy; +use proc_macro::TokenStream; +use quote::quote; +use syn::{parse_macro_input, DeriveInput}; + +struct FieldInfo { + required: bool, + accepted_values: Vec, +} + +static ALLOWED_CLASS_ATTRIBUTES: Lazy> = Lazy::new(|| { + let mut map = HashMap::new(); + + map.insert( + "period".to_string(), + FieldInfo { + required: true, + accepted_values: vec![ + "once".to_string(), + "visual-frame".to_string(), + "sim-frame".to_string(), + "second".to_string(), + ], + }, + ); + map.insert( + "condition".to_string(), + FieldInfo { + required: false, + accepted_values: vec!["none".to_string(), "changed".to_string()], + }, + ); + + map +}); + +static ALLOWED_FIELD_ATTRIBUTES: Lazy> = Lazy::new(|| { + let mut map = HashMap::new(); + + map.insert( + "name".to_string(), + FieldInfo { + required: true, + accepted_values: vec![], + }, + ); + map.insert( + "unit".to_string(), + FieldInfo { + required: true, + accepted_values: vec![], + }, + ); + + map +}); +const SUPPORTED_FIELD_TYPES: [&str; 2] = ["f64", "bool"]; + +/// SimConnectObject derive macro. +/// +/// # Struct Arguments +/// * `period` - Required. One of `once`, `visual-frame`, `sim-frame`, `second`. +/// * `condition` - Optional. The condition of the data. Must be either `none` or `changed`. Defaults to `none`. +/// +/// # Field Arguments +/// * `name` - Required. The name of the field. One from . +/// * `unit` - Required. The unit of the field. +/// +/// # Example +/// ```rust +/// use simconnect_sdk::SimConnectObject; +/// +/// #[derive(Debug, Clone, SimConnectObject)] +/// #[simconnect(period = "second")] +/// struct GpsData { +/// #[simconnect(name = "PLANE LATITUDE", unit = "degrees")] +/// lat: f64, +/// #[simconnect(name = "PLANE LONGITUDE", unit = "degrees")] +/// lon: f64, +/// #[simconnect(name = "PLANE LONGITUDE", unit = "degrees")] +/// alt: f64, +/// } +/// ``` +#[proc_macro_derive(SimConnectObject, attributes(simconnect))] +pub fn derive(input: TokenStream) -> TokenStream { + let ast = parse_macro_input!(input as DeriveInput); + + let name = &ast.ident; + + let fields = if let syn::Data::Struct(syn::DataStruct { + fields: syn::Fields::Named(syn::FieldsNamed { ref named, .. }), + .. + }) = ast.data + { + named + } else { + return mk_err( + ast, + "Unsupported field type. Only named fields are supported.", + ) + .into(); + }; + + let build_fields = fields.iter().map(parse_field); + let request_data = request_data(&ast); + + let expanded = quote! { + impl simconnect_sdk::SimConnectObjectExt for #name { + fn register(client: &mut simconnect_sdk::SimConnect, id: u32) -> Result<(), simconnect_sdk::SimConnectError> { + #(#build_fields)* + + #request_data + + Ok(()) + } + } + impl TryFrom<&simconnect_sdk::NotificationData> for #name { + type Error = simconnect_sdk::SimConnectError; + + fn try_from(value: &simconnect_sdk::NotificationData) -> Result { + value.try_transmute::<#name>() + } + } + }; + + expanded.into() +} + +fn parse_field(f: &syn::Field) -> proc_macro2::TokenStream { + let error_message = "expected attribute `#[simconnect(name = \"...\", unit = \"...\")]`"; + + let attr = get_attribute(&f.attrs); + + match attr { + Some(attr) => { + let ty = &f.ty; + let properties = + extract_attribute_string_properties(attr, &ALLOWED_FIELD_ATTRIBUTES, error_message); + + match properties { + Ok(properties) => { + let error_message_supported_types = &format!( + "Field type must be one of ['{}']", + SUPPORTED_FIELD_TYPES.join("', '") + ); + + match ty { + syn::Type::Path(syn::TypePath { path, .. }) => { + let path = &path.segments; + let path = path.iter().map(|s| &s.ident); + + let name = properties.get("name").expect("this should never happen"); + let unit = properties.get("unit").expect("this should never happen"); + + match path.last() { + Some(value) if value == "f64" => { + quote! { + client.add_to_data_definition(id, #name, #unit, simconnect_sdk::DataType::Float64)?; + } + } + Some(value) if value == "bool" => { + quote! { + client.add_to_data_definition(id, #name, #unit, simconnect_sdk::DataType::Bool)?; + } + } + _ => mk_err(f, error_message_supported_types), + } + } + _ => mk_err(f, error_message_supported_types), + } + } + Err(e) => e, + } + } + None => mk_err(f, error_message), + } +} + +fn request_data(ast: &DeriveInput) -> proc_macro2::TokenStream { + let attr = get_attribute(&ast.attrs); + let error_message = "expected attribute `#[simconnect(period = \"...\", condition = \"...\")]`"; + + match attr { + Some(attr) => { + let properties = + extract_attribute_string_properties(attr, &ALLOWED_CLASS_ATTRIBUTES, error_message); + + match properties { + Ok(properties) => { + let period = match properties.get("period") { + Some(p) if p == "once" => { + quote! { + simconnect_sdk::Period::Once + } + } + Some(p) if p == "visual-frame" => { + quote! { + simconnect_sdk::Period::VisualFrame + } + } + Some(p) if p == "sim-frame" => { + quote! { + simconnect_sdk::Period::SimFrame + } + } + _ => { + quote! { + simconnect_sdk::Period::Second + } + } + }; + + let condition = match properties.get("condition") { + Some(c) if c == "changed" => { + quote! { + simconnect_sdk::Condition::Changed + } + } + _ => { + quote! { + simconnect_sdk::Condition::None + } + } + }; + + quote! { + client.request_data_on_sim_object(id, #period, #condition, 0)?; + } + } + Err(e) => e, + } + } + None => mk_err(ast, error_message), + } +} + +fn get_attribute(attrs: &[syn::Attribute]) -> Option<&syn::Attribute> { + attrs + .iter() + .find(|&attr| attr.path.segments.len() == 1 && attr.path.segments[0].ident == "simconnect") +} + +fn extract_attribute_string_properties( + attr: &syn::Attribute, + allowed_properties: &HashMap, + error_message: &str, +) -> Result, proc_macro2::TokenStream> { + let mut results = HashMap::new(); + + match attr.parse_meta() { + Ok(syn::Meta::List(nvs)) => { + for item in nvs.nested.iter() { + match &item { + syn::NestedMeta::Meta(syn::Meta::NameValue(nv)) => { + match nv.path.get_ident() { + Some(ident) => { + let ident_string = ident.to_string(); + + let allowed_property = allowed_properties.get(&ident_string); + + match allowed_property { + Some(property) => { + if results.contains_key(&ident_string) { + // found a duplicate property name + return Err(mk_err(nvs.clone(), error_message)); + } + + match &nv.lit { + syn::Lit::Str(s) => { + let value = s.value(); + + if !property.accepted_values.is_empty() + && !property.accepted_values.contains(&value) + { + // found an invalid value + return Err(mk_err( + nv, + &format!( + "`{ident_string}` must be one of ['{}']", + property.accepted_values.join("', '") + ), + )); + } + + results.insert(ident_string, value); + } + lit => { + return Err(syn::Error::new_spanned( + nv, + format!("expected string, found {lit:?}"), + ) + .to_compile_error()) + } + } + } + None => { + // found an unexpected property name + return Err(mk_err(nvs.clone(), error_message)); + } + } + } + None => { + // no ident found + return Err(mk_err(nvs.clone(), error_message)); + } + } + } + meta => { + // nvc.nested[] was not k = v + return Err(mk_err(meta, error_message)); + } + } + } + + // check that all required properties are specified + for (field, _) in allowed_properties.iter().filter(|(_, fi)| fi.required) { + if !results.contains_key(field) { + return Err(mk_err(nvs, error_message)); + } + } + } + Ok(meta) => { + // inside of #[] there was just an identifier (`#[simconnect]`) + // or a key-value mapping (`#[simconnect = "foo"]`), neither of which are okay. + + return Err(mk_err(meta, error_message)); + } + Err(e) => { + return Err(e.to_compile_error()); + } + }; + + Ok(results) +} + +fn mk_err(t: T, message: &str) -> proc_macro2::TokenStream { + syn::Error::new_spanned(t, message).to_compile_error() } diff --git a/simconnect-sdk-derive/tests/01-parse.rs b/simconnect-sdk-derive/tests/01-parse.rs new file mode 100644 index 0000000..e982ab0 --- /dev/null +++ b/simconnect-sdk-derive/tests/01-parse.rs @@ -0,0 +1,31 @@ +#![allow(unused_variables, dead_code)] + +use simconnect_sdk_derive::SimConnectObject; +#[derive(Debug, Clone, SimConnectObject)] +#[simconnect(period = "second")] +struct GpsData1 { + #[simconnect(name = "PLANE LATITUDE", unit = "degrees")] + pub lat: f64, + #[simconnect(name = "PLANE LONGITUDE", unit = "degrees")] + pub lon: f64, +} + +#[derive(Debug, Clone, SimConnectObject)] +#[simconnect(period = "second", condition = "none")] +struct GpsData2 { + #[simconnect(name = "PLANE LATITUDE", unit = "degrees")] + pub lat: f64, + #[simconnect(name = "PLANE LONGITUDE", unit = "degrees")] + pub lon: f64, +} + +#[derive(Debug, Clone, SimConnectObject)] +#[simconnect(period = "visual-frame", condition = "changed")] +struct GpsData3 { + #[simconnect(name = "PLANE LATITUDE", unit = "degrees")] + pub lat: f64, + #[simconnect(name = "PLANE LONGITUDE", unit = "degrees")] + pub lon: f64, +} + +fn main() {} diff --git a/simconnect-sdk-derive/tests/02-struct-attr-errors.rs b/simconnect-sdk-derive/tests/02-struct-attr-errors.rs new file mode 100644 index 0000000..b63353e --- /dev/null +++ b/simconnect-sdk-derive/tests/02-struct-attr-errors.rs @@ -0,0 +1,39 @@ +#![allow(unused_variables, dead_code)] +use simconnect_sdk_derive::SimConnectObject; + +#[derive(Debug, Clone, SimConnectObject)] +#[simconnect(period = "visual-frame", condition = "changed")] +struct GpsData1(f64); + +#[derive(Debug, Clone, SimConnectObject)] +struct GpsData2 {} + +#[derive(Debug, Clone, SimConnectObject)] +#[simconnect] +struct GpsData3 {} + +#[derive(Debug, Clone, SimConnectObject)] +#[simconnect()] +struct GpsData4 {} + +#[derive(Debug, Clone, SimConnectObject)] +#[simconnect(period = "second", period = "second")] +struct GpsData5 {} + +#[derive(Debug, Clone, SimConnectObject)] +#[simconnect(condition = "none", condition = "none")] +struct GpsData6 {} + +#[derive(Debug, Clone, SimConnectObject)] +#[simconnect(period = "second", condition = "none", test = "test")] +struct GpsData7 {} + +#[derive(Debug, Clone, SimConnectObject)] +#[simconnect(periodX = "second", condition = "none")] +struct GpsData8 {} + +#[derive(Debug, Clone, SimConnectObject)] +#[simconnect(period = "second", conditionX = "none")] +struct GpsData9 {} + +fn main() {} diff --git a/simconnect-sdk-derive/tests/02-struct-attr-errors.stderr b/simconnect-sdk-derive/tests/02-struct-attr-errors.stderr new file mode 100644 index 0000000..6ca1b01 --- /dev/null +++ b/simconnect-sdk-derive/tests/02-struct-attr-errors.stderr @@ -0,0 +1,54 @@ +error: Unsupported field type. Only named fields are supported. + --> tests/02-struct-attr-errors.rs:5:1 + | +5 | / #[simconnect(period = "visual-frame", condition = "changed")] +6 | | struct GpsData1(f64); + | |_____________________^ + +error: expected attribute `#[simconnect(period = "...", condition = "...")]` + --> tests/02-struct-attr-errors.rs:9:1 + | +9 | struct GpsData2 {} + | ^^^^^^^^^^^^^^^^^^ + +error: expected attribute `#[simconnect(period = "...", condition = "...")]` + --> tests/02-struct-attr-errors.rs:12:3 + | +12 | #[simconnect] + | ^^^^^^^^^^ + +error: expected attribute `#[simconnect(period = "...", condition = "...")]` + --> tests/02-struct-attr-errors.rs:16:3 + | +16 | #[simconnect()] + | ^^^^^^^^^^^^ + +error: expected attribute `#[simconnect(period = "...", condition = "...")]` + --> tests/02-struct-attr-errors.rs:20:3 + | +20 | #[simconnect(period = "second", period = "second")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: expected attribute `#[simconnect(period = "...", condition = "...")]` + --> tests/02-struct-attr-errors.rs:24:3 + | +24 | #[simconnect(condition = "none", condition = "none")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: expected attribute `#[simconnect(period = "...", condition = "...")]` + --> tests/02-struct-attr-errors.rs:28:3 + | +28 | #[simconnect(period = "second", condition = "none", test = "test")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: expected attribute `#[simconnect(period = "...", condition = "...")]` + --> tests/02-struct-attr-errors.rs:32:3 + | +32 | #[simconnect(periodX = "second", condition = "none")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: expected attribute `#[simconnect(period = "...", condition = "...")]` + --> tests/02-struct-attr-errors.rs:36:3 + | +36 | #[simconnect(period = "second", conditionX = "none")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/simconnect-sdk-derive/tests/03-field-attr-errors.rs b/simconnect-sdk-derive/tests/03-field-attr-errors.rs new file mode 100644 index 0000000..a7caba9 --- /dev/null +++ b/simconnect-sdk-derive/tests/03-field-attr-errors.rs @@ -0,0 +1,73 @@ +#![allow(unused_variables, dead_code)] +use simconnect_sdk_derive::SimConnectObject; + +#[derive(Debug, Clone, SimConnectObject)] +#[simconnect(period = "second", condition = "none")] +struct GpsData1 { + pub lat: f64, +} + +#[derive(Debug, Clone, SimConnectObject)] +#[simconnect(period = "second", condition = "none")] +struct GpsData2 { + #[simconnect] + pub lat: f64, +} + +#[derive(Debug, Clone, SimConnectObject)] +#[simconnect(period = "second", condition = "none")] +struct GpsData3 { + #[simconnect()] + pub lat: f64, +} + +#[derive(Debug, Clone, SimConnectObject)] +#[simconnect(period = "second", condition = "none")] +struct GpsData4 { + #[simconnect(name = "PLANE LATITUDE")] + pub lat: f64, +} + +#[derive(Debug, Clone, SimConnectObject)] +#[simconnect(period = "second", condition = "none")] +struct GpsData5 { + #[simconnect(unit = "degrees")] + pub lat: f64, +} + +#[derive(Debug, Clone, SimConnectObject)] +#[simconnect(period = "second", condition = "none")] +struct GpsData6 { + #[simconnect(name = "PLANE LATITUDE", name = "PLANE LATITUDE")] + pub lat: f64, +} + +#[derive(Debug, Clone, SimConnectObject)] +#[simconnect(period = "second", condition = "none")] +struct GpsData7 { + #[simconnect(unit = "degrees", unit = "degrees")] + pub lat: f64, +} + +#[derive(Debug, Clone, SimConnectObject)] +#[simconnect(period = "second", condition = "none")] +struct GpsData8 { + #[simconnect(name = "PLANE LATITUDE", unit = "degrees", unit = "degrees")] + pub lat: f64, +} + +#[derive(Debug, Clone, SimConnectObject)] +#[simconnect(period = "second", condition = "none")] +struct GpsData9 { + #[simconnect(nameX = "PLANE LATITUDE", unit = "degrees")] + pub lat: f64, +} + +#[derive(Debug, Clone, SimConnectObject)] +#[simconnect(period = "second", condition = "none")] +struct GpsData10 { + #[simconnect(name = "PLANE LATITUDE", unitX = "degrees")] + pub lat: f64, +} + +fn main() {} diff --git a/simconnect-sdk-derive/tests/03-field-attr-errors.stderr b/simconnect-sdk-derive/tests/03-field-attr-errors.stderr new file mode 100644 index 0000000..d14d28f --- /dev/null +++ b/simconnect-sdk-derive/tests/03-field-attr-errors.stderr @@ -0,0 +1,59 @@ +error: expected attribute `#[simconnect(name = "...", unit = "...")]` + --> tests/03-field-attr-errors.rs:7:5 + | +7 | pub lat: f64, + | ^^^^^^^^^^^^ + +error: expected attribute `#[simconnect(name = "...", unit = "...")]` + --> tests/03-field-attr-errors.rs:13:7 + | +13 | #[simconnect] + | ^^^^^^^^^^ + +error: expected attribute `#[simconnect(name = "...", unit = "...")]` + --> tests/03-field-attr-errors.rs:20:7 + | +20 | #[simconnect()] + | ^^^^^^^^^^^^ + +error: expected attribute `#[simconnect(name = "...", unit = "...")]` + --> tests/03-field-attr-errors.rs:27:7 + | +27 | #[simconnect(name = "PLANE LATITUDE")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: expected attribute `#[simconnect(name = "...", unit = "...")]` + --> tests/03-field-attr-errors.rs:34:7 + | +34 | #[simconnect(unit = "degrees")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: expected attribute `#[simconnect(name = "...", unit = "...")]` + --> tests/03-field-attr-errors.rs:41:7 + | +41 | #[simconnect(name = "PLANE LATITUDE", name = "PLANE LATITUDE")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: expected attribute `#[simconnect(name = "...", unit = "...")]` + --> tests/03-field-attr-errors.rs:48:7 + | +48 | #[simconnect(unit = "degrees", unit = "degrees")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: expected attribute `#[simconnect(name = "...", unit = "...")]` + --> tests/03-field-attr-errors.rs:55:7 + | +55 | #[simconnect(name = "PLANE LATITUDE", unit = "degrees", unit = "degrees")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: expected attribute `#[simconnect(name = "...", unit = "...")]` + --> tests/03-field-attr-errors.rs:62:7 + | +62 | #[simconnect(nameX = "PLANE LATITUDE", unit = "degrees")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: expected attribute `#[simconnect(name = "...", unit = "...")]` + --> tests/03-field-attr-errors.rs:69:7 + | +69 | #[simconnect(name = "PLANE LATITUDE", unitX = "degrees")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/simconnect-sdk-derive/tests/04-invalid-values.rs b/simconnect-sdk-derive/tests/04-invalid-values.rs new file mode 100644 index 0000000..00c2bb8 --- /dev/null +++ b/simconnect-sdk-derive/tests/04-invalid-values.rs @@ -0,0 +1,27 @@ +#![allow(unused_variables, dead_code)] +use simconnect_sdk_derive::SimConnectObject; + +#[derive(Debug, Clone, SimConnectObject)] +#[simconnect(period = 123, condition = "none")] +struct GpsData1 {} + +#[derive(Debug, Clone, SimConnectObject)] +#[simconnect(period = "second", condition = 123)] +struct GpsData2 {} + +#[derive(Debug, Clone, SimConnectObject)] +#[simconnect(period = "X")] +struct GpsData3 {} + +#[derive(Debug, Clone, SimConnectObject)] +#[simconnect(period = "second", condition = "X")] +struct GpsData4 {} + +#[derive(Debug, Clone, SimConnectObject)] +#[simconnect(period = "second", condition = "none")] +struct GpsData5 { + #[simconnect(name = "PLANE LATITUDE", unit = "degrees")] + pub lat: String, +} + +fn main() {} diff --git a/simconnect-sdk-derive/tests/04-invalid-values.stderr b/simconnect-sdk-derive/tests/04-invalid-values.stderr new file mode 100644 index 0000000..c8a38b9 --- /dev/null +++ b/simconnect-sdk-derive/tests/04-invalid-values.stderr @@ -0,0 +1,30 @@ +error: expected string, found Int(LitInt { token: 123 }) + --> tests/04-invalid-values.rs:5:14 + | +5 | #[simconnect(period = 123, condition = "none")] + | ^^^^^^^^^^^^ + +error: expected string, found Int(LitInt { token: 123 }) + --> tests/04-invalid-values.rs:9:33 + | +9 | #[simconnect(period = "second", condition = 123)] + | ^^^^^^^^^^^^^^^ + +error: `period` must be one of ['once', 'visual-frame', 'sim-frame', 'second'] + --> tests/04-invalid-values.rs:13:14 + | +13 | #[simconnect(period = "X")] + | ^^^^^^^^^^^^ + +error: `condition` must be one of ['none', 'changed'] + --> tests/04-invalid-values.rs:17:33 + | +17 | #[simconnect(period = "second", condition = "X")] + | ^^^^^^^^^^^^^^^ + +error: Field type must be one of ['f64', 'bool'] + --> tests/04-invalid-values.rs:23:5 + | +23 | / #[simconnect(name = "PLANE LATITUDE", unit = "degrees")] +24 | | pub lat: String, + | |___________________^ diff --git a/simconnect-sdk-derive/tests/run.rs b/simconnect-sdk-derive/tests/run.rs new file mode 100644 index 0000000..d3e36c1 --- /dev/null +++ b/simconnect-sdk-derive/tests/run.rs @@ -0,0 +1,8 @@ +#[test] +fn tests() { + let t = trybuild::TestCases::new(); + t.pass("tests/01-parse.rs"); + t.compile_fail("tests/02-struct-attr-errors.rs"); + t.compile_fail("tests/03-field-attr-errors.rs"); + t.compile_fail("tests/04-invalid-values.rs"); +} diff --git a/simconnect-sdk/Cargo.toml b/simconnect-sdk/Cargo.toml index bd3e4b2..11f874d 100644 --- a/simconnect-sdk/Cargo.toml +++ b/simconnect-sdk/Cargo.toml @@ -6,6 +6,10 @@ edition = "2021" description = "SimConnect SDK for Rust" license = "MIT" +[features] +default = [] +derive = ["simconnect-sdk-derive"] + [build-dependencies] bindgen = "0.60" @@ -13,4 +17,4 @@ bindgen = "0.60" num_enum = "0.5" tracing = "0.1" thiserror = "1.0" -simconnect-sdk-derive = { path = "../simconnect-sdk-derive" } +simconnect-sdk-derive = { path = "../simconnect-sdk-derive", optional = true } diff --git a/simconnect-sdk/src/domain/airport_data.rs b/simconnect-sdk/src/domain/airport_data.rs index 4f9498e..ff6d841 100644 --- a/simconnect-sdk/src/domain/airport_data.rs +++ b/simconnect-sdk/src/domain/airport_data.rs @@ -1,3 +1,4 @@ +/// Airport data. #[derive(Debug, Clone)] pub struct AirportData { pub icao: String, diff --git a/simconnect-sdk/src/domain/condition.rs b/simconnect-sdk/src/domain/condition.rs index f69b3fd..60a5615 100644 --- a/simconnect-sdk/src/domain/condition.rs +++ b/simconnect-sdk/src/domain/condition.rs @@ -1,5 +1,8 @@ +/// Specifies under which conditions the data is to be sent by the server and received by the client. #[derive(Debug, Clone)] -pub enum ConditionEnum { +pub enum Condition { + /// The default, data will be sent strictly according to the defined period. None, + /// Data will only be sent to the client when one or more values have changed. Changed, } diff --git a/simconnect-sdk/src/domain/data_type.rs b/simconnect-sdk/src/domain/data_type.rs index a3db80f..d6a55eb 100644 --- a/simconnect-sdk/src/domain/data_type.rs +++ b/simconnect-sdk/src/domain/data_type.rs @@ -1,5 +1,6 @@ +/// SimConnect object property data type. #[derive(Debug, Clone)] pub enum DataType { - F64, + Float64, Bool, } diff --git a/simconnect-sdk/src/domain/event.rs b/simconnect-sdk/src/domain/event.rs index ad30f3e..69a4ad1 100644 --- a/simconnect-sdk/src/domain/event.rs +++ b/simconnect-sdk/src/domain/event.rs @@ -1,5 +1,7 @@ use std::os::raw::c_char; +/// SimConnect event. +/// As defined at #[derive(Debug, Copy, Clone, num_enum::TryFromPrimitive)] #[repr(u32)] pub enum Event { diff --git a/simconnect-sdk/src/domain/group.rs b/simconnect-sdk/src/domain/group.rs deleted file mode 100644 index b246c9f..0000000 --- a/simconnect-sdk/src/domain/group.rs +++ /dev/null @@ -1,5 +0,0 @@ -#[derive(Copy, Clone)] -#[repr(u32)] -pub enum Group { - Group0, -} diff --git a/simconnect-sdk/src/domain/mod.rs b/simconnect-sdk/src/domain/mod.rs index 79096b6..5ebc498 100644 --- a/simconnect-sdk/src/domain/mod.rs +++ b/simconnect-sdk/src/domain/mod.rs @@ -2,14 +2,14 @@ mod airport_data; mod condition; mod data_type; mod event; -mod group; mod notification; +mod notification_group; mod period; pub use airport_data::*; pub use condition::*; pub use data_type::*; pub use event::*; -pub use group::*; pub use notification::*; +pub use notification_group::*; pub use period::*; diff --git a/simconnect-sdk/src/domain/notification.rs b/simconnect-sdk/src/domain/notification.rs index b20cb5b..ee5a7f9 100644 --- a/simconnect-sdk/src/domain/notification.rs +++ b/simconnect-sdk/src/domain/notification.rs @@ -1,28 +1,39 @@ -use crate::{AirportData, Event, SimConnectObjectExt}; +use crate::{AirportData, Event, SimConnectError, SimConnectObjectExt}; +/// Notification received from SimConnect. pub enum Notification { + /// SimConnect open Open, + /// SimConnect event Event(Event), + /// SimConnect object Data(NotificationData), + /// SimConnect airport list AirportList(Vec), + /// SimConnect quit Quit, + /// SimConnect exception Exception(u32), } +/// Notification data object. pub struct NotificationData { pub(crate) type_id: String, pub(crate) data_addr: *const u32, } impl NotificationData { - pub fn try_into(&self) -> Option { + pub fn try_transmute(&self) -> Result { let type_id: String = std::any::type_name::().into(); if self.type_id == type_id { let data: &T = unsafe { std::mem::transmute_copy(&self.data_addr) }; - Some(data.clone()) + Ok(data.clone()) } else { - None + Err(SimConnectError::ObjectMismatch { + actual: self.type_id.clone(), + expected: type_id, + }) } } } diff --git a/simconnect-sdk/src/domain/notification_group.rs b/simconnect-sdk/src/domain/notification_group.rs new file mode 100644 index 0000000..e76cae1 --- /dev/null +++ b/simconnect-sdk/src/domain/notification_group.rs @@ -0,0 +1,6 @@ +/// SimConnect event notification group. +#[derive(Copy, Clone)] +#[repr(u32)] +pub enum NotificationGroup { + Group0, +} diff --git a/simconnect-sdk/src/domain/period.rs b/simconnect-sdk/src/domain/period.rs index 1f6fbff..078076c 100644 --- a/simconnect-sdk/src/domain/period.rs +++ b/simconnect-sdk/src/domain/period.rs @@ -1,5 +1,16 @@ +/// Specifies how often the data is to be sent by the server and received by the client. +/// 0 - every interval. +/// 1 - every other interval. +/// 2 - every third interval. +/// etc. #[derive(Debug, Clone)] -pub enum PeriodEnum { - VisualFrame { interval: u32 }, +pub enum Period { + /// Specifies that the data should be sent once only. Note that this is not an efficient way of receiving data frequently, use one of the other periods if there is a regular frequency to the data request. + Once, + /// Specifies that the data should be sent every visual (rendered) frame. + VisualFrame, + /// Specifies that the data should be sent every simulated frame, whether that frame is rendered or not. + SimFrame, + /// Specifies that the data should be sent once every second. Second, } diff --git a/simconnect-sdk/src/errors.rs b/simconnect-sdk/src/errors.rs index 5f61754..a236dfc 100644 --- a/simconnect-sdk/src/errors.rs +++ b/simconnect-sdk/src/errors.rs @@ -1,15 +1,24 @@ use thiserror::Error; +/// SimConnect SDK error. #[derive(Error, Debug)] pub enum SimConnectError { + /// SimConnect error. #[error("SimConnect error: {0}")] SimConnectError(i32), #[error("SimConnect unrecognized: {0}")] + /// SimConnect unrecognized error. Occurs when an unimplemented event is received by the SDK. SimConnectUnrecognizedEvent(u32), + /// Object already registered with the client instance. #[error("Object `{0}` has already been registered")] ObjectAlreadyRegistered(String), + /// Object mismatch. + #[error("Tried to convert object of type {actual} to {expected}")] + ObjectMismatch { actual: String, expected: String }, + /// Conversation error. #[error("Conversion error: {0}")] ConversionError(#[from] std::num::TryFromIntError), + /// Unexpected error. #[error("Unexpected error: {0}")] UnexpectedError(String), } diff --git a/simconnect-sdk/src/lib.rs b/simconnect-sdk/src/lib.rs index 42b2646..8bcaf3d 100644 --- a/simconnect-sdk/src/lib.rs +++ b/simconnect-sdk/src/lib.rs @@ -1,3 +1,8 @@ +//! # SimConnect SDK +//! SimConnect SDK in Rust. +//! +//! See [examples](https://github.com/mihai-dinculescu/simconnect-sdk/tree/main/examples/src). + mod bindings; mod domain; mod errors; @@ -13,5 +18,7 @@ pub use errors::SimConnectError; pub use simconnect::SimConnect; pub use simconnect_object_ext::SimConnectObjectExt; +#[cfg(feature = "simconnect-sdk-derive")] extern crate simconnect_sdk_derive; +#[cfg(feature = "simconnect-sdk-derive")] pub use simconnect_sdk_derive::*; diff --git a/simconnect-sdk/src/simconnect.rs b/simconnect-sdk/src/simconnect.rs index 67cc6f0..7842cec 100644 --- a/simconnect-sdk/src/simconnect.rs +++ b/simconnect-sdk/src/simconnect.rs @@ -2,10 +2,11 @@ use std::ffi::c_void; use crate::{ as_c_string, bindings, helpers::fixed_c_str_to_string, ok_if_fail, success, AirportData, - ConditionEnum, DataType, Event, Group, Notification, NotificationData, PeriodEnum, + Condition, DataType, Event, Notification, NotificationData, NotificationGroup, Period, SimConnectError, SimConnectObjectExt, }; +/// SimConnect SDK Client. #[derive(Debug)] pub struct SimConnect { pub handle: std::ptr::NonNull, @@ -71,7 +72,7 @@ impl SimConnect { ) }); - let group = Group::Group0; + let group = NotificationGroup::Group0; success!(unsafe { bindings::SimConnect_AddClientEventToNotificationGroup( @@ -98,7 +99,7 @@ impl SimConnect { data_type: DataType, ) -> Result<(), SimConnectError> { let c_type = match data_type { - DataType::F64 => bindings::SIMCONNECT_DATATYPE_SIMCONNECT_DATATYPE_FLOAT64, + DataType::Float64 => bindings::SIMCONNECT_DATATYPE_SIMCONNECT_DATATYPE_FLOAT64, DataType::Bool => bindings::SIMCONNECT_DATATYPE_SIMCONNECT_DATATYPE_INT32, }; @@ -121,21 +122,22 @@ impl SimConnect { pub fn request_data_on_sim_object( &self, request_id: u32, - period: PeriodEnum, - condition: ConditionEnum, + period: Period, + condition: Condition, + interval: u32, ) -> Result<(), SimConnectError> { unsafe { - let (simconnect_period, simconnect_interval) = match period { - PeriodEnum::VisualFrame { interval } => ( - bindings::SIMCONNECT_PERIOD_SIMCONNECT_PERIOD_VISUAL_FRAME, - interval, - ), - PeriodEnum::Second => (bindings::SIMCONNECT_PERIOD_SIMCONNECT_PERIOD_SECOND, 0), + let simconnect_period = match period { + Period::Once => bindings::SIMCONNECT_PERIOD_SIMCONNECT_PERIOD_ONCE, + Period::VisualFrame => bindings::SIMCONNECT_PERIOD_SIMCONNECT_PERIOD_VISUAL_FRAME, + Period::SimFrame => bindings::SIMCONNECT_PERIOD_SIMCONNECT_PERIOD_SIM_FRAME, + + Period::Second => bindings::SIMCONNECT_PERIOD_SIMCONNECT_PERIOD_SECOND, }; let simconnect_flags: u32 = match condition { - ConditionEnum::None => 0, - ConditionEnum::Changed => bindings::SIMCONNECT_DATA_REQUEST_FLAG_CHANGED, + Condition::None => 0, + Condition::Changed => bindings::SIMCONNECT_DATA_REQUEST_FLAG_CHANGED, }; success!(bindings::SimConnect_RequestDataOnSimObject( @@ -146,7 +148,7 @@ impl SimConnect { simconnect_period, simconnect_flags, 0, - simconnect_interval, + interval, 0, )); } diff --git a/simconnect-sdk/src/simconnect_object_ext.rs b/simconnect-sdk/src/simconnect_object_ext.rs index 85e4be9..1ec3812 100644 --- a/simconnect-sdk/src/simconnect_object_ext.rs +++ b/simconnect-sdk/src/simconnect_object_ext.rs @@ -1,5 +1,6 @@ use crate::{NotificationData, SimConnect, SimConnectError}; +/// Trait to be implemented by objects that can be registered with SimConnect. pub trait SimConnectObjectExt: Clone + for<'a> TryFrom<&'a NotificationData> { fn register(client: &mut SimConnect, id: u32) -> Result<(), SimConnectError>; }