Add support for to the macro

This commit is contained in:
Mihai Dinculescu
2022-10-19 13:18:26 +01:00
parent a868d09b6e
commit 3a7237addf
13 changed files with 155 additions and 45 deletions

View File

@@ -15,6 +15,7 @@ simconnect-sdk = { version = "0.1", features = ["derive"] }
use simconnect_sdk::{Notification, SimConnect, SimConnectObject}; use simconnect_sdk::{Notification, SimConnect, SimConnectObject};
/// A data structure that will be used to receive data from SimConnect. /// A data structure that will be used to receive data from SimConnect.
/// See the documentation of `SimConnectObject` for more information on the arguments of the `simconnect` attribute.
#[derive(Debug, Clone, SimConnectObject)] #[derive(Debug, Clone, SimConnectObject)]
#[simconnect(period = "second")] #[simconnect(period = "second")]
#[allow(dead_code)] #[allow(dead_code)]

View File

@@ -1,6 +1,7 @@
use simconnect_sdk::{Notification, SimConnect, SimConnectObject}; use simconnect_sdk::{Notification, SimConnect, SimConnectObject};
/// A data structure that will be used to receive data from SimConnect. /// A data structure that will be used to receive data from SimConnect.
/// See the documentation of `SimConnectObject` for more information on the arguments of the `simconnect` attribute.
#[derive(Debug, Clone, SimConnectObject)] #[derive(Debug, Clone, SimConnectObject)]
#[simconnect(period = "second")] #[simconnect(period = "second")]
#[allow(dead_code)] #[allow(dead_code)]

View File

@@ -1,6 +1,7 @@
use simconnect_sdk::{Notification, SimConnect, SimConnectObject}; use simconnect_sdk::{Notification, SimConnect, SimConnectObject};
/// A data structure that will be used to receive data from SimConnect. /// A data structure that will be used to receive data from SimConnect.
/// See the documentation of `SimConnectObject` for more information on the arguments of the `simconnect` attribute.
#[derive(Debug, Clone, SimConnectObject)] #[derive(Debug, Clone, SimConnectObject)]
#[simconnect(period = "second")] #[simconnect(period = "second")]
#[allow(dead_code)] #[allow(dead_code)]
@@ -14,6 +15,7 @@ struct GpsData {
} }
/// A second data structure that will be used to receive data from SimConnect. /// A second data structure that will be used to receive data from SimConnect.
/// See the documentation of `SimConnectObject` for more information on the arguments of the `simconnect` attribute.
#[derive(Debug, Clone, SimConnectObject)] #[derive(Debug, Clone, SimConnectObject)]
#[simconnect(period = "second", condition = "changed")] #[simconnect(period = "second", condition = "changed")]
#[allow(dead_code)] #[allow(dead_code)]

View File

@@ -3,6 +3,7 @@ use tracing::{error, info};
use tracing_subscriber::{fmt, prelude::*, EnvFilter}; use tracing_subscriber::{fmt, prelude::*, EnvFilter};
/// A data structure that will be used to receive data from SimConnect. /// A data structure that will be used to receive data from SimConnect.
/// See the documentation of `SimConnectObject` for more information on the arguments of the `simconnect` attribute.
#[derive(Debug, Clone, SimConnectObject)] #[derive(Debug, Clone, SimConnectObject)]
#[simconnect(period = "second")] #[simconnect(period = "second")]
#[allow(dead_code)] #[allow(dead_code)]

View File

