Add support for String

This commit is contained in:
Mihai Dinculescu
2022-10-20 15:42:51 +01:00
parent 60b73c1557
commit 4ac94cdb96
21 changed files with 574 additions and 404 deletions

View 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)),
}
}

View 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()
}

View File

@@ -1,82 +1,15 @@
extern crate proc_macro;
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 quote::quote;
use syn::{parse_macro_input, DeriveInput};
#[derive(Debug, PartialEq, Eq)]
enum FieldType {
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"];
mod fields;
mod helpers;
/// SimConnectObject derive macro.
///
@@ -87,7 +20,7 @@ const SUPPORTED_FIELD_TYPES: [&str; 2] = ["f64", "bool"];
///
/// # Field Arguments
/// * `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
///
@@ -96,20 +29,27 @@ const SUPPORTED_FIELD_TYPES: [&str; 2] = ["f64", "bool"];
///
/// #[derive(Debug, Clone, SimConnectObject)]
/// #[simconnect(period = "second")]
/// struct GpsData {
/// struct AirplaneData {
/// #[simconnect(name = "TITLE")]
/// title: String,
/// #[simconnect(name = "CATEGORY")]
/// category: String,
/// #[simconnect(name = "PLANE LATITUDE", unit = "degrees")]
/// lat: f64,
/// #[simconnect(name = "PLANE LONGITUDE", unit = "degrees")]
/// lon: f64,
/// #[simconnect(name = "PLANE LONGITUDE", unit = "degrees")]
/// #[simconnect(name = "PLANE ALTITUDE", unit = "feet")]
/// alt: f64,
/// #[simconnect(name = "SIM ON GROUND", unit = "bool")]
/// sim_on_ground: bool,
/// }
/// ```
#[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 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 {
fields: syn::Fields::Named(syn::FieldsNamed { ref named, .. }),
@@ -125,24 +65,53 @@ pub fn derive(input: TokenStream) -> TokenStream {
.into();
};
let build_fields = fields.iter().map(parse_field);
let request_data = request_data(&ast);
// parse the fields and their attributes
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! {
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> {
#(#build_fields)*
#request_data
#(#sc_definition)*
#sc_request
Ok(())
}
}
impl TryFrom<&simconnect_sdk::Object> for #name {
impl TryFrom<&simconnect_sdk::Object> for #name_ident {
type Error = simconnect_sdk::SimConnectError;
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,64 +119,92 @@ pub fn derive(input: TokenStream) -> TokenStream {
expanded.into()
}
fn parse_field(f: &syn::Field) -> proc_macro2::TokenStream {
let error_message = "expected attribute `#[simconnect(name = \"...\", unit = \"...\")]`";
fn build_packed_field(ident: &proc_macro2::Ident, path: &syn::Path) -> proc_macro2::TokenStream {
let path_segments = &path.segments;
let path_idents = path_segments.iter().map(|s| &s.ident);
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!(
r#"Field type must be one of ["{}"]"#,
SUPPORTED_FIELD_TYPES.join(r#"", ""#)
);
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,
match path_idents.last() {
Some(value) if value == "String" => {
quote! {
#ident: [std::primitive::i8; 256]
}
}
_ => {
quote! {
#ident: #path
}
}
None => mk_err(f, error_message),
}
}
fn request_data(ast: &DeriveInput) -> proc_macro2::TokenStream {
fn build_packed_field_assignment(
ident: &proc_macro2::Ident,
path: &syn::Path,
) -> proc_macro2::TokenStream {
let path_segments = &path.segments;
let path_idents = path_segments.iter().map(|s| &s.ident);
match path_idents.last() {
Some(value) if value == "String" => {
quote! {
#ident: simconnect_sdk::fixed_c_str_to_string(&raw.#ident)
}
}
_ => {
quote! {
#ident: raw.#ident
}
}
}
}
fn build_sc_definition(
path: &syn::Path,
properties: &HashMap<String, String>,
) -> 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 unit = match properties.get("unit") {
Some(unit) => unit,
None => "",
};
match path_idents.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)?;
}
}
Some(value) if value == "String" => {
quote! {
client.add_to_data_definition(id, #name, #unit, simconnect_sdk::DataType::String)?;
}
}
_ => {
// this error is already caught in `parse_field_attributes`
mk_err(path, error_message)
}
}
}
fn build_sc_request(ast: &DeriveInput) -> proc_macro2::TokenStream {
let attr = get_attribute(&ast.attrs);
let error_message = "expected attribute `#[simconnect(period = \"...\", condition = \"...\", interval = ...)]`. `condition` and `interval` are optional.";
match attr {
Some(attr) => {
let properties =
extract_attribute_string_properties(attr, &ALLOWED_CLASS_ATTRIBUTES, error_message);
extract_attribute_properties(attr, &ALLOWED_CLASS_ATTRIBUTES, error_message);
match properties {
Ok(properties) => {
@@ -262,135 +259,3 @@ fn request_data(ast: &DeriveInput) -> proc_macro2::TokenStream {
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()
}

View File

@@ -3,7 +3,7 @@
use simconnect_sdk_derive::SimConnectObject;
#[derive(Debug, Clone, SimConnectObject)]
#[simconnect(period = "second")]
struct GpsData1 {
struct Data1 {
#[simconnect(name = "PLANE LATITUDE", unit = "degrees")]
pub lat: f64,
#[simconnect(name = "PLANE LONGITUDE", unit = "degrees")]
@@ -12,7 +12,7 @@ struct GpsData1 {
#[derive(Debug, Clone, SimConnectObject)]
#[simconnect(period = "second", condition = "none")]
struct GpsData2 {
struct Data2 {
#[simconnect(name = "PLANE LATITUDE", unit = "degrees")]
pub lat: f64,
#[simconnect(name = "PLANE LONGITUDE", unit = "degrees")]
@@ -21,7 +21,7 @@ struct GpsData2 {
#[derive(Debug, Clone, SimConnectObject)]
#[simconnect(period = "visual-frame", condition = "changed")]
struct GpsData3 {
struct Data3 {
#[simconnect(name = "PLANE LATITUDE", unit = "degrees")]
pub lat: f64,
#[simconnect(name = "PLANE LONGITUDE", unit = "degrees")]
@@ -30,7 +30,7 @@ struct GpsData3 {
#[derive(Debug, Clone, SimConnectObject)]
#[simconnect(period = "visual-frame", condition = "changed", interval = 0)]
struct GpsData4 {
struct Data4 {
#[simconnect(name = "PLANE LATITUDE", unit = "degrees")]
pub lat: f64,
#[simconnect(name = "PLANE LONGITUDE", unit = "degrees")]

View File

@@ -3,45 +3,45 @@ use simconnect_sdk_derive::SimConnectObject;
#[derive(Debug, Clone, SimConnectObject)]
#[simconnect(period = "visual-frame", condition = "changed")]
struct GpsData1(f64);
struct Data1(f64);
#[derive(Debug, Clone, SimConnectObject)]
struct GpsData2 {}
struct Data2 {}
#[derive(Debug, Clone, SimConnectObject)]
#[simconnect]
struct GpsData3 {}
struct Data3 {}
#[derive(Debug, Clone, SimConnectObject)]
#[simconnect()]
struct GpsData4 {}
struct Data4 {}
#[derive(Debug, Clone, SimConnectObject)]
#[simconnect(period = "second", period = "second")]
struct GpsData5 {}
struct Data5 {}
#[derive(Debug, Clone, SimConnectObject)]
#[simconnect(period = "second", condition = "none", condition = "none")]
struct GpsData6 {}
struct Data6 {}
#[derive(Debug, Clone, SimConnectObject)]
#[simconnect(period = "second", interval = 0, interval = 0)]
struct GpsData7 {}
struct Data7 {}
#[derive(Debug, Clone, SimConnectObject)]
#[simconnect(period = "second", test = "test")]
struct GpsData8 {}
struct Data8 {}
#[derive(Debug, Clone, SimConnectObject)]
#[simconnect(periodX = "second", condition = "none")]
struct GpsData9 {}
struct Data9 {}
#[derive(Debug, Clone, SimConnectObject)]
#[simconnect(period = "second", conditionX = "none")]
struct GpsData10 {}
struct Data10 {}
#[derive(Debug, Clone, SimConnectObject)]
#[simconnect(period = "second", intervalX = 0)]
struct GpsData11 {}
struct Data11 {}
fn main() {}

View File

@@ -2,14 +2,14 @@ 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);
| |_____________________^
6 | | struct Data1(f64);
| |__________________^
error: expected attribute `#[simconnect(period = "...", condition = "...", interval = ...)]`. `condition` and `interval` are optional.
--> 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.
--> tests/02-struct-attr-errors.rs:12:3

View File

@@ -3,69 +3,62 @@ use simconnect_sdk_derive::SimConnectObject;
#[derive(Debug, Clone, SimConnectObject)]
#[simconnect(period = "second", condition = "none")]
struct GpsData1 {
struct Data1 {
pub lat: f64,
}
#[derive(Debug, Clone, SimConnectObject)]
#[simconnect(period = "second", condition = "none")]
struct GpsData2 {
struct Data2 {
#[simconnect]
pub lat: f64,
}
#[derive(Debug, Clone, SimConnectObject)]
#[simconnect(period = "second", condition = "none")]
struct GpsData3 {
struct Data3 {
#[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 {
struct Data4 {
#[simconnect(unit = "degrees")]
pub lat: f64,
}
#[derive(Debug, Clone, SimConnectObject)]
#[simconnect(period = "second", condition = "none")]
struct GpsData6 {
struct Data5 {
#[simconnect(name = "PLANE LATITUDE", name = "PLANE LATITUDE")]
pub lat: f64,
}
#[derive(Debug, Clone, SimConnectObject)]
#[simconnect(period = "second", condition = "none")]
struct GpsData7 {
struct Data6 {
#[simconnect(unit = "degrees", unit = "degrees")]
pub lat: f64,
}
#[derive(Debug, Clone, SimConnectObject)]
#[simconnect(period = "second", condition = "none")]
struct GpsData8 {
struct Data7 {
#[simconnect(name = "PLANE LATITUDE", unit = "degrees", unit = "degrees")]
pub lat: f64,
}
#[derive(Debug, Clone, SimConnectObject)]
#[simconnect(period = "second", condition = "none")]
struct GpsData9 {
struct Data8 {
#[simconnect(nameX = "PLANE LATITUDE", unit = "degrees")]
pub lat: f64,
}
#[derive(Debug, Clone, SimConnectObject)]
#[simconnect(period = "second", condition = "none")]
struct GpsData10 {
struct Data9 {
#[simconnect(name = "PLANE LATITUDE", unitX = "degrees")]
pub lat: f64,
}

View File

@@ -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
|
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
|
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
|
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
|
27 | #[simconnect(name = "PLANE LATITUDE")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: expected attribute `#[simconnect(name = "...", unit = "...")]`
--> tests/03-field-attr-errors.rs:34:7
|
34 | #[simconnect(unit = "degrees")]
27 | #[simconnect(unit = "degrees")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: expected attribute `#[simconnect(name = "...", unit = "...")]`
--> tests/03-field-attr-errors.rs:41:7
error: expected attribute `#[simconnect(name = "...", unit = "...")]`. `unit` is optional.
--> 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 = "...")]`
--> tests/03-field-attr-errors.rs:48:7
error: expected attribute `#[simconnect(name = "...", unit = "...")]`. `unit` is optional.
--> 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 = "...")]`
--> tests/03-field-attr-errors.rs:55:7
error: expected attribute `#[simconnect(name = "...", unit = "...")]`. `unit` is optional.
--> 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 = "...")]`
--> tests/03-field-attr-errors.rs:62:7
error: expected attribute `#[simconnect(name = "...", unit = "...")]`. `unit` is optional.
--> 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 = "...")]`
--> tests/03-field-attr-errors.rs:69:7
error: expected attribute `#[simconnect(name = "...", unit = "...")]`. `unit` is optional.
--> tests/03-field-attr-errors.rs:62:7
|
69 | #[simconnect(name = "PLANE LATITUDE", unitX = "degrees")]
62 | #[simconnect(name = "PLANE LATITUDE", unitX = "degrees")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

View File

@@ -3,33 +3,33 @@ use simconnect_sdk_derive::SimConnectObject;
#[derive(Debug, Clone, SimConnectObject)]
#[simconnect(period = 123, condition = "none")]
struct GpsData1 {}
struct Data1 {}
#[derive(Debug, Clone, SimConnectObject)]
#[simconnect(period = "second", condition = 123)]
struct GpsData2 {}
struct Data2 {}
#[derive(Debug, Clone, SimConnectObject)]
#[simconnect(period = "X")]
struct GpsData3 {}
struct Data3 {}
#[derive(Debug, Clone, SimConnectObject)]
#[simconnect(period = "second", condition = "X")]
struct GpsData4 {}
struct Data4 {}
#[derive(Debug, Clone, SimConnectObject)]
#[simconnect(period = "second", interval = "X")]
struct GpsData5 {}
struct Data5 {}
#[derive(Debug, Clone, SimConnectObject)]
#[simconnect(period = "second", interval = 0.0)]
struct GpsData6 {}
struct Data6 {}
#[derive(Debug, Clone, SimConnectObject)]
#[simconnect(period = "second", condition = "none")]
struct GpsData7 {
struct Data7 {
#[simconnect(name = "PLANE LATITUDE", unit = "degrees")]
pub lat: String,
pub lat: u64,
}
fn main() {}

View File

@@ -10,13 +10,13 @@ error: Expected Str, found Int(LitInt { token: 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
|
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
|
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)]
| ^^^^^^^^^^^^^^
error: Field type must be one of ["f64", "bool"]
--> tests/04-invalid-values.rs:31:5
error: Field type must be one of ["f64", "bool", "String"].
--> tests/04-invalid-values.rs:32:14
|
31 | / #[simconnect(name = "PLANE LATITUDE", unit = "degrees")]
32 | | pub lat: String,
| |___________________^
32 | pub lat: u64,
| ^^^