Add support for String
This commit is contained in:
16
README.md
16
README.md
@@ -19,13 +19,19 @@ use simconnect_sdk::{Notification, SimConnect, SimConnectObject};
|
|||||||
#[derive(Debug, Clone, SimConnectObject)]
|
#[derive(Debug, Clone, SimConnectObject)]
|
||||||
#[simconnect(period = "second")]
|
#[simconnect(period = "second")]
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
struct GpsData {
|
struct AirplaneData {
|
||||||
|
#[simconnect(name = "TITLE")]
|
||||||
|
title: String,
|
||||||
|
#[simconnect(name = "CATEGORY")]
|
||||||
|
category: String,
|
||||||
#[simconnect(name = "PLANE LATITUDE", unit = "degrees")]
|
#[simconnect(name = "PLANE LATITUDE", unit = "degrees")]
|
||||||
lat: f64,
|
lat: f64,
|
||||||
#[simconnect(name = "PLANE LONGITUDE", unit = "degrees")]
|
#[simconnect(name = "PLANE LONGITUDE", unit = "degrees")]
|
||||||
lon: f64,
|
lon: f64,
|
||||||
#[simconnect(name = "PLANE ALTITUDE", unit = "feet")]
|
#[simconnect(name = "PLANE ALTITUDE", unit = "feet")]
|
||||||
alt: f64,
|
alt: f64,
|
||||||
|
#[simconnect(name = "SIM ON GROUND")]
|
||||||
|
sim_on_ground: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
@@ -43,17 +49,17 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
println!("Connection opened.");
|
println!("Connection opened.");
|
||||||
|
|
||||||
// After the connection is successfully open, we register the struct
|
// After the connection is successfully open, we register the struct
|
||||||
client.register_object::<GpsData>()?;
|
client.register_object::<AirplaneData>()?;
|
||||||
}
|
}
|
||||||
Some(Notification::Object(data)) => {
|
Some(Notification::Object(data)) => {
|
||||||
if let Ok(gps_data) = GpsData::try_from(&data) {
|
if let Ok(airplane_data) = AirplaneData::try_from(&data) {
|
||||||
println!("{gps_data:?}");
|
println!("{airplane_data:?}");
|
||||||
|
|
||||||
notifications_received += 1;
|
notifications_received += 1;
|
||||||
|
|
||||||
// After we have received 10 notifications, we unregister the struct
|
// After we have received 10 notifications, we unregister the struct
|
||||||
if notifications_received > 10 {
|
if notifications_received > 10 {
|
||||||
client.unregister_object::<GpsData>()?;
|
client.unregister_object::<AirplaneData>()?;
|
||||||
println!("Subscription stopped.");
|
println!("Subscription stopped.");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@@ -5,13 +5,19 @@ use simconnect_sdk::{Notification, SimConnect, SimConnectObject};
|
|||||||
#[derive(Debug, Clone, SimConnectObject)]
|
#[derive(Debug, Clone, SimConnectObject)]
|
||||||
#[simconnect(period = "second")]
|
#[simconnect(period = "second")]
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
struct GpsData {
|
struct AirplaneData {
|
||||||
|
#[simconnect(name = "TITLE")]
|
||||||
|
title: String,
|
||||||
|
#[simconnect(name = "CATEGORY")]
|
||||||
|
category: String,
|
||||||
#[simconnect(name = "PLANE LATITUDE", unit = "degrees")]
|
#[simconnect(name = "PLANE LATITUDE", unit = "degrees")]
|
||||||
lat: f64,
|
lat: f64,
|
||||||
#[simconnect(name = "PLANE LONGITUDE", unit = "degrees")]
|
#[simconnect(name = "PLANE LONGITUDE", unit = "degrees")]
|
||||||
lon: f64,
|
lon: f64,
|
||||||
#[simconnect(name = "PLANE ALTITUDE", unit = "feet")]
|
#[simconnect(name = "PLANE ALTITUDE", unit = "feet")]
|
||||||
alt: f64,
|
alt: f64,
|
||||||
|
#[simconnect(name = "SIM ON GROUND")]
|
||||||
|
sim_on_ground: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
@@ -29,17 +35,17 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
println!("Connection opened.");
|
println!("Connection opened.");
|
||||||
|
|
||||||
// After the connection is successfully open, we register the struct
|
// After the connection is successfully open, we register the struct
|
||||||
client.register_object::<GpsData>()?;
|
client.register_object::<AirplaneData>()?;
|
||||||
}
|
}
|
||||||
Some(Notification::Object(data)) => {
|
Some(Notification::Object(data)) => {
|
||||||
if let Ok(gps_data) = GpsData::try_from(&data) {
|
if let Ok(airplane_data) = AirplaneData::try_from(&data) {
|
||||||
println!("{gps_data:?}");
|
println!("{airplane_data:?}");
|
||||||
|
|
||||||
notifications_received += 1;
|
notifications_received += 1;
|
||||||
|
|
||||||
// After we have received 10 notifications, we unregister the struct
|
// After we have received 10 notifications, we unregister the struct
|
||||||
if notifications_received > 10 {
|
if notifications_received > 10 {
|
||||||
client.unregister_object::<GpsData>()?;
|
client.unregister_object::<AirplaneData>()?;
|
||||||
println!("Subscription stopped.");
|
println!("Subscription stopped.");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@@ -19,8 +19,20 @@ struct GpsData {
|
|||||||
#[derive(Debug, Clone, SimConnectObject)]
|
#[derive(Debug, Clone, SimConnectObject)]
|
||||||
#[simconnect(period = "second", condition = "changed")]
|
#[simconnect(period = "second", condition = "changed")]
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
|
struct AirplaneData {
|
||||||
|
#[simconnect(name = "TITLE")]
|
||||||
|
title: String,
|
||||||
|
#[simconnect(name = "CATEGORY")]
|
||||||
|
category: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A third 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)]
|
||||||
|
#[simconnect(period = "second", condition = "changed")]
|
||||||
|
#[allow(dead_code)]
|
||||||
pub struct OnGround {
|
pub struct OnGround {
|
||||||
#[simconnect(name = "SIM ON GROUND", unit = "bool")]
|
#[simconnect(name = "SIM ON GROUND")]
|
||||||
sim_on_ground: bool,
|
sim_on_ground: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,6 +49,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
|
|
||||||
// After the connection is successfully open, we register the structs
|
// After the connection is successfully open, we register the structs
|
||||||
client.register_object::<GpsData>()?;
|
client.register_object::<GpsData>()?;
|
||||||
|
client.register_object::<AirplaneData>()?;
|
||||||
client.register_object::<OnGround>()?;
|
client.register_object::<OnGround>()?;
|
||||||
}
|
}
|
||||||
Some(Notification::Object(data)) => {
|
Some(Notification::Object(data)) => {
|
||||||
@@ -45,6 +58,11 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
// We've already got our data, there's no point in trying another in this iteration
|
// We've already got our data, there's no point in trying another in this iteration
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if let Ok(airplane_data) = AirplaneData::try_from(&data) {
|
||||||
|
println!("{airplane_data:?}");
|
||||||
|
// We've already got our data, there's no point in trying another in this iteration
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if let Ok(on_ground) = OnGround::try_from(&data) {
|
if let Ok(on_ground) = OnGround::try_from(&data) {
|
||||||
println!("{on_ground:?}");
|
println!("{on_ground:?}");
|
||||||
// We've already got our data, there's no point in trying another in this iteration
|
// We've already got our data, there's no point in trying another in this iteration
|
||||||
|
@@ -7,13 +7,19 @@ use tracing_subscriber::{fmt, prelude::*, EnvFilter};
|
|||||||
#[derive(Debug, Clone, SimConnectObject)]
|
#[derive(Debug, Clone, SimConnectObject)]
|
||||||
#[simconnect(period = "second")]
|
#[simconnect(period = "second")]
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
struct GpsData {
|
struct AirplaneData {
|
||||||
|
#[simconnect(name = "TITLE")]
|
||||||
|
title: String,
|
||||||
|
#[simconnect(name = "CATEGORY")]
|
||||||
|
category: String,
|
||||||
#[simconnect(name = "PLANE LATITUDE", unit = "degrees")]
|
#[simconnect(name = "PLANE LATITUDE", unit = "degrees")]
|
||||||
lat: f64,
|
lat: f64,
|
||||||
#[simconnect(name = "PLANE LONGITUDE", unit = "degrees")]
|
#[simconnect(name = "PLANE LONGITUDE", unit = "degrees")]
|
||||||
lon: f64,
|
lon: f64,
|
||||||
#[simconnect(name = "PLANE ALTITUDE", unit = "feet")]
|
#[simconnect(name = "PLANE ALTITUDE", unit = "feet")]
|
||||||
alt: f64,
|
alt: f64,
|
||||||
|
#[simconnect(name = "SIM ON GROUND")]
|
||||||
|
sim_on_ground: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
@@ -33,17 +39,17 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
info!("Open");
|
info!("Open");
|
||||||
|
|
||||||
// After the connection is successfully open, we register the struct
|
// After the connection is successfully open, we register the struct
|
||||||
client.register_object::<GpsData>()?;
|
client.register_object::<AirplaneData>()?;
|
||||||
}
|
}
|
||||||
Some(Notification::Object(data)) => {
|
Some(Notification::Object(data)) => {
|
||||||
if let Ok(gps_data) = GpsData::try_from(&data) {
|
if let Ok(airplane_data) = AirplaneData::try_from(&data) {
|
||||||
info!("{gps_data:?}");
|
info!("{airplane_data:?}");
|
||||||
|
|
||||||
notifications_received += 1;
|
notifications_received += 1;
|
||||||
|
|
||||||
// After we have received 10 notifications, we unregister the struct
|
// After we have received 10 notifications, we unregister the struct
|
||||||
if notifications_received > 10 {
|
if notifications_received > 10 {
|
||||||
client.unregister_object::<GpsData>()?;
|
client.unregister_object::<AirplaneData>()?;
|
||||||
println!("Subscription stopped.");
|
println!("Subscription stopped.");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@@ -1,22 +1,41 @@
|
|||||||
|
/// This example shows all the work that the [`simconnect_sdk::SimConnectObject`] macro is doing behind the scenes.
|
||||||
|
/// You're probably better off using the macro in a real-life use-case.
|
||||||
use simconnect_sdk::{
|
use simconnect_sdk::{
|
||||||
Condition, DataType, Notification, Object, Period, SimConnect, SimConnectError,
|
fixed_c_str_to_string, Condition, DataType, Notification, Object, Period, SimConnect,
|
||||||
SimConnectObjectExt,
|
SimConnectError, SimConnectObjectExt,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A data structure that will be used to receive data from SimConnect.
|
/// A data structure that will be used to receive data from SimConnect.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub struct GpsData {
|
pub struct AirplaneData {
|
||||||
|
title: String,
|
||||||
|
category: String,
|
||||||
lat: f64,
|
lat: f64,
|
||||||
lon: f64,
|
lon: f64,
|
||||||
alt: f64,
|
alt: f64,
|
||||||
|
sim_on_ground: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SimConnectObjectExt for GpsData {
|
/// An intermediate data structure that will map 1:1 to the object received from SimConnect.
|
||||||
|
#[repr(C, packed)]
|
||||||
|
struct AirplaneDataCPacked {
|
||||||
|
title: [i8; 256],
|
||||||
|
category: [i8; 256],
|
||||||
|
lat: f64,
|
||||||
|
lon: f64,
|
||||||
|
alt: f64,
|
||||||
|
sim_on_ground: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SimConnectObjectExt for AirplaneData {
|
||||||
fn register(client: &mut SimConnect, id: u32) -> Result<(), SimConnectError> {
|
fn register(client: &mut SimConnect, id: u32) -> Result<(), SimConnectError> {
|
||||||
|
client.add_to_data_definition(id, "TITLE", "", DataType::String)?;
|
||||||
|
client.add_to_data_definition(id, "CATEGORY", "", DataType::String)?;
|
||||||
client.add_to_data_definition(id, "PLANE LATITUDE", "degrees", DataType::Float64)?;
|
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 LONGITUDE", "degrees", DataType::Float64)?;
|
||||||
client.add_to_data_definition(id, "PLANE ALTITUDE", "feet", DataType::Float64)?;
|
client.add_to_data_definition(id, "PLANE ALTITUDE", "feet", DataType::Float64)?;
|
||||||
|
client.add_to_data_definition(id, "SIM ON GROUND", "", DataType::Float64)?;
|
||||||
|
|
||||||
client.request_data_on_sim_object(id, Period::Second, Condition::None, 0)?;
|
client.request_data_on_sim_object(id, Period::Second, Condition::None, 0)?;
|
||||||
|
|
||||||
@@ -24,11 +43,20 @@ impl SimConnectObjectExt for GpsData {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<&Object> for GpsData {
|
impl TryFrom<&Object> for AirplaneData {
|
||||||
type Error = SimConnectError;
|
type Error = SimConnectError;
|
||||||
|
|
||||||
fn try_from(value: &Object) -> Result<Self, Self::Error> {
|
fn try_from(value: &Object) -> Result<Self, Self::Error> {
|
||||||
value.try_transmute::<GpsData>()
|
let raw = value.try_transmute::<AirplaneData, AirplaneDataCPacked>()?;
|
||||||
|
|
||||||
|
Ok(AirplaneData {
|
||||||
|
title: fixed_c_str_to_string(&raw.title),
|
||||||
|
category: fixed_c_str_to_string(&raw.category),
|
||||||
|
lat: raw.lat,
|
||||||
|
lon: raw.lon,
|
||||||
|
alt: raw.alt,
|
||||||
|
sim_on_ground: raw.sim_on_ground,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,17 +75,17 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
println!("Connection opened.");
|
println!("Connection opened.");
|
||||||
|
|
||||||
// After the connection is successfully open, we register the struct
|
// After the connection is successfully open, we register the struct
|
||||||
client.register_object::<GpsData>()?;
|
client.register_object::<AirplaneData>()?;
|
||||||
}
|
}
|
||||||
Some(Notification::Object(data)) => {
|
Some(Notification::Object(data)) => {
|
||||||
if let Ok(gps_data) = GpsData::try_from(&data) {
|
if let Ok(gps_data) = AirplaneData::try_from(&data) {
|
||||||
println!("{gps_data:?}");
|
println!("{gps_data:?}");
|
||||||
|
|
||||||
notifications_received += 1;
|
notifications_received += 1;
|
||||||
|
|
||||||
// After we have received 10 notifications, we unregister the struct
|
// After we have received 10 notifications, we unregister the struct
|
||||||
if notifications_received > 10 {
|
if notifications_received > 10 {
|
||||||
client.unregister_object::<GpsData>()?;
|
client.unregister_object::<AirplaneData>()?;
|
||||||
println!("Subscription stopped.");
|
println!("Subscription stopped.");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
248
simconnect-sdk-derive/src/fields.rs
Normal file
248
simconnect-sdk-derive/src/fields.rs
Normal file
@@ -0,0 +1,248 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
|
|
||||||
|
use crate::helpers::{get_attribute, mk_err};
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
pub enum FieldType {
|
||||||
|
Str,
|
||||||
|
Int,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct FieldInfo {
|
||||||
|
pub field_type: FieldType,
|
||||||
|
pub required: bool,
|
||||||
|
pub accepted_values: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub static ALLOWED_CLASS_ATTRIBUTES: Lazy<HashMap<String, FieldInfo>> = Lazy::new(|| {
|
||||||
|
let mut map = HashMap::new();
|
||||||
|
|
||||||
|
map.insert(
|
||||||
|
"period".to_string(),
|
||||||
|
FieldInfo {
|
||||||
|
field_type: FieldType::Str,
|
||||||
|
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 {
|
||||||
|
field_type: FieldType::Str,
|
||||||
|
required: false,
|
||||||
|
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
|
||||||
|
});
|
||||||
|
|
||||||
|
pub static ALLOWED_FIELD_ATTRIBUTES: Lazy<HashMap<String, FieldInfo>> = Lazy::new(|| {
|
||||||
|
let mut map = HashMap::new();
|
||||||
|
|
||||||
|
map.insert(
|
||||||
|
"name".to_string(),
|
||||||
|
FieldInfo {
|
||||||
|
field_type: FieldType::Str,
|
||||||
|
required: true,
|
||||||
|
accepted_values: vec![],
|
||||||
|
},
|
||||||
|
);
|
||||||
|
map.insert(
|
||||||
|
"unit".to_string(),
|
||||||
|
FieldInfo {
|
||||||
|
field_type: FieldType::Str,
|
||||||
|
required: false,
|
||||||
|
accepted_values: vec![],
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
map
|
||||||
|
});
|
||||||
|
pub const SUPPORTED_FIELD_TYPES: [&str; 3] = ["f64", "bool", "String"];
|
||||||
|
|
||||||
|
pub fn extract_attribute_properties(
|
||||||
|
attr: &syn::Attribute,
|
||||||
|
allowed_properties: &HashMap<String, FieldInfo>,
|
||||||
|
error_message: &str,
|
||||||
|
) -> Result<HashMap<String, String>, 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(lit)
|
||||||
|
if property.field_type == FieldType::Str =>
|
||||||
|
{
|
||||||
|
let value = lit.value();
|
||||||
|
|
||||||
|
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#"", ""#)
|
||||||
|
),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
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#"", ""#)
|
||||||
|
),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
results.insert(ident_string, value);
|
||||||
|
}
|
||||||
|
lit => {
|
||||||
|
return Err(syn::Error::new_spanned(
|
||||||
|
nv,
|
||||||
|
format!(
|
||||||
|
"Expected {:?}, found {:?}",
|
||||||
|
property.field_type, 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_field_attributes(
|
||||||
|
field: &syn::Field,
|
||||||
|
) -> Result<(&proc_macro2::Ident, &syn::Path, HashMap<String, String>), proc_macro2::TokenStream> {
|
||||||
|
let attr = get_attribute(&field.attrs);
|
||||||
|
|
||||||
|
let error_message =
|
||||||
|
"expected attribute `#[simconnect(name = \"...\", unit = \"...\")]`. `unit` is optional.";
|
||||||
|
|
||||||
|
let name = field.ident.as_ref().expect("this should not happen");
|
||||||
|
let ty = &field.ty;
|
||||||
|
|
||||||
|
match attr {
|
||||||
|
Some(attr) => {
|
||||||
|
let properties =
|
||||||
|
extract_attribute_properties(attr, &ALLOWED_FIELD_ATTRIBUTES, error_message);
|
||||||
|
|
||||||
|
match properties {
|
||||||
|
Ok(properties) => {
|
||||||
|
let error_message_supported_types = &format!(
|
||||||
|
r#"Field type must be one of ["{}"]."#,
|
||||||
|
SUPPORTED_FIELD_TYPES.join(r#"", ""#)
|
||||||
|
);
|
||||||
|
|
||||||
|
match ty {
|
||||||
|
syn::Type::Path(syn::TypePath { path, .. }) => {
|
||||||
|
let path_segments = &path.segments;
|
||||||
|
let path_idents = path_segments.iter().map(|s| &s.ident);
|
||||||
|
|
||||||
|
match path_idents.last() {
|
||||||
|
Some(value)
|
||||||
|
if SUPPORTED_FIELD_TYPES
|
||||||
|
.contains(&value.to_string().as_str()) =>
|
||||||
|
{
|
||||||
|
Ok((name, path, properties))
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => Err(mk_err(ty, error_message_supported_types)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => Err(mk_err(ty, error_message_supported_types)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => Err(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => Err(mk_err(field, error_message)),
|
||||||
|
}
|
||||||
|
}
|
9
simconnect-sdk-derive/src/helpers.rs
Normal file
9
simconnect-sdk-derive/src/helpers.rs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
pub fn get_attribute(attrs: &[syn::Attribute]) -> Option<&syn::Attribute> {
|
||||||
|
attrs
|
||||||
|
.iter()
|
||||||
|
.find(|&attr| attr.path.segments.len() == 1 && attr.path.segments[0].ident == "simconnect")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mk_err<T: quote::ToTokens>(t: T, message: &str) -> proc_macro2::TokenStream {
|
||||||
|
syn::Error::new_spanned(t, message).to_compile_error()
|
||||||
|
}
|
@@ -1,82 +1,15 @@
|
|||||||
extern crate proc_macro;
|
extern crate proc_macro;
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use once_cell::sync::Lazy;
|
use fields::{extract_attribute_properties, parse_field_attributes, ALLOWED_CLASS_ATTRIBUTES};
|
||||||
|
use helpers::{get_attribute, mk_err};
|
||||||
use proc_macro::TokenStream;
|
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)]
|
mod fields;
|
||||||
enum FieldType {
|
mod helpers;
|
||||||
Str,
|
|
||||||
Int,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct FieldInfo {
|
|
||||||
field_type: FieldType,
|
|
||||||
required: bool,
|
|
||||||
accepted_values: Vec<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
static ALLOWED_CLASS_ATTRIBUTES: Lazy<HashMap<String, FieldInfo>> = Lazy::new(|| {
|
|
||||||
let mut map = HashMap::new();
|
|
||||||
|
|
||||||
map.insert(
|
|
||||||
"period".to_string(),
|
|
||||||
FieldInfo {
|
|
||||||
field_type: FieldType::Str,
|
|
||||||
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 {
|
|
||||||
field_type: FieldType::Str,
|
|
||||||
required: false,
|
|
||||||
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
|
|
||||||
});
|
|
||||||
|
|
||||||
static ALLOWED_FIELD_ATTRIBUTES: Lazy<HashMap<String, FieldInfo>> = Lazy::new(|| {
|
|
||||||
let mut map = HashMap::new();
|
|
||||||
|
|
||||||
map.insert(
|
|
||||||
"name".to_string(),
|
|
||||||
FieldInfo {
|
|
||||||
field_type: FieldType::Str,
|
|
||||||
required: true,
|
|
||||||
accepted_values: vec![],
|
|
||||||
},
|
|
||||||
);
|
|
||||||
map.insert(
|
|
||||||
"unit".to_string(),
|
|
||||||
FieldInfo {
|
|
||||||
field_type: FieldType::Str,
|
|
||||||
required: true,
|
|
||||||
accepted_values: vec![],
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
map
|
|
||||||
});
|
|
||||||
const SUPPORTED_FIELD_TYPES: [&str; 2] = ["f64", "bool"];
|
|
||||||
|
|
||||||
/// SimConnectObject derive macro.
|
/// SimConnectObject derive macro.
|
||||||
///
|
///
|
||||||
@@ -87,7 +20,7 @@ const SUPPORTED_FIELD_TYPES: [&str; 2] = ["f64", "bool"];
|
|||||||
///
|
///
|
||||||
/// # 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>.
|
||||||
/// * `unit` - Required. The unit of the field.
|
/// * `unit` - Optional. The unit of the field. For `string`s and `bool`s it should be left out or be empty string. For numeric fields it should be one from <http://www.prepar3d.com/SDKv3/LearningCenter/utilities/variables/simulation_variables.html#Simulation%20Variables>.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
@@ -96,20 +29,27 @@ const SUPPORTED_FIELD_TYPES: [&str; 2] = ["f64", "bool"];
|
|||||||
///
|
///
|
||||||
/// #[derive(Debug, Clone, SimConnectObject)]
|
/// #[derive(Debug, Clone, SimConnectObject)]
|
||||||
/// #[simconnect(period = "second")]
|
/// #[simconnect(period = "second")]
|
||||||
/// struct GpsData {
|
/// struct AirplaneData {
|
||||||
|
/// #[simconnect(name = "TITLE")]
|
||||||
|
/// title: String,
|
||||||
|
/// #[simconnect(name = "CATEGORY")]
|
||||||
|
/// category: String,
|
||||||
/// #[simconnect(name = "PLANE LATITUDE", unit = "degrees")]
|
/// #[simconnect(name = "PLANE LATITUDE", unit = "degrees")]
|
||||||
/// lat: f64,
|
/// lat: f64,
|
||||||
/// #[simconnect(name = "PLANE LONGITUDE", unit = "degrees")]
|
/// #[simconnect(name = "PLANE LONGITUDE", unit = "degrees")]
|
||||||
/// lon: f64,
|
/// lon: f64,
|
||||||
/// #[simconnect(name = "PLANE LONGITUDE", unit = "degrees")]
|
/// #[simconnect(name = "PLANE ALTITUDE", unit = "feet")]
|
||||||
/// alt: f64,
|
/// alt: f64,
|
||||||
|
/// #[simconnect(name = "SIM ON GROUND", unit = "bool")]
|
||||||
|
/// sim_on_ground: bool,
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
#[proc_macro_derive(SimConnectObject, attributes(simconnect))]
|
#[proc_macro_derive(SimConnectObject, attributes(simconnect))]
|
||||||
pub fn derive(input: TokenStream) -> TokenStream {
|
pub fn derive(input: TokenStream) -> TokenStream {
|
||||||
let ast = parse_macro_input!(input as DeriveInput);
|
let ast = parse_macro_input!(input as DeriveInput);
|
||||||
|
|
||||||
let name = &ast.ident;
|
let name_ident = &ast.ident;
|
||||||
|
let packed_ident = syn::Ident::new(&format!("{}CPacked", name_ident), name_ident.span());
|
||||||
|
|
||||||
let fields = if let syn::Data::Struct(syn::DataStruct {
|
let fields = if let syn::Data::Struct(syn::DataStruct {
|
||||||
fields: syn::Fields::Named(syn::FieldsNamed { ref named, .. }),
|
fields: syn::Fields::Named(syn::FieldsNamed { ref named, .. }),
|
||||||
@@ -125,24 +65,53 @@ pub fn derive(input: TokenStream) -> TokenStream {
|
|||||||
.into();
|
.into();
|
||||||
};
|
};
|
||||||
|
|
||||||
let build_fields = fields.iter().map(parse_field);
|
// parse the fields and their attributes
|
||||||
let request_data = request_data(&ast);
|
let mut parsed_fields = Vec::with_capacity(fields.len());
|
||||||
|
for field in fields {
|
||||||
|
let result = parse_field_attributes(field);
|
||||||
|
|
||||||
|
match result {
|
||||||
|
Ok(field) => {
|
||||||
|
parsed_fields.push(field);
|
||||||
|
}
|
||||||
|
Err(e) => return e.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// packed struct fields
|
||||||
|
let packed_fields = parsed_fields
|
||||||
|
.iter()
|
||||||
|
.map(|(ident, path, _)| build_packed_field(ident, path));
|
||||||
|
let packed_fields_assignments = parsed_fields
|
||||||
|
.iter()
|
||||||
|
.map(|(ident, path, _)| build_packed_field_assignment(ident, path));
|
||||||
|
|
||||||
|
// SC fields
|
||||||
|
let sc_definition = parsed_fields
|
||||||
|
.iter()
|
||||||
|
.map(|(_, path, properties)| build_sc_definition(path, properties));
|
||||||
|
let sc_request = build_sc_request(&ast);
|
||||||
|
|
||||||
|
// put everything together
|
||||||
let expanded = quote! {
|
let expanded = quote! {
|
||||||
impl simconnect_sdk::SimConnectObjectExt for #name {
|
#[repr(C, packed)]
|
||||||
|
struct #packed_ident {
|
||||||
|
#(#packed_fields,)*
|
||||||
|
}
|
||||||
|
impl simconnect_sdk::SimConnectObjectExt for #name_ident {
|
||||||
fn register(client: &mut simconnect_sdk::SimConnect, id: u32) -> Result<(), simconnect_sdk::SimConnectError> {
|
fn register(client: &mut simconnect_sdk::SimConnect, id: u32) -> Result<(), simconnect_sdk::SimConnectError> {
|
||||||
#(#build_fields)*
|
#(#sc_definition)*
|
||||||
|
#sc_request
|
||||||
#request_data
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl TryFrom<&simconnect_sdk::Object> for #name {
|
impl TryFrom<&simconnect_sdk::Object> for #name_ident {
|
||||||
type Error = simconnect_sdk::SimConnectError;
|
type Error = simconnect_sdk::SimConnectError;
|
||||||
|
|
||||||
fn try_from(value: &simconnect_sdk::Object) -> Result<Self, Self::Error> {
|
fn try_from(value: &simconnect_sdk::Object) -> Result<Self, Self::Error> {
|
||||||
value.try_transmute::<#name>()
|
let raw = value.try_transmute::<#name_ident, #packed_ident>()?;
|
||||||
|
Ok(#name_ident {
|
||||||
|
#(#packed_fields_assignments,)*
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -150,33 +119,62 @@ pub fn derive(input: TokenStream) -> TokenStream {
|
|||||||
expanded.into()
|
expanded.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_field(f: &syn::Field) -> proc_macro2::TokenStream {
|
fn build_packed_field(ident: &proc_macro2::Ident, path: &syn::Path) -> proc_macro2::TokenStream {
|
||||||
let error_message = "expected attribute `#[simconnect(name = \"...\", unit = \"...\")]`";
|
let path_segments = &path.segments;
|
||||||
|
let path_idents = path_segments.iter().map(|s| &s.ident);
|
||||||
|
|
||||||
let attr = get_attribute(&f.attrs);
|
match path_idents.last() {
|
||||||
|
Some(value) if value == "String" => {
|
||||||
|
quote! {
|
||||||
|
#ident: [std::primitive::i8; 256]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
quote! {
|
||||||
|
#ident: #path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
match attr {
|
fn build_packed_field_assignment(
|
||||||
Some(attr) => {
|
ident: &proc_macro2::Ident,
|
||||||
let ty = &f.ty;
|
path: &syn::Path,
|
||||||
let properties =
|
) -> proc_macro2::TokenStream {
|
||||||
extract_attribute_string_properties(attr, &ALLOWED_FIELD_ATTRIBUTES, error_message);
|
let path_segments = &path.segments;
|
||||||
|
let path_idents = path_segments.iter().map(|s| &s.ident);
|
||||||
|
|
||||||
match properties {
|
match path_idents.last() {
|
||||||
Ok(properties) => {
|
Some(value) if value == "String" => {
|
||||||
let error_message_supported_types = &format!(
|
quote! {
|
||||||
r#"Field type must be one of ["{}"]"#,
|
#ident: simconnect_sdk::fixed_c_str_to_string(&raw.#ident)
|
||||||
SUPPORTED_FIELD_TYPES.join(r#"", ""#)
|
}
|
||||||
);
|
}
|
||||||
|
_ => {
|
||||||
|
quote! {
|
||||||
|
#ident: raw.#ident
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
match ty {
|
fn build_sc_definition(
|
||||||
syn::Type::Path(syn::TypePath { path, .. }) => {
|
path: &syn::Path,
|
||||||
let path = &path.segments;
|
properties: &HashMap<String, String>,
|
||||||
let path = path.iter().map(|s| &s.ident);
|
) -> proc_macro2::TokenStream {
|
||||||
|
let error_message =
|
||||||
|
"expected attribute `#[simconnect(name = \"...\", unit = \"...\")]`. `unit` is optional.";
|
||||||
|
|
||||||
|
let path_segments = &path.segments;
|
||||||
|
let path_idents = path_segments.iter().map(|s| &s.ident);
|
||||||
|
|
||||||
let name = properties.get("name").expect("this should never happen");
|
let name = properties.get("name").expect("this should never happen");
|
||||||
let unit = properties.get("unit").expect("this should never happen");
|
let unit = match properties.get("unit") {
|
||||||
|
Some(unit) => unit,
|
||||||
|
None => "",
|
||||||
|
};
|
||||||
|
|
||||||
match path.last() {
|
match path_idents.last() {
|
||||||
Some(value) if value == "f64" => {
|
Some(value) if value == "f64" => {
|
||||||
quote! {
|
quote! {
|
||||||
client.add_to_data_definition(id, #name, #unit, simconnect_sdk::DataType::Float64)?;
|
client.add_to_data_definition(id, #name, #unit, simconnect_sdk::DataType::Float64)?;
|
||||||
@@ -187,27 +185,26 @@ fn parse_field(f: &syn::Field) -> proc_macro2::TokenStream {
|
|||||||
client.add_to_data_definition(id, #name, #unit, simconnect_sdk::DataType::Bool)?;
|
client.add_to_data_definition(id, #name, #unit, simconnect_sdk::DataType::Bool)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => mk_err(f, error_message_supported_types),
|
Some(value) if value == "String" => {
|
||||||
|
quote! {
|
||||||
|
client.add_to_data_definition(id, #name, #unit, simconnect_sdk::DataType::String)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => mk_err(f, error_message_supported_types),
|
_ => {
|
||||||
|
// this error is already caught in `parse_field_attributes`
|
||||||
|
mk_err(path, error_message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => e,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => mk_err(f, error_message),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn request_data(ast: &DeriveInput) -> proc_macro2::TokenStream {
|
fn build_sc_request(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 = \"...\", interval = ...)]`. `condition` and `interval` are optional.";
|
let error_message = "expected attribute `#[simconnect(period = \"...\", condition = \"...\", interval = ...)]`. `condition` and `interval` are optional.";
|
||||||
|
|
||||||
match attr {
|
match attr {
|
||||||
Some(attr) => {
|
Some(attr) => {
|
||||||
let properties =
|
let properties =
|
||||||
extract_attribute_string_properties(attr, &ALLOWED_CLASS_ATTRIBUTES, error_message);
|
extract_attribute_properties(attr, &ALLOWED_CLASS_ATTRIBUTES, error_message);
|
||||||
|
|
||||||
match properties {
|
match properties {
|
||||||
Ok(properties) => {
|
Ok(properties) => {
|
||||||
@@ -262,135 +259,3 @@ fn request_data(ast: &DeriveInput) -> proc_macro2::TokenStream {
|
|||||||
None => mk_err(ast, error_message),
|
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<String, FieldInfo>,
|
|
||||||
error_message: &str,
|
|
||||||
) -> Result<HashMap<String, String>, 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(lit)
|
|
||||||
if property.field_type == FieldType::Str =>
|
|
||||||
{
|
|
||||||
let value = lit.value();
|
|
||||||
|
|
||||||
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#"", ""#)
|
|
||||||
),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
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#"", ""#)
|
|
||||||
),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
results.insert(ident_string, value);
|
|
||||||
}
|
|
||||||
lit => {
|
|
||||||
return Err(syn::Error::new_spanned(
|
|
||||||
nv,
|
|
||||||
format!(
|
|
||||||
"Expected {:?}, found {:?}",
|
|
||||||
property.field_type, 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: quote::ToTokens>(t: T, message: &str) -> proc_macro2::TokenStream {
|
|
||||||
syn::Error::new_spanned(t, message).to_compile_error()
|
|
||||||
}
|
|
||||||
|
@@ -3,7 +3,7 @@
|
|||||||
use simconnect_sdk_derive::SimConnectObject;
|
use simconnect_sdk_derive::SimConnectObject;
|
||||||
#[derive(Debug, Clone, SimConnectObject)]
|
#[derive(Debug, Clone, SimConnectObject)]
|
||||||
#[simconnect(period = "second")]
|
#[simconnect(period = "second")]
|
||||||
struct GpsData1 {
|
struct Data1 {
|
||||||
#[simconnect(name = "PLANE LATITUDE", unit = "degrees")]
|
#[simconnect(name = "PLANE LATITUDE", unit = "degrees")]
|
||||||
pub lat: f64,
|
pub lat: f64,
|
||||||
#[simconnect(name = "PLANE LONGITUDE", unit = "degrees")]
|
#[simconnect(name = "PLANE LONGITUDE", unit = "degrees")]
|
||||||
@@ -12,7 +12,7 @@ struct GpsData1 {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, SimConnectObject)]
|
#[derive(Debug, Clone, SimConnectObject)]
|
||||||
#[simconnect(period = "second", condition = "none")]
|
#[simconnect(period = "second", condition = "none")]
|
||||||
struct GpsData2 {
|
struct Data2 {
|
||||||
#[simconnect(name = "PLANE LATITUDE", unit = "degrees")]
|
#[simconnect(name = "PLANE LATITUDE", unit = "degrees")]
|
||||||
pub lat: f64,
|
pub lat: f64,
|
||||||
#[simconnect(name = "PLANE LONGITUDE", unit = "degrees")]
|
#[simconnect(name = "PLANE LONGITUDE", unit = "degrees")]
|
||||||
@@ -21,7 +21,7 @@ struct GpsData2 {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, SimConnectObject)]
|
#[derive(Debug, Clone, SimConnectObject)]
|
||||||
#[simconnect(period = "visual-frame", condition = "changed")]
|
#[simconnect(period = "visual-frame", condition = "changed")]
|
||||||
struct GpsData3 {
|
struct Data3 {
|
||||||
#[simconnect(name = "PLANE LATITUDE", unit = "degrees")]
|
#[simconnect(name = "PLANE LATITUDE", unit = "degrees")]
|
||||||
pub lat: f64,
|
pub lat: f64,
|
||||||
#[simconnect(name = "PLANE LONGITUDE", unit = "degrees")]
|
#[simconnect(name = "PLANE LONGITUDE", unit = "degrees")]
|
||||||
@@ -30,7 +30,7 @@ struct GpsData3 {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, SimConnectObject)]
|
#[derive(Debug, Clone, SimConnectObject)]
|
||||||
#[simconnect(period = "visual-frame", condition = "changed", interval = 0)]
|
#[simconnect(period = "visual-frame", condition = "changed", interval = 0)]
|
||||||
struct GpsData4 {
|
struct Data4 {
|
||||||
#[simconnect(name = "PLANE LATITUDE", unit = "degrees")]
|
#[simconnect(name = "PLANE LATITUDE", unit = "degrees")]
|
||||||
pub lat: f64,
|
pub lat: f64,
|
||||||
#[simconnect(name = "PLANE LONGITUDE", unit = "degrees")]
|
#[simconnect(name = "PLANE LONGITUDE", unit = "degrees")]
|
||||||
|
@@ -3,45 +3,45 @@ use simconnect_sdk_derive::SimConnectObject;
|
|||||||
|
|
||||||
#[derive(Debug, Clone, SimConnectObject)]
|
#[derive(Debug, Clone, SimConnectObject)]
|
||||||
#[simconnect(period = "visual-frame", condition = "changed")]
|
#[simconnect(period = "visual-frame", condition = "changed")]
|
||||||
struct GpsData1(f64);
|
struct Data1(f64);
|
||||||
|
|
||||||
#[derive(Debug, Clone, SimConnectObject)]
|
#[derive(Debug, Clone, SimConnectObject)]
|
||||||
struct GpsData2 {}
|
struct Data2 {}
|
||||||
|
|
||||||
#[derive(Debug, Clone, SimConnectObject)]
|
#[derive(Debug, Clone, SimConnectObject)]
|
||||||
#[simconnect]
|
#[simconnect]
|
||||||
struct GpsData3 {}
|
struct Data3 {}
|
||||||
|
|
||||||
#[derive(Debug, Clone, SimConnectObject)]
|
#[derive(Debug, Clone, SimConnectObject)]
|
||||||
#[simconnect()]
|
#[simconnect()]
|
||||||
struct GpsData4 {}
|
struct Data4 {}
|
||||||
|
|
||||||
#[derive(Debug, Clone, SimConnectObject)]
|
#[derive(Debug, Clone, SimConnectObject)]
|
||||||
#[simconnect(period = "second", period = "second")]
|
#[simconnect(period = "second", period = "second")]
|
||||||
struct GpsData5 {}
|
struct Data5 {}
|
||||||
|
|
||||||
#[derive(Debug, Clone, SimConnectObject)]
|
#[derive(Debug, Clone, SimConnectObject)]
|
||||||
#[simconnect(period = "second", condition = "none", condition = "none")]
|
#[simconnect(period = "second", condition = "none", condition = "none")]
|
||||||
struct GpsData6 {}
|
struct Data6 {}
|
||||||
|
|
||||||
#[derive(Debug, Clone, SimConnectObject)]
|
#[derive(Debug, Clone, SimConnectObject)]
|
||||||
#[simconnect(period = "second", interval = 0, interval = 0)]
|
#[simconnect(period = "second", interval = 0, interval = 0)]
|
||||||
struct GpsData7 {}
|
struct Data7 {}
|
||||||
|
|
||||||
#[derive(Debug, Clone, SimConnectObject)]
|
#[derive(Debug, Clone, SimConnectObject)]
|
||||||
#[simconnect(period = "second", test = "test")]
|
#[simconnect(period = "second", test = "test")]
|
||||||
struct GpsData8 {}
|
struct Data8 {}
|
||||||
|
|
||||||
#[derive(Debug, Clone, SimConnectObject)]
|
#[derive(Debug, Clone, SimConnectObject)]
|
||||||
#[simconnect(periodX = "second", condition = "none")]
|
#[simconnect(periodX = "second", condition = "none")]
|
||||||
struct GpsData9 {}
|
struct Data9 {}
|
||||||
|
|
||||||
#[derive(Debug, Clone, SimConnectObject)]
|
#[derive(Debug, Clone, SimConnectObject)]
|
||||||
#[simconnect(period = "second", conditionX = "none")]
|
#[simconnect(period = "second", conditionX = "none")]
|
||||||
struct GpsData10 {}
|
struct Data10 {}
|
||||||
|
|
||||||
#[derive(Debug, Clone, SimConnectObject)]
|
#[derive(Debug, Clone, SimConnectObject)]
|
||||||
#[simconnect(period = "second", intervalX = 0)]
|
#[simconnect(period = "second", intervalX = 0)]
|
||||||
struct GpsData11 {}
|
struct Data11 {}
|
||||||
|
|
||||||
fn main() {}
|
fn main() {}
|
||||||
|
@@ -2,14 +2,14 @@ error: Unsupported field type. Only named fields are supported.
|
|||||||
--> tests/02-struct-attr-errors.rs:5:1
|
--> tests/02-struct-attr-errors.rs:5:1
|
||||||
|
|
|
|
||||||
5 | / #[simconnect(period = "visual-frame", condition = "changed")]
|
5 | / #[simconnect(period = "visual-frame", condition = "changed")]
|
||||||
6 | | struct GpsData1(f64);
|
6 | | struct Data1(f64);
|
||||||
| |_____________________^
|
| |__________________^
|
||||||
|
|
||||||
error: expected attribute `#[simconnect(period = "...", condition = "...", interval = ...)]`. `condition` and `interval` are optional.
|
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 Data2 {}
|
||||||
| ^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
error: expected attribute `#[simconnect(period = "...", condition = "...", interval = ...)]`. `condition` and `interval` are optional.
|
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
|
||||||
|
@@ -3,69 +3,62 @@ use simconnect_sdk_derive::SimConnectObject;
|
|||||||
|
|
||||||
#[derive(Debug, Clone, SimConnectObject)]
|
#[derive(Debug, Clone, SimConnectObject)]
|
||||||
#[simconnect(period = "second", condition = "none")]
|
#[simconnect(period = "second", condition = "none")]
|
||||||
struct GpsData1 {
|
struct Data1 {
|
||||||
pub lat: f64,
|
pub lat: f64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, SimConnectObject)]
|
#[derive(Debug, Clone, SimConnectObject)]
|
||||||
#[simconnect(period = "second", condition = "none")]
|
#[simconnect(period = "second", condition = "none")]
|
||||||
struct GpsData2 {
|
struct Data2 {
|
||||||
#[simconnect]
|
#[simconnect]
|
||||||
pub lat: f64,
|
pub lat: f64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, SimConnectObject)]
|
#[derive(Debug, Clone, SimConnectObject)]
|
||||||
#[simconnect(period = "second", condition = "none")]
|
#[simconnect(period = "second", condition = "none")]
|
||||||
struct GpsData3 {
|
struct Data3 {
|
||||||
#[simconnect()]
|
#[simconnect()]
|
||||||
pub lat: f64,
|
pub lat: f64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, SimConnectObject)]
|
#[derive(Debug, Clone, SimConnectObject)]
|
||||||
#[simconnect(period = "second", condition = "none")]
|
#[simconnect(period = "second", condition = "none")]
|
||||||
struct GpsData4 {
|
struct Data4 {
|
||||||
#[simconnect(name = "PLANE LATITUDE")]
|
|
||||||
pub lat: f64,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, SimConnectObject)]
|
|
||||||
#[simconnect(period = "second", condition = "none")]
|
|
||||||
struct GpsData5 {
|
|
||||||
#[simconnect(unit = "degrees")]
|
#[simconnect(unit = "degrees")]
|
||||||
pub lat: f64,
|
pub lat: f64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, SimConnectObject)]
|
#[derive(Debug, Clone, SimConnectObject)]
|
||||||
#[simconnect(period = "second", condition = "none")]
|
#[simconnect(period = "second", condition = "none")]
|
||||||
struct GpsData6 {
|
struct Data5 {
|
||||||
#[simconnect(name = "PLANE LATITUDE", name = "PLANE LATITUDE")]
|
#[simconnect(name = "PLANE LATITUDE", name = "PLANE LATITUDE")]
|
||||||
pub lat: f64,
|
pub lat: f64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, SimConnectObject)]
|
#[derive(Debug, Clone, SimConnectObject)]
|
||||||
#[simconnect(period = "second", condition = "none")]
|
#[simconnect(period = "second", condition = "none")]
|
||||||
struct GpsData7 {
|
struct Data6 {
|
||||||
#[simconnect(unit = "degrees", unit = "degrees")]
|
#[simconnect(unit = "degrees", unit = "degrees")]
|
||||||
pub lat: f64,
|
pub lat: f64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, SimConnectObject)]
|
#[derive(Debug, Clone, SimConnectObject)]
|
||||||
#[simconnect(period = "second", condition = "none")]
|
#[simconnect(period = "second", condition = "none")]
|
||||||
struct GpsData8 {
|
struct Data7 {
|
||||||
#[simconnect(name = "PLANE LATITUDE", unit = "degrees", unit = "degrees")]
|
#[simconnect(name = "PLANE LATITUDE", unit = "degrees", unit = "degrees")]
|
||||||
pub lat: f64,
|
pub lat: f64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, SimConnectObject)]
|
#[derive(Debug, Clone, SimConnectObject)]
|
||||||
#[simconnect(period = "second", condition = "none")]
|
#[simconnect(period = "second", condition = "none")]
|
||||||
struct GpsData9 {
|
struct Data8 {
|
||||||
#[simconnect(nameX = "PLANE LATITUDE", unit = "degrees")]
|
#[simconnect(nameX = "PLANE LATITUDE", unit = "degrees")]
|
||||||
pub lat: f64,
|
pub lat: f64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, SimConnectObject)]
|
#[derive(Debug, Clone, SimConnectObject)]
|
||||||
#[simconnect(period = "second", condition = "none")]
|
#[simconnect(period = "second", condition = "none")]
|
||||||
struct GpsData10 {
|
struct Data9 {
|
||||||
#[simconnect(name = "PLANE LATITUDE", unitX = "degrees")]
|
#[simconnect(name = "PLANE LATITUDE", unitX = "degrees")]
|
||||||
pub lat: f64,
|
pub lat: f64,
|
||||||
}
|
}
|
||||||
|
@@ -1,59 +1,53 @@
|
|||||||
error: expected attribute `#[simconnect(name = "...", unit = "...")]`
|
error: expected attribute `#[simconnect(name = "...", unit = "...")]`. `unit` is optional.
|
||||||
--> tests/03-field-attr-errors.rs:7:5
|
--> tests/03-field-attr-errors.rs:7:5
|
||||||
|
|
|
|
||||||
7 | pub lat: f64,
|
7 | pub lat: f64,
|
||||||
| ^^^^^^^^^^^^
|
| ^^^^^^^^^^^^
|
||||||
|
|
||||||
error: expected attribute `#[simconnect(name = "...", unit = "...")]`
|
error: expected attribute `#[simconnect(name = "...", unit = "...")]`. `unit` is optional.
|
||||||
--> tests/03-field-attr-errors.rs:13:7
|
--> tests/03-field-attr-errors.rs:13:7
|
||||||
|
|
|
|
||||||
13 | #[simconnect]
|
13 | #[simconnect]
|
||||||
| ^^^^^^^^^^
|
| ^^^^^^^^^^
|
||||||
|
|
||||||
error: expected attribute `#[simconnect(name = "...", unit = "...")]`
|
error: expected attribute `#[simconnect(name = "...", unit = "...")]`. `unit` is optional.
|
||||||
--> tests/03-field-attr-errors.rs:20:7
|
--> tests/03-field-attr-errors.rs:20:7
|
||||||
|
|
|
|
||||||
20 | #[simconnect()]
|
20 | #[simconnect()]
|
||||||
| ^^^^^^^^^^^^
|
| ^^^^^^^^^^^^
|
||||||
|
|
||||||
error: expected attribute `#[simconnect(name = "...", unit = "...")]`
|
error: expected attribute `#[simconnect(name = "...", unit = "...")]`. `unit` is optional.
|
||||||
--> tests/03-field-attr-errors.rs:27:7
|
--> tests/03-field-attr-errors.rs:27:7
|
||||||
|
|
|
|
||||||
27 | #[simconnect(name = "PLANE LATITUDE")]
|
27 | #[simconnect(unit = "degrees")]
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
error: expected attribute `#[simconnect(name = "...", unit = "...")]`
|
|
||||||
--> tests/03-field-attr-errors.rs:34:7
|
|
||||||
|
|
|
||||||
34 | #[simconnect(unit = "degrees")]
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
error: expected attribute `#[simconnect(name = "...", unit = "...")]`
|
error: expected attribute `#[simconnect(name = "...", unit = "...")]`. `unit` is optional.
|
||||||
--> tests/03-field-attr-errors.rs:41:7
|
--> tests/03-field-attr-errors.rs:34:7
|
||||||
|
|
|
|
||||||
41 | #[simconnect(name = "PLANE LATITUDE", name = "PLANE LATITUDE")]
|
34 | #[simconnect(name = "PLANE LATITUDE", name = "PLANE LATITUDE")]
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
error: expected attribute `#[simconnect(name = "...", unit = "...")]`
|
error: expected attribute `#[simconnect(name = "...", unit = "...")]`. `unit` is optional.
|
||||||
--> tests/03-field-attr-errors.rs:48:7
|
--> tests/03-field-attr-errors.rs:41:7
|
||||||
|
|
|
|
||||||
48 | #[simconnect(unit = "degrees", unit = "degrees")]
|
41 | #[simconnect(unit = "degrees", unit = "degrees")]
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
error: expected attribute `#[simconnect(name = "...", unit = "...")]`
|
error: expected attribute `#[simconnect(name = "...", unit = "...")]`. `unit` is optional.
|
||||||
--> tests/03-field-attr-errors.rs:55:7
|
--> tests/03-field-attr-errors.rs:48:7
|
||||||
|
|
|
|
||||||
55 | #[simconnect(name = "PLANE LATITUDE", unit = "degrees", unit = "degrees")]
|
48 | #[simconnect(name = "PLANE LATITUDE", unit = "degrees", unit = "degrees")]
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
error: expected attribute `#[simconnect(name = "...", unit = "...")]`
|
error: expected attribute `#[simconnect(name = "...", unit = "...")]`. `unit` is optional.
|
||||||
--> tests/03-field-attr-errors.rs:62:7
|
--> tests/03-field-attr-errors.rs:55:7
|
||||||
|
|
|
|
||||||
62 | #[simconnect(nameX = "PLANE LATITUDE", unit = "degrees")]
|
55 | #[simconnect(nameX = "PLANE LATITUDE", unit = "degrees")]
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
error: expected attribute `#[simconnect(name = "...", unit = "...")]`
|
error: expected attribute `#[simconnect(name = "...", unit = "...")]`. `unit` is optional.
|
||||||
--> tests/03-field-attr-errors.rs:69:7
|
--> tests/03-field-attr-errors.rs:62:7
|
||||||
|
|
|
|
||||||
69 | #[simconnect(name = "PLANE LATITUDE", unitX = "degrees")]
|
62 | #[simconnect(name = "PLANE LATITUDE", unitX = "degrees")]
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
@@ -3,33 +3,33 @@ use simconnect_sdk_derive::SimConnectObject;
|
|||||||
|
|
||||||
#[derive(Debug, Clone, SimConnectObject)]
|
#[derive(Debug, Clone, SimConnectObject)]
|
||||||
#[simconnect(period = 123, condition = "none")]
|
#[simconnect(period = 123, condition = "none")]
|
||||||
struct GpsData1 {}
|
struct Data1 {}
|
||||||
|
|
||||||
#[derive(Debug, Clone, SimConnectObject)]
|
#[derive(Debug, Clone, SimConnectObject)]
|
||||||
#[simconnect(period = "second", condition = 123)]
|
#[simconnect(period = "second", condition = 123)]
|
||||||
struct GpsData2 {}
|
struct Data2 {}
|
||||||
|
|
||||||
#[derive(Debug, Clone, SimConnectObject)]
|
#[derive(Debug, Clone, SimConnectObject)]
|
||||||
#[simconnect(period = "X")]
|
#[simconnect(period = "X")]
|
||||||
struct GpsData3 {}
|
struct Data3 {}
|
||||||
|
|
||||||
#[derive(Debug, Clone, SimConnectObject)]
|
#[derive(Debug, Clone, SimConnectObject)]
|
||||||
#[simconnect(period = "second", condition = "X")]
|
#[simconnect(period = "second", condition = "X")]
|
||||||
struct GpsData4 {}
|
struct Data4 {}
|
||||||
|
|
||||||
#[derive(Debug, Clone, SimConnectObject)]
|
#[derive(Debug, Clone, SimConnectObject)]
|
||||||
#[simconnect(period = "second", interval = "X")]
|
#[simconnect(period = "second", interval = "X")]
|
||||||
struct GpsData5 {}
|
struct Data5 {}
|
||||||
|
|
||||||
#[derive(Debug, Clone, SimConnectObject)]
|
#[derive(Debug, Clone, SimConnectObject)]
|
||||||
#[simconnect(period = "second", interval = 0.0)]
|
#[simconnect(period = "second", interval = 0.0)]
|
||||||
struct GpsData6 {}
|
struct Data6 {}
|
||||||
|
|
||||||
#[derive(Debug, Clone, SimConnectObject)]
|
#[derive(Debug, Clone, SimConnectObject)]
|
||||||
#[simconnect(period = "second", condition = "none")]
|
#[simconnect(period = "second", condition = "none")]
|
||||||
struct GpsData7 {
|
struct Data7 {
|
||||||
#[simconnect(name = "PLANE LATITUDE", unit = "degrees")]
|
#[simconnect(name = "PLANE LATITUDE", unit = "degrees")]
|
||||||
pub lat: String,
|
pub lat: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {}
|
fn main() {}
|
||||||
|
@@ -10,13 +10,13 @@ error: Expected Str, found Int(LitInt { token: 123 })
|
|||||||
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")]
|
||||||
@@ -34,9 +34,8 @@ error: Expected Int, found Float(LitFloat { token: 0.0 })
|
|||||||
25 | #[simconnect(period = "second", interval = 0.0)]
|
25 | #[simconnect(period = "second", interval = 0.0)]
|
||||||
| ^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^
|
||||||
|
|
||||||
error: Field type must be one of ["f64", "bool"]
|
error: Field type must be one of ["f64", "bool", "String"].
|
||||||
--> tests/04-invalid-values.rs:31:5
|
--> tests/04-invalid-values.rs:32:14
|
||||||
|
|
|
|
||||||
31 | / #[simconnect(name = "PLANE LATITUDE", unit = "degrees")]
|
32 | pub lat: u64,
|
||||||
32 | | pub lat: String,
|
| ^^^
|
||||||
| |___________________^
|
|
||||||
|
@@ -44,6 +44,7 @@ fn main() {
|
|||||||
.allowlist_var("SIMCONNECT_RECV_ID_VOR_LIST_HAS_LOCALIZER")
|
.allowlist_var("SIMCONNECT_RECV_ID_VOR_LIST_HAS_LOCALIZER")
|
||||||
.allowlist_var("SIMCONNECT_RECV_ID_VOR_LIST_HAS_GLIDE_SLOPE")
|
.allowlist_var("SIMCONNECT_RECV_ID_VOR_LIST_HAS_GLIDE_SLOPE")
|
||||||
.allowlist_var("SIMCONNECT_RECV_ID_VOR_LIST_HAS_DME")
|
.allowlist_var("SIMCONNECT_RECV_ID_VOR_LIST_HAS_DME")
|
||||||
|
.allowlist_var("SIMCONNECT_OBJECT_ID_USER")
|
||||||
.generate()
|
.generate()
|
||||||
.expect("Unable to generate bindings");
|
.expect("Unable to generate bindings");
|
||||||
|
|
||||||
|
@@ -3,4 +3,5 @@
|
|||||||
pub enum DataType {
|
pub enum DataType {
|
||||||
Float64,
|
Float64,
|
||||||
Bool,
|
Bool,
|
||||||
|
String,
|
||||||
}
|
}
|
||||||
|
@@ -35,12 +35,12 @@ impl Object {
|
|||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
/// - [`crate::SimConnectError::ObjectMismatch`] -- The type of this SimConnect object is different from `T`.
|
/// - [`crate::SimConnectError::ObjectMismatch`] -- The type of this SimConnect object is different from `T`.
|
||||||
pub fn try_transmute<T: SimConnectObjectExt>(&self) -> Result<T, SimConnectError> {
|
pub fn try_transmute<T: SimConnectObjectExt, I>(&self) -> Result<I, SimConnectError> {
|
||||||
let type_name: String = std::any::type_name::<T>().into();
|
let type_name: String = std::any::type_name::<T>().into();
|
||||||
|
|
||||||
if self.type_name == type_name {
|
if self.type_name == type_name {
|
||||||
let data: &T = unsafe { std::mem::transmute_copy(&self.data_addr) };
|
let data: I = unsafe { std::ptr::read_unaligned(self.data_addr as *const I) };
|
||||||
Ok(data.clone())
|
Ok(data)
|
||||||
} else {
|
} else {
|
||||||
Err(SimConnectError::ObjectMismatch {
|
Err(SimConnectError::ObjectMismatch {
|
||||||
actual: self.type_name.clone(),
|
actual: self.type_name.clone(),
|
||||||
|
@@ -26,6 +26,7 @@ pub(crate) use macros::{as_c_string, ok_if_fail, success};
|
|||||||
|
|
||||||
pub use domain::*;
|
pub use domain::*;
|
||||||
pub use errors::SimConnectError;
|
pub use errors::SimConnectError;
|
||||||
|
pub use helpers::fixed_c_str_to_string;
|
||||||
pub use simconnect::SimConnect;
|
pub use simconnect::SimConnect;
|
||||||
pub use simconnect_object_ext::SimConnectObjectExt;
|
pub use simconnect_object_ext::SimConnectObjectExt;
|
||||||
|
|
||||||
|
@@ -68,21 +68,23 @@ impl SimConnect {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
let result = match unsafe { (*data_buf).dwID as i32 } {
|
let recv_id = unsafe { (*data_buf).dwID as i32 };
|
||||||
|
|
||||||
|
let result = match recv_id {
|
||||||
bindings::SIMCONNECT_RECV_ID_SIMCONNECT_RECV_ID_OPEN => Some(Notification::Open),
|
bindings::SIMCONNECT_RECV_ID_SIMCONNECT_RECV_ID_OPEN => Some(Notification::Open),
|
||||||
bindings::SIMCONNECT_RECV_ID_SIMCONNECT_RECV_ID_QUIT => Some(Notification::Quit),
|
bindings::SIMCONNECT_RECV_ID_SIMCONNECT_RECV_ID_QUIT => Some(Notification::Quit),
|
||||||
bindings::SIMCONNECT_RECV_ID_SIMCONNECT_RECV_ID_EVENT => {
|
bindings::SIMCONNECT_RECV_ID_SIMCONNECT_RECV_ID_EVENT => {
|
||||||
let event = unsafe { *(data_buf as *const bindings::SIMCONNECT_RECV_EVENT) };
|
let event: &bindings::SIMCONNECT_RECV_EVENT =
|
||||||
|
unsafe { &*(data_buf as *const bindings::SIMCONNECT_RECV_EVENT) };
|
||||||
|
|
||||||
let event = Event::try_from(event.uEventID)
|
let event = Event::try_from(event.uEventID)
|
||||||
.map_err(|_| SimConnectError::SimConnectUnrecognizedEvent(event.uEventID))?;
|
.map_err(|_| SimConnectError::SimConnectUnrecognizedEvent(event.uEventID))?;
|
||||||
|
|
||||||
Some(Notification::Event(event))
|
Some(Notification::Event(event))
|
||||||
}
|
}
|
||||||
bindings::SIMCONNECT_RECV_ID_SIMCONNECT_RECV_ID_SIMOBJECT_DATA => {
|
bindings::SIMCONNECT_RECV_ID_SIMCONNECT_RECV_ID_SIMOBJECT_DATA => {
|
||||||
let event: &bindings::SIMCONNECT_RECV_SIMOBJECT_DATA = unsafe {
|
let event: &bindings::SIMCONNECT_RECV_SIMOBJECT_DATA =
|
||||||
std::mem::transmute_copy(
|
unsafe { &*(data_buf as *const bindings::SIMCONNECT_RECV_SIMOBJECT_DATA) };
|
||||||
&(data_buf as *const bindings::SIMCONNECT_RECV_SIMOBJECT_DATA),
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
let type_name = self.get_type_name_by_request_id(event.dwDefineID);
|
let type_name = self.get_type_name_by_request_id(event.dwDefineID);
|
||||||
|
|
||||||
@@ -99,11 +101,8 @@ impl SimConnect {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
bindings::SIMCONNECT_RECV_ID_SIMCONNECT_RECV_ID_AIRPORT_LIST => {
|
bindings::SIMCONNECT_RECV_ID_SIMCONNECT_RECV_ID_AIRPORT_LIST => {
|
||||||
let event: &bindings::SIMCONNECT_RECV_AIRPORT_LIST = unsafe {
|
let event: &bindings::SIMCONNECT_RECV_AIRPORT_LIST =
|
||||||
std::mem::transmute_copy(
|
unsafe { &*(data_buf as *const bindings::SIMCONNECT_RECV_AIRPORT_LIST) };
|
||||||
&(data_buf as *const bindings::SIMCONNECT_RECV_AIRPORT_LIST),
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
let data = (0..event._base.dwArraySize as usize)
|
let data = (0..event._base.dwArraySize as usize)
|
||||||
.map(|i| {
|
.map(|i| {
|
||||||
@@ -122,11 +121,8 @@ impl SimConnect {
|
|||||||
Some(Notification::AirportList(data))
|
Some(Notification::AirportList(data))
|
||||||
}
|
}
|
||||||
bindings::SIMCONNECT_RECV_ID_SIMCONNECT_RECV_ID_WAYPOINT_LIST => {
|
bindings::SIMCONNECT_RECV_ID_SIMCONNECT_RECV_ID_WAYPOINT_LIST => {
|
||||||
let event: &bindings::SIMCONNECT_RECV_WAYPOINT_LIST = unsafe {
|
let event: &bindings::SIMCONNECT_RECV_WAYPOINT_LIST =
|
||||||
std::mem::transmute_copy(
|
unsafe { &*(data_buf as *const bindings::SIMCONNECT_RECV_WAYPOINT_LIST) };
|
||||||
&(data_buf as *const bindings::SIMCONNECT_RECV_WAYPOINT_LIST),
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
let data = (0..event._base.dwArraySize as usize)
|
let data = (0..event._base.dwArraySize as usize)
|
||||||
.map(|i| {
|
.map(|i| {
|
||||||
@@ -146,11 +142,8 @@ impl SimConnect {
|
|||||||
Some(Notification::WaypointList(data))
|
Some(Notification::WaypointList(data))
|
||||||
}
|
}
|
||||||
bindings::SIMCONNECT_RECV_ID_SIMCONNECT_RECV_ID_NDB_LIST => {
|
bindings::SIMCONNECT_RECV_ID_SIMCONNECT_RECV_ID_NDB_LIST => {
|
||||||
let event: &bindings::SIMCONNECT_RECV_NDB_LIST = unsafe {
|
let event: &bindings::SIMCONNECT_RECV_NDB_LIST =
|
||||||
std::mem::transmute_copy(
|
unsafe { &*(data_buf as *const bindings::SIMCONNECT_RECV_NDB_LIST) };
|
||||||
&(data_buf as *const bindings::SIMCONNECT_RECV_NDB_LIST),
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
let data = (0..event._base.dwArraySize as usize)
|
let data = (0..event._base.dwArraySize as usize)
|
||||||
.map(|i| {
|
.map(|i| {
|
||||||
@@ -171,11 +164,8 @@ impl SimConnect {
|
|||||||
Some(Notification::NdbList(data))
|
Some(Notification::NdbList(data))
|
||||||
}
|
}
|
||||||
bindings::SIMCONNECT_RECV_ID_SIMCONNECT_RECV_ID_VOR_LIST => {
|
bindings::SIMCONNECT_RECV_ID_SIMCONNECT_RECV_ID_VOR_LIST => {
|
||||||
let event: &bindings::SIMCONNECT_RECV_VOR_LIST = unsafe {
|
let event: &bindings::SIMCONNECT_RECV_VOR_LIST =
|
||||||
std::mem::transmute_copy(
|
unsafe { &*(data_buf as *const bindings::SIMCONNECT_RECV_VOR_LIST) };
|
||||||
&(data_buf as *const bindings::SIMCONNECT_RECV_VOR_LIST),
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
let data = (0..event._base.dwArraySize as usize)
|
let data = (0..event._base.dwArraySize as usize)
|
||||||
.map(|i| {
|
.map(|i| {
|
||||||
@@ -241,13 +231,12 @@ impl SimConnect {
|
|||||||
Some(Notification::VorList(data))
|
Some(Notification::VorList(data))
|
||||||
}
|
}
|
||||||
bindings::SIMCONNECT_RECV_ID_SIMCONNECT_RECV_ID_EXCEPTION => {
|
bindings::SIMCONNECT_RECV_ID_SIMCONNECT_RECV_ID_EXCEPTION => {
|
||||||
let event = unsafe { *(data_buf as *const bindings::SIMCONNECT_RECV_EXCEPTION) };
|
let event: &bindings::SIMCONNECT_RECV_EXCEPTION =
|
||||||
|
unsafe { &*(data_buf as *const bindings::SIMCONNECT_RECV_EXCEPTION) };
|
||||||
Some(Notification::Exception(event.dwException))
|
Some(Notification::Exception(event.dwException))
|
||||||
}
|
}
|
||||||
bindings::SIMCONNECT_RECV_ID_SIMCONNECT_RECV_ID_NULL => None,
|
bindings::SIMCONNECT_RECV_ID_SIMCONNECT_RECV_ID_NULL => None,
|
||||||
_ => panic!("Got unrecognized notification: {}", unsafe {
|
id => panic!("Got unrecognized notification: {id}"),
|
||||||
(*data_buf).dwID as i32
|
|
||||||
}),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(result)
|
Ok(result)
|
||||||
|
@@ -45,21 +45,22 @@ impl SimConnect {
|
|||||||
pub fn add_to_data_definition(
|
pub fn add_to_data_definition(
|
||||||
&self,
|
&self,
|
||||||
request_id: u32,
|
request_id: u32,
|
||||||
datum_name: &str,
|
name: &str,
|
||||||
units_name: &str,
|
unit: &str,
|
||||||
data_type: DataType,
|
data_type: DataType,
|
||||||
) -> Result<(), SimConnectError> {
|
) -> Result<(), SimConnectError> {
|
||||||
let c_type = match data_type {
|
let c_type = match data_type {
|
||||||
DataType::Float64 => bindings::SIMCONNECT_DATATYPE_SIMCONNECT_DATATYPE_FLOAT64,
|
DataType::Float64 => bindings::SIMCONNECT_DATATYPE_SIMCONNECT_DATATYPE_FLOAT64,
|
||||||
DataType::Bool => bindings::SIMCONNECT_DATATYPE_SIMCONNECT_DATATYPE_INT32,
|
DataType::Bool => bindings::SIMCONNECT_DATATYPE_SIMCONNECT_DATATYPE_INT32,
|
||||||
|
DataType::String => bindings::SIMCONNECT_DATATYPE_SIMCONNECT_DATATYPE_STRING256,
|
||||||
};
|
};
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
success!(bindings::SimConnect_AddToDataDefinition(
|
success!(bindings::SimConnect_AddToDataDefinition(
|
||||||
self.handle.as_ptr(),
|
self.handle.as_ptr(),
|
||||||
request_id,
|
request_id,
|
||||||
as_c_string!(datum_name),
|
as_c_string!(name),
|
||||||
as_c_string!(units_name),
|
as_c_string!(unit),
|
||||||
c_type,
|
c_type,
|
||||||
0.0,
|
0.0,
|
||||||
u32::MAX,
|
u32::MAX,
|
||||||
@@ -71,7 +72,12 @@ 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.
|
||||||
///
|
///
|
||||||
|
/// # Current limitation
|
||||||
|
/// All objects are requested from the local user's aircraft POV.
|
||||||
|
/// This comes with the side-effect that currently there is no way to request data for other aircraft in multiplayer.
|
||||||
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
|
/// * `request_id` - The request ID of the object.
|
||||||
/// * `period` - [`crate::Period`]
|
/// * `period` - [`crate::Period`]
|
||||||
/// * `condition` - [`crate::Condition`]
|
/// * `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.
|
/// * `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.
|
||||||
@@ -91,7 +97,7 @@ impl SimConnect {
|
|||||||
self.handle.as_ptr(),
|
self.handle.as_ptr(),
|
||||||
request_id,
|
request_id,
|
||||||
request_id,
|
request_id,
|
||||||
request_id,
|
bindings::SIMCONNECT_OBJECT_ID_USER,
|
||||||
period.into(),
|
period.into(),
|
||||||
condition.into(),
|
condition.into(),
|
||||||
0,
|
0,
|
||||||
|
Reference in New Issue
Block a user