@@ -6,7 +6,14 @@ use proc_macro::TokenStream;
use quote::quote; use quote::quote;
use syn::{parse_macro_input, DeriveInput}; use syn::{parse_macro_input, DeriveInput};
#[derive(Debug, PartialEq, Eq)]
enum FieldType {
Str,
Int,
}
struct FieldInfo { struct FieldInfo {
field_type: FieldType,
required: bool, required: bool,
accepted_values: Vec<String>, accepted_values: Vec<String>,
} }
@@ -17,6 +24,7 @@ static ALLOWED_CLASS_ATTRIBUTES: Lazy<HashMap<String, FieldInfo>> = Lazy::new(||
map.insert( map.insert(
"period".to_string(), "period".to_string(),
FieldInfo { FieldInfo {
field_type: FieldType::Str,
required: true, required: true,
accepted_values: vec![ accepted_values: vec![
"once".to_string(), "once".to_string(),
@@ -29,10 +37,19 @@ static ALLOWED_CLASS_ATTRIBUTES: Lazy<HashMap<String, FieldInfo>> = Lazy::new(||
map.insert( map.insert(
"condition".to_string(), "condition".to_string(),
FieldInfo { FieldInfo {
field_type: FieldType::Str,
required: false, required: false,
accepted_values: vec!["none".to_string(), "changed".to_string()], accepted_values: vec!["none".to_string(), "changed".to_string()],
}, },
); );
map.insert(
"interval".to_string(),
FieldInfo {
field_type: FieldType::Int,
required: false,
accepted_values: vec![],
},
);
map map
}); });
@@ -43,6 +60,7 @@ static ALLOWED_FIELD_ATTRIBUTES: Lazy<HashMap<String, FieldInfo>> = Lazy::new(||
map.insert( map.insert(
"name".to_string(), "name".to_string(),
FieldInfo { FieldInfo {
field_type: FieldType::Str,
required: true, required: true,
accepted_values: vec![], accepted_values: vec![],
}, },
@@ -50,6 +68,7 @@ static ALLOWED_FIELD_ATTRIBUTES: Lazy<HashMap<String, FieldInfo>> = Lazy::new(||
map.insert( map.insert(
"unit".to_string(), "unit".to_string(),
FieldInfo { FieldInfo {
field_type: FieldType::Str,
required: true, required: true,
accepted_values: vec![], accepted_values: vec![],
}, },
@@ -63,7 +82,8 @@ const SUPPORTED_FIELD_TYPES: [&str; 2] = ["f64", "bool"];
/// ///
/// # Struct Arguments /// # Struct Arguments
/// * `period` - Required. One of `once`, `visual-frame`, `sim-frame`, `second`. /// * `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`. /// * `condition` - Optional. Defaults to `none`. The condition of the data. Must be either `none` or `changed`. `changed` = Data will only be sent to the client when one or more values have changed. All the variables in a data definition will be returned if just one of the values changes.
/// * `interval` - Optional. Defaults to `0`. The number of period events that should elapse between transmissions of the data. `0` means the data is transmitted every Period, `1` means that the data is transmitted every other Period, etc.
/// ///
/// # Field Arguments /// # Field Arguments
/// * `name` - Required. The name of the field. One from <http://www.prepar3d.com/SDKv3/LearningCenter/utilities/variables/simulation_variables.html#Simulation%20Variables>. /// * `name` - Required. The name of the field. One from <http://www.prepar3d.com/SDKv3/LearningCenter/utilities/variables/simulation_variables.html#Simulation%20Variables>.
@@ -144,8 +164,8 @@ fn parse_field(f: &syn::Field) -> proc_macro2::TokenStream {
match properties { match properties {
Ok(properties) => { Ok(properties) => {
let error_message_supported_types = &format!( let error_message_supported_types = &format!(
"Field type must be one of ['{}']", r#"Field type must be one of ["{}"]"#,
SUPPORTED_FIELD_TYPES.join("', '") SUPPORTED_FIELD_TYPES.join(r#"", ""#)
); );
match ty { match ty {
@@ -182,7 +202,7 @@ fn parse_field(f: &syn::Field) -> proc_macro2::TokenStream {
fn request_data(ast: &DeriveInput) -> proc_macro2::TokenStream { fn request_data(ast: &DeriveInput) -> proc_macro2::TokenStream {
let attr = get_attribute(&ast.attrs); let attr = get_attribute(&ast.attrs);
let error_message = "expected attribute `#[simconnect(period = \"...\", condition = \"...\")]`"; let error_message = "expected attribute `#[simconnect(period = \"...\", condition = \"...\", interval = ...)]`. `condition` and `interval` are optional.";
match attr { match attr {
Some(attr) => { Some(attr) => {
@@ -227,8 +247,13 @@ fn request_data(ast: &DeriveInput) -> proc_macro2::TokenStream {
} }
}; };
let interval = match properties.get("interval") {
Some(i) => i.parse::<u32>().unwrap_or_default(),
None => 0,
};
quote! { quote! {
client.request_data_on_sim_object(id, #period, #condition, 0)?; client.request_data_on_sim_object(id, #period, #condition, #interval)?;
} }
} }
Err(e) => e, Err(e) => e,
@@ -270,8 +295,10 @@ fn extract_attribute_string_properties(
} }
match &nv.lit { match &nv.lit {
syn::Lit::Str(s) => { syn::Lit::Str(lit)
let value = s.value(); if property.field_type == FieldType::Str =>
{
let value = lit.value();
if !property.accepted_values.is_empty() if !property.accepted_values.is_empty()
&& !property.accepted_values.contains(&value) && !property.accepted_values.contains(&value)
@@ -280,8 +307,32 @@ fn extract_attribute_string_properties(
return Err(mk_err( return Err(mk_err(
nv, nv,
&format!( &format!(
"`{ident_string}` must be one of ['{}']", r#"`{ident_string}` must be one of ["{}"]"#,
property.accepted_values.join("', '") property
.accepted_values
.join(r#"", ""#)
),
));
}
results.insert(ident_string, value);
}
syn::Lit::Int(lit)
if property.field_type == FieldType::Int =>
{
let value = lit.to_string();
if !property.accepted_values.is_empty()
&& !property.accepted_values.contains(&value)
{
// found an invalid value
return Err(mk_err(
nv,
&format!(
r#"`{ident_string}` must be one of ["{}""]"#,
property
.accepted_values
.join(r#"", ""#)
), ),
)); ));
} }
@@ -291,7 +342,10 @@ fn extract_attribute_string_properties(
lit => { lit => {
return Err(syn::Error::new_spanned( return Err(syn::Error::new_spanned(
nv, nv,
format!("expected string, found {lit:?}"), format!(
"Expected {:?}, found {:?}",
property.field_type, lit
),
) )
.to_compile_error()) .to_compile_error())
} }

View File

@@ -28,4 +28,13 @@ struct GpsData3 {
pub lon: f64, pub lon: f64,
} }
#[derive(Debug, Clone, SimConnectObject)]
#[simconnect(period = "visual-frame", condition = "changed", interval = 0)]
struct GpsData4 {
#[simconnect(name = "PLANE LATITUDE", unit = "degrees")]
pub lat: f64,
#[simconnect(name = "PLANE LONGITUDE", unit = "degrees")]
pub lon: f64,
}
fn main() {} fn main() {}

View File

@@ -21,19 +21,27 @@ struct GpsData4 {}
struct GpsData5 {} struct GpsData5 {}
#[derive(Debug, Clone, SimConnectObject)] #[derive(Debug, Clone, SimConnectObject)]
#[simconnect(condition = "none", condition = "none")] #[simconnect(period = "second", condition = "none", condition = "none")]
struct GpsData6 {} struct GpsData6 {}
#[derive(Debug, Clone, SimConnectObject)] #[derive(Debug, Clone, SimConnectObject)]
#[simconnect(period = "second", condition = "none", test = "test")] #[simconnect(period = "second", interval = 0, interval = 0)]
struct GpsData7 {} struct GpsData7 {}
#[derive(Debug, Clone, SimConnectObject)] #[derive(Debug, Clone, SimConnectObject)]
#[simconnect(periodX = "second", condition = "none")] #[simconnect(period = "second", test = "test")]
struct GpsData8 {} struct GpsData8 {}
#[derive(Debug, Clone, SimConnectObject)] #[derive(Debug, Clone, SimConnectObject)]
#[simconnect(period = "second", conditionX = "none")] #[simconnect(periodX = "second", condition = "none")]
struct GpsData9 {} struct GpsData9 {}
#[derive(Debug, Clone, SimConnectObject)]
#[simconnect(period = "second", conditionX = "none")]
struct GpsData10 {}
#[derive(Debug, Clone, SimConnectObject)]
#[simconnect(period = "second", intervalX = 0)]
struct GpsData11 {}
fn main() {} fn main() {}

View File

@@ -5,50 +5,62 @@ error: Unsupported field type. Only named fields are supported.
6 | | struct GpsData1(f64); 6 | | struct GpsData1(f64);
| |_____________________^ | |_____________________^
error: expected attribute `#[simconnect(period = "...", condition = "...")]` error: expected attribute `#[simconnect(period = "...", condition = "...", interval = ...)]`. `condition` and `interval` are optional.
--> tests/02-struct-attr-errors.rs:9:1 --> tests/02-struct-attr-errors.rs:9:1
| |
9 | struct GpsData2 {} 9 | struct GpsData2 {}
| ^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^
error: expected attribute `#[simconnect(period = "...", condition = "...")]` error: expected attribute `#[simconnect(period = "...", condition = "...", interval = ...)]`. `condition` and `interval` are optional.
--> tests/02-struct-attr-errors.rs:12:3 --> tests/02-struct-attr-errors.rs:12:3
| |
12 | #[simconnect] 12 | #[simconnect]
| ^^^^^^^^^^ | ^^^^^^^^^^
error: expected attribute `#[simconnect(period = "...", condition = "...")]` error: expected attribute `#[simconnect(period = "...", condition = "...", interval = ...)]`. `condition` and `interval` are optional.
--> tests/02-struct-attr-errors.rs:16:3 --> tests/02-struct-attr-errors.rs:16:3
| |
16 | #[simconnect()] 16 | #[simconnect()]
| ^^^^^^^^^^^^ | ^^^^^^^^^^^^
error: expected attribute `#[simconnect(period = "...", condition = "...")]` error: expected attribute `#[simconnect(period = "...", condition = "...", interval = ...)]`. `condition` and `interval` are optional.
--> tests/02-struct-attr-errors.rs:20:3 --> tests/02-struct-attr-errors.rs:20:3
| |
20 | #[simconnect(period = "second", period = "second")] 20 | #[simconnect(period = "second", period = "second")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: expected attribute `#[simconnect(period = "...", condition = "...")]` error: expected attribute `#[simconnect(period = "...", condition = "...", interval = ...)]`. `condition` and `interval` are optional.
--> tests/02-struct-attr-errors.rs:24:3 --> tests/02-struct-attr-errors.rs:24:3
| |
24 | #[simconnect(condition = "none", condition = "none")] 24 | #[simconnect(period = "second", condition = "none", condition = "none")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: expected attribute `#[simconnect(period = "...", condition = "...")]` error: expected attribute `#[simconnect(period = "...", condition = "...", interval = ...)]`. `condition` and `interval` are optional.
--> tests/02-struct-attr-errors.rs:28:3 --> tests/02-struct-attr-errors.rs:28:3
| |
28 | #[simconnect(period = "second", condition = "none", test = "test")] 28 | #[simconnect(period = "second", interval = 0, interval = 0)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: expected attribute `#[simconnect(period = "...", condition = "...")]` error: expected attribute `#[simconnect(period = "...", condition = "...", interval = ...)]`. `condition` and `interval` are optional.
--> tests/02-struct-attr-errors.rs:32:3 --> tests/02-struct-attr-errors.rs:32:3
| |
32 | #[simconnect(periodX = "second", condition = "none")] 32 | #[simconnect(period = "second", test = "test")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: expected attribute `#[simconnect(period = "...", condition = "...")]` error: expected attribute `#[simconnect(period = "...", condition = "...", interval = ...)]`. `condition` and `interval` are optional.
--> tests/02-struct-attr-errors.rs:36:3 --> tests/02-struct-attr-errors.rs:36:3
| |
36 | #[simconnect(period = "second", conditionX = "none")] 36 | #[simconnect(periodX = "second", condition = "none")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: expected attribute `#[simconnect(period = "...", condition = "...", interval = ...)]`. `condition` and `interval` are optional.
--> tests/02-struct-attr-errors.rs:40:3
|
40 | #[simconnect(period = "second", conditionX = "none")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: expected attribute `#[simconnect(period = "...", condition = "...", interval = ...)]`. `condition` and `interval` are optional.
--> tests/02-struct-attr-errors.rs:44:3
|
44 | #[simconnect(period = "second", intervalX = 0)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

View File

@@ -17,9 +17,17 @@ struct GpsData3 {}
#[simconnect(period = "second", condition = "X")] #[simconnect(period = "second", condition = "X")]
struct GpsData4 {} struct GpsData4 {}
#[derive(Debug, Clone, SimConnectObject)]
#[simconnect(period = "second", interval = "X")]
struct GpsData5 {}
#[derive(Debug, Clone, SimConnectObject)]
#[simconnect(period = "second", interval = 0.0)]
struct GpsData6 {}
#[derive(Debug, Clone, SimConnectObject)] #[derive(Debug, Clone, SimConnectObject)]
#[simconnect(period = "second", condition = "none")] #[simconnect(period = "second", condition = "none")]
struct GpsData5 { struct GpsData7 {
#[simconnect(name = "PLANE LATITUDE", unit = "degrees")] #[simconnect(name = "PLANE LATITUDE", unit = "degrees")]
pub lat: String, pub lat: String,
} }

View File

@@ -1,30 +1,42 @@
error: expected string, found Int(LitInt { token: 123 }) error: Expected Str, found Int(LitInt { token: 123 })
--> tests/04-invalid-values.rs:5:14 --> tests/04-invalid-values.rs:5:14
| |
5 | #[simconnect(period = 123, condition = "none")] 5 | #[simconnect(period = 123, condition = "none")]
| ^^^^^^^^^^^^ | ^^^^^^^^^^^^
error: expected string, found Int(LitInt { token: 123 }) error: Expected Str, found Int(LitInt { token: 123 })
--> tests/04-invalid-values.rs:9:33 --> tests/04-invalid-values.rs:9:33
| |
9 | #[simconnect(period = "second", condition = 123)] 9 | #[simconnect(period = "second", condition = 123)]
| ^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^
error: `period` must be one of ['once', 'visual-frame', 'sim-frame', 'second'] error: `period` must be one of ["once", "visual-frame", "sim-frame", "second"]
--> tests/04-invalid-values.rs:13:14 --> tests/04-invalid-values.rs:13:14
| |
13 | #[simconnect(period = "X")] 13 | #[simconnect(period = "X")]
| ^^^^^^^^^^^^ | ^^^^^^^^^^^^
error: `condition` must be one of ['none', 'changed'] error: `condition` must be one of ["none", "changed"]
--> tests/04-invalid-values.rs:17:33 --> tests/04-invalid-values.rs:17:33
| |
17 | #[simconnect(period = "second", condition = "X")] 17 | #[simconnect(period = "second", condition = "X")]
| ^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^
error: Field type must be one of ['f64', 'bool'] error: Expected Int, found Str(LitStr { token: "X" })
--> tests/04-invalid-values.rs:23:5 --> tests/04-invalid-values.rs:21:33
| |
23 | / #[simconnect(name = "PLANE LATITUDE", unit = "degrees")] 21 | #[simconnect(period = "second", interval = "X")]
24 | | pub lat: String, | ^^^^^^^^^^^^^^
error: Expected Int, found Float(LitFloat { token: 0.0 })
--> tests/04-invalid-values.rs:25:33
|
25 | #[simconnect(period = "second", interval = 0.0)]
| ^^^^^^^^^^^^^^
error: Field type must be one of ["f64", "bool"]
--> tests/04-invalid-values.rs:31:5
|
31 | / #[simconnect(name = "PLANE LATITUDE", unit = "degrees")]
32 | | pub lat: String,
| |___________________^ | |___________________^

View File

@@ -5,7 +5,7 @@ use crate::bindings;
pub enum Condition { pub enum Condition {
/// The default, data will be sent strictly according to the defined period. /// The default, data will be sent strictly according to the defined period.
None, None,
/// Data will only be sent to the client when one or more values have changed. /// Data will only be sent to the client when one or more values have changed. All the variables in a data definition will be returned if just one of the values changes.
Changed, Changed,
} }

View File

@@ -1,5 +1,5 @@
/// SimConnect event notification group. /// SimConnect event notification group.
#[derive(Debug)] #[derive(Debug, Copy, Clone)]
#[repr(u32)] #[repr(u32)]
pub enum NotificationGroup { pub enum NotificationGroup {
Group0, Group0,

View File

@@ -40,7 +40,7 @@ impl SimConnect {
/// Add a Microsoft Flight Simulator simulation variable name to a client defined object definition. /// Add a Microsoft Flight Simulator simulation variable name to a client defined object definition.
/// ///
/// # Remarks /// # Remarks
/// The [`crate::SimConnectObject`] macro will automatically call this method for you. /// The [`crate::SimConnectObject`] macro will automatically call this method for the struct.
#[tracing::instrument(name = "SimConnect::add_to_data_definition")] #[tracing::instrument(name = "SimConnect::add_to_data_definition")]
pub fn add_to_data_definition( pub fn add_to_data_definition(
&self, &self,
@@ -71,11 +71,13 @@ impl SimConnect {
/// Request when the SimConnect client is to receive data values for a specific object. /// Request when the SimConnect client is to receive data values for a specific object.
/// ///
/// # Remarks /// # Arguments
/// The [`crate::SimConnectObject`] macro will automatically call this method for you. /// * `period` - [`crate::Period`]
/// * `condition` - [`crate::Condition`]
/// * `interval` - The number of period events that should elapse between transmissions of the data. `0` means the data is transmitted every Period, `1` means that the data is transmitted every other Period, etc.
/// ///
/// It is possible to change the period of a request, by re-sending the [`crate::SimConnect::request_data_on_sim_object`] call with the same `request_id` parameters, but with a new `period`. /// # Remarks
/// The one exception to this is the new period cannot be [`crate::Period::Once`], in this case a request with a new `request_id` should be sent. /// The [`crate::SimConnectObject`] macro will automatically call this method for the struct.
#[tracing::instrument(name = "SimConnect::request_data_on_sim_object")] #[tracing::instrument(name = "SimConnect::request_data_on_sim_object")]
pub fn request_data_on_sim_object( pub fn request_data_on_sim_object(
&self, &self,