Add the derive proc macro
This commit is contained in:
@@ -5,11 +5,22 @@ authors = ["Mihai Dinculescu <mihai.dinculescu@outlook.com>"]
|
||||
edition = "2021"
|
||||
description = "Macros of SimConnect SDK for Rust"
|
||||
license = "MIT"
|
||||
autotests = false
|
||||
publish = false
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[[test]]
|
||||
name = "tests"
|
||||
path = "tests/run.rs"
|
||||
|
||||
[dependencies]
|
||||
proc-macro2 = "1.0"
|
||||
quote = "1.0"
|
||||
syn = "1.0"
|
||||
once_cell = "1.7"
|
||||
|
||||
[dev-dependencies]
|
||||
simconnect-sdk = { path = "../simconnect-sdk" }
|
||||
trybuild = { version = "1.0", features = ["diff"] }
|
||||
|
@@ -1,7 +1,341 @@
|
||||
extern crate proc_macro;
|
||||
use proc_macro::TokenStream;
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[proc_macro_derive(SimConnectObject, attributes(field))]
|
||||
pub fn derive(_: TokenStream) -> TokenStream {
|
||||
TokenStream::new()
|
||||
use once_cell::sync::Lazy;
|
||||
use proc_macro::TokenStream;
|
||||
use quote::quote;
|
||||
use syn::{parse_macro_input, DeriveInput};
|
||||
|
||||
struct FieldInfo {
|
||||
required: bool,
|
||||
accepted_values: Vec<String>,
|
||||
}
|
||||
|
||||
static ALLOWED_CLASS_ATTRIBUTES: Lazy<HashMap<String, FieldInfo>> = Lazy::new(|| {
|
||||
let mut map = HashMap::new();
|
||||
|
||||
map.insert(
|
||||
"period".to_string(),
|
||||
FieldInfo {
|
||||
required: true,
|
||||
accepted_values: vec![
|
||||
"once".to_string(),
|
||||
"visual-frame".to_string(),
|
||||
"sim-frame".to_string(),
|
||||
"second".to_string(),
|
||||
],
|
||||
},
|
||||
);
|
||||
map.insert(
|
||||
"condition".to_string(),
|
||||
FieldInfo {
|
||||
required: false,
|
||||
accepted_values: vec!["none".to_string(), "changed".to_string()],
|
||||
},
|
||||
);
|
||||
|
||||
map
|
||||
});
|
||||
|
||||
static ALLOWED_FIELD_ATTRIBUTES: Lazy<HashMap<String, FieldInfo>> = Lazy::new(|| {
|
||||
let mut map = HashMap::new();
|
||||
|
||||
map.insert(
|
||||
"name".to_string(),
|
||||
FieldInfo {
|
||||
required: true,
|
||||
accepted_values: vec![],
|
||||
},
|
||||
);
|
||||
map.insert(
|
||||
"unit".to_string(),
|
||||
FieldInfo {
|
||||
required: true,
|
||||
accepted_values: vec![],
|
||||
},
|
||||
);
|
||||
|
||||
map
|
||||
});
|
||||
const SUPPORTED_FIELD_TYPES: [&str; 2] = ["f64", "bool"];
|
||||
|
||||
/// SimConnectObject derive macro.
|
||||
///
|
||||
/// # Struct Arguments
|
||||
/// * `period` - Required. One of `once`, `visual-frame`, `sim-frame`, `second`.
|
||||
/// * `condition` - Optional. The condition of the data. Must be either `none` or `changed`. Defaults to `none`.
|
||||
///
|
||||
/// # Field Arguments
|
||||
/// * `name` - Required. The name of the field. One from <http://www.prepar3d.com/SDKv3/LearningCenter/utilities/variables/simulation_variables.html#Simulation%20Variables>.
|
||||
/// * `unit` - Required. The unit of the field.
|
||||
///
|
||||
/// # Example
|
||||
/// ```rust
|
||||
/// use simconnect_sdk::SimConnectObject;
|
||||
///
|
||||
/// #[derive(Debug, Clone, SimConnectObject)]
|
||||
/// #[simconnect(period = "second")]
|
||||
/// struct GpsData {
|
||||
/// #[simconnect(name = "PLANE LATITUDE", unit = "degrees")]
|
||||
/// lat: f64,
|
||||
/// #[simconnect(name = "PLANE LONGITUDE", unit = "degrees")]
|
||||
/// lon: f64,
|
||||
/// #[simconnect(name = "PLANE LONGITUDE", unit = "degrees")]
|
||||
/// alt: f64,
|
||||
/// }
|
||||
/// ```
|
||||
#[proc_macro_derive(SimConnectObject, attributes(simconnect))]
|
||||
pub fn derive(input: TokenStream) -> TokenStream {
|
||||
let ast = parse_macro_input!(input as DeriveInput);
|
||||
|
||||
let name = &ast.ident;
|
||||
|
||||
let fields = if let syn::Data::Struct(syn::DataStruct {
|
||||
fields: syn::Fields::Named(syn::FieldsNamed { ref named, .. }),
|
||||
..
|
||||
}) = ast.data
|
||||
{
|
||||
named
|
||||
} else {
|
||||
return mk_err(
|
||||
ast,
|
||||
"Unsupported field type. Only named fields are supported.",
|
||||
)
|
||||
.into();
|
||||
};
|
||||
|
||||
let build_fields = fields.iter().map(parse_field);
|
||||
let request_data = request_data(&ast);
|
||||
|
||||
let expanded = quote! {
|
||||
impl simconnect_sdk::SimConnectObjectExt for #name {
|
||||
fn register(client: &mut simconnect_sdk::SimConnect, id: u32) -> Result<(), simconnect_sdk::SimConnectError> {
|
||||
#(#build_fields)*
|
||||
|
||||
#request_data
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
impl TryFrom<&simconnect_sdk::NotificationData> for #name {
|
||||
type Error = simconnect_sdk::SimConnectError;
|
||||
|
||||
fn try_from(value: &simconnect_sdk::NotificationData) -> Result<Self, Self::Error> {
|
||||
value.try_transmute::<#name>()
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
expanded.into()
|
||||
}
|
||||
|
||||
fn parse_field(f: &syn::Field) -> proc_macro2::TokenStream {
|
||||
let error_message = "expected attribute `#[simconnect(name = \"...\", unit = \"...\")]`";
|
||||
|
||||
let attr = get_attribute(&f.attrs);
|
||||
|
||||
match attr {
|
||||
Some(attr) => {
|
||||
let ty = &f.ty;
|
||||
let properties =
|
||||
extract_attribute_string_properties(attr, &ALLOWED_FIELD_ATTRIBUTES, error_message);
|
||||
|
||||
match properties {
|
||||
Ok(properties) => {
|
||||
let error_message_supported_types = &format!(
|
||||
"Field type must be one of ['{}']",
|
||||
SUPPORTED_FIELD_TYPES.join("', '")
|
||||
);
|
||||
|
||||
match ty {
|
||||
syn::Type::Path(syn::TypePath { path, .. }) => {
|
||||
let path = &path.segments;
|
||||
let path = path.iter().map(|s| &s.ident);
|
||||
|
||||
let name = properties.get("name").expect("this should never happen");
|
||||
let unit = properties.get("unit").expect("this should never happen");
|
||||
|
||||
match path.last() {
|
||||
Some(value) if value == "f64" => {
|
||||
quote! {
|
||||
client.add_to_data_definition(id, #name, #unit, simconnect_sdk::DataType::Float64)?;
|
||||
}
|
||||
}
|
||||
Some(value) if value == "bool" => {
|
||||
quote! {
|
||||
client.add_to_data_definition(id, #name, #unit, simconnect_sdk::DataType::Bool)?;
|
||||
}
|
||||
}
|
||||
_ => mk_err(f, error_message_supported_types),
|
||||
}
|
||||
}
|
||||
_ => mk_err(f, error_message_supported_types),
|
||||
}
|
||||
}
|
||||
Err(e) => e,
|
||||
}
|
||||
}
|
||||
None => mk_err(f, error_message),
|
||||
}
|
||||
}
|
||||
|
||||
fn request_data(ast: &DeriveInput) -> proc_macro2::TokenStream {
|
||||
let attr = get_attribute(&ast.attrs);
|
||||
let error_message = "expected attribute `#[simconnect(period = \"...\", condition = \"...\")]`";
|
||||
|
||||
match attr {
|
||||
Some(attr) => {
|
||||
let properties =
|
||||
extract_attribute_string_properties(attr, &ALLOWED_CLASS_ATTRIBUTES, error_message);
|
||||
|
||||
match properties {
|
||||
Ok(properties) => {
|
||||
let period = match properties.get("period") {
|
||||
Some(p) if p == "once" => {
|
||||
quote! {
|
||||
simconnect_sdk::Period::Once
|
||||
}
|
||||
}
|
||||
Some(p) if p == "visual-frame" => {
|
||||
quote! {
|
||||
simconnect_sdk::Period::VisualFrame
|
||||
}
|
||||
}
|
||||
Some(p) if p == "sim-frame" => {
|
||||
quote! {
|
||||
simconnect_sdk::Period::SimFrame
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
quote! {
|
||||
simconnect_sdk::Period::Second
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let condition = match properties.get("condition") {
|
||||
Some(c) if c == "changed" => {
|
||||
quote! {
|
||||
simconnect_sdk::Condition::Changed
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
quote! {
|
||||
simconnect_sdk::Condition::None
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
quote! {
|
||||
client.request_data_on_sim_object(id, #period, #condition, 0)?;
|
||||
}
|
||||
}
|
||||
Err(e) => e,
|
||||
}
|
||||
}
|
||||
None => mk_err(ast, error_message),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_attribute(attrs: &[syn::Attribute]) -> Option<&syn::Attribute> {
|
||||
attrs
|
||||
.iter()
|
||||
.find(|&attr| attr.path.segments.len() == 1 && attr.path.segments[0].ident == "simconnect")
|
||||
}
|
||||
|
||||
fn extract_attribute_string_properties(
|
||||
attr: &syn::Attribute,
|
||||
allowed_properties: &HashMap<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(s) => {
|
||||
let value = s.value();
|
||||
|
||||
if !property.accepted_values.is_empty()
|
||||
&& !property.accepted_values.contains(&value)
|
||||
{
|
||||
// found an invalid value
|
||||
return Err(mk_err(
|
||||
nv,
|
||||
&format!(
|
||||
"`{ident_string}` must be one of ['{}']",
|
||||
property.accepted_values.join("', '")
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
results.insert(ident_string, value);
|
||||
}
|
||||
lit => {
|
||||
return Err(syn::Error::new_spanned(
|
||||
nv,
|
||||
format!("expected string, found {lit:?}"),
|
||||
)
|
||||
.to_compile_error())
|
||||
}
|
||||
}
|
||||
}
|
||||
None => {
|
||||
// found an unexpected property name
|
||||
return Err(mk_err(nvs.clone(), error_message));
|
||||
}
|
||||
}
|
||||
}
|
||||
None => {
|
||||
// no ident found
|
||||
return Err(mk_err(nvs.clone(), error_message));
|
||||
}
|
||||
}
|
||||
}
|
||||
meta => {
|
||||
// nvc.nested[] was not k = v
|
||||
return Err(mk_err(meta, error_message));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check that all required properties are specified
|
||||
for (field, _) in allowed_properties.iter().filter(|(_, fi)| fi.required) {
|
||||
if !results.contains_key(field) {
|
||||
return Err(mk_err(nvs, error_message));
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(meta) => {
|
||||
// inside of #[] there was just an identifier (`#[simconnect]`)
|
||||
// or a key-value mapping (`#[simconnect = "foo"]`), neither of which are okay.
|
||||
|
||||
return Err(mk_err(meta, error_message));
|
||||
}
|
||||
Err(e) => {
|
||||
return Err(e.to_compile_error());
|
||||
}
|
||||
};
|
||||
|
||||
Ok(results)
|
||||
}
|
||||
|
||||
fn mk_err<T: quote::ToTokens>(t: T, message: &str) -> proc_macro2::TokenStream {
|
||||
syn::Error::new_spanned(t, message).to_compile_error()
|
||||
}
|
||||
|
31
simconnect-sdk-derive/tests/01-parse.rs
Normal file
31
simconnect-sdk-derive/tests/01-parse.rs
Normal file
@@ -0,0 +1,31 @@
|
||||
#![allow(unused_variables, dead_code)]
|
||||
|
||||
use simconnect_sdk_derive::SimConnectObject;
|
||||
#[derive(Debug, Clone, SimConnectObject)]
|
||||
#[simconnect(period = "second")]
|
||||
struct GpsData1 {
|
||||
#[simconnect(name = "PLANE LATITUDE", unit = "degrees")]
|
||||
pub lat: f64,
|
||||
#[simconnect(name = "PLANE LONGITUDE", unit = "degrees")]
|
||||
pub lon: f64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, SimConnectObject)]
|
||||
#[simconnect(period = "second", condition = "none")]
|
||||
struct GpsData2 {
|
||||
#[simconnect(name = "PLANE LATITUDE", unit = "degrees")]
|
||||
pub lat: f64,
|
||||
#[simconnect(name = "PLANE LONGITUDE", unit = "degrees")]
|
||||
pub lon: f64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, SimConnectObject)]
|
||||
#[simconnect(period = "visual-frame", condition = "changed")]
|
||||
struct GpsData3 {
|
||||
#[simconnect(name = "PLANE LATITUDE", unit = "degrees")]
|
||||
pub lat: f64,
|
||||
#[simconnect(name = "PLANE LONGITUDE", unit = "degrees")]
|
||||
pub lon: f64,
|
||||
}
|
||||
|
||||
fn main() {}
|
39
simconnect-sdk-derive/tests/02-struct-attr-errors.rs
Normal file
39
simconnect-sdk-derive/tests/02-struct-attr-errors.rs
Normal file
@@ -0,0 +1,39 @@
|
||||
#![allow(unused_variables, dead_code)]
|
||||
use simconnect_sdk_derive::SimConnectObject;
|
||||
|
||||
#[derive(Debug, Clone, SimConnectObject)]
|
||||
#[simconnect(period = "visual-frame", condition = "changed")]
|
||||
struct GpsData1(f64);
|
||||
|
||||
#[derive(Debug, Clone, SimConnectObject)]
|
||||
struct GpsData2 {}
|
||||
|
||||
#[derive(Debug, Clone, SimConnectObject)]
|
||||
#[simconnect]
|
||||
struct GpsData3 {}
|
||||
|
||||
#[derive(Debug, Clone, SimConnectObject)]
|
||||
#[simconnect()]
|
||||
struct GpsData4 {}
|
||||
|
||||
#[derive(Debug, Clone, SimConnectObject)]
|
||||
#[simconnect(period = "second", period = "second")]
|
||||
struct GpsData5 {}
|
||||
|
||||
#[derive(Debug, Clone, SimConnectObject)]
|
||||
#[simconnect(condition = "none", condition = "none")]
|
||||
struct GpsData6 {}
|
||||
|
||||
#[derive(Debug, Clone, SimConnectObject)]
|
||||
#[simconnect(period = "second", condition = "none", test = "test")]
|
||||
struct GpsData7 {}
|
||||
|
||||
#[derive(Debug, Clone, SimConnectObject)]
|
||||
#[simconnect(periodX = "second", condition = "none")]
|
||||
struct GpsData8 {}
|
||||
|
||||
#[derive(Debug, Clone, SimConnectObject)]
|
||||
#[simconnect(period = "second", conditionX = "none")]
|
||||
struct GpsData9 {}
|
||||
|
||||
fn main() {}
|
54
simconnect-sdk-derive/tests/02-struct-attr-errors.stderr
Normal file
54
simconnect-sdk-derive/tests/02-struct-attr-errors.stderr
Normal file
@@ -0,0 +1,54 @@
|
||||
error: Unsupported field type. Only named fields are supported.
|
||||
--> tests/02-struct-attr-errors.rs:5:1
|
||||
|
|
||||
5 | / #[simconnect(period = "visual-frame", condition = "changed")]
|
||||
6 | | struct GpsData1(f64);
|
||||
| |_____________________^
|
||||
|
||||
error: expected attribute `#[simconnect(period = "...", condition = "...")]`
|
||||
--> tests/02-struct-attr-errors.rs:9:1
|
||||
|
|
||||
9 | struct GpsData2 {}
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: expected attribute `#[simconnect(period = "...", condition = "...")]`
|
||||
--> tests/02-struct-attr-errors.rs:12:3
|
||||
|
|
||||
12 | #[simconnect]
|
||||
| ^^^^^^^^^^
|
||||
|
||||
error: expected attribute `#[simconnect(period = "...", condition = "...")]`
|
||||
--> tests/02-struct-attr-errors.rs:16:3
|
||||
|
|
||||
16 | #[simconnect()]
|
||||
| ^^^^^^^^^^^^
|
||||
|
||||
error: expected attribute `#[simconnect(period = "...", condition = "...")]`
|
||||
--> tests/02-struct-attr-errors.rs:20:3
|
||||
|
|
||||
20 | #[simconnect(period = "second", period = "second")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: expected attribute `#[simconnect(period = "...", condition = "...")]`
|
||||
--> tests/02-struct-attr-errors.rs:24:3
|
||||
|
|
||||
24 | #[simconnect(condition = "none", condition = "none")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: expected attribute `#[simconnect(period = "...", condition = "...")]`
|
||||
--> tests/02-struct-attr-errors.rs:28:3
|
||||
|
|
||||
28 | #[simconnect(period = "second", condition = "none", test = "test")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: expected attribute `#[simconnect(period = "...", condition = "...")]`
|
||||
--> tests/02-struct-attr-errors.rs:32:3
|
||||
|
|
||||
32 | #[simconnect(periodX = "second", condition = "none")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: expected attribute `#[simconnect(period = "...", condition = "...")]`
|
||||
--> tests/02-struct-attr-errors.rs:36:3
|
||||
|
|
||||
36 | #[simconnect(period = "second", conditionX = "none")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
73
simconnect-sdk-derive/tests/03-field-attr-errors.rs
Normal file
73
simconnect-sdk-derive/tests/03-field-attr-errors.rs
Normal file
@@ -0,0 +1,73 @@
|
||||
#![allow(unused_variables, dead_code)]
|
||||
use simconnect_sdk_derive::SimConnectObject;
|
||||
|
||||
#[derive(Debug, Clone, SimConnectObject)]
|
||||
#[simconnect(period = "second", condition = "none")]
|
||||
struct GpsData1 {
|
||||
pub lat: f64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, SimConnectObject)]
|
||||
#[simconnect(period = "second", condition = "none")]
|
||||
struct GpsData2 {
|
||||
#[simconnect]
|
||||
pub lat: f64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, SimConnectObject)]
|
||||
#[simconnect(period = "second", condition = "none")]
|
||||
struct GpsData3 {
|
||||
#[simconnect()]
|
||||
pub lat: f64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, SimConnectObject)]
|
||||
#[simconnect(period = "second", condition = "none")]
|
||||
struct GpsData4 {
|
||||
#[simconnect(name = "PLANE LATITUDE")]
|
||||
pub lat: f64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, SimConnectObject)]
|
||||
#[simconnect(period = "second", condition = "none")]
|
||||
struct GpsData5 {
|
||||
#[simconnect(unit = "degrees")]
|
||||
pub lat: f64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, SimConnectObject)]
|
||||
#[simconnect(period = "second", condition = "none")]
|
||||
struct GpsData6 {
|
||||
#[simconnect(name = "PLANE LATITUDE", name = "PLANE LATITUDE")]
|
||||
pub lat: f64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, SimConnectObject)]
|
||||
#[simconnect(period = "second", condition = "none")]
|
||||
struct GpsData7 {
|
||||
#[simconnect(unit = "degrees", unit = "degrees")]
|
||||
pub lat: f64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, SimConnectObject)]
|
||||
#[simconnect(period = "second", condition = "none")]
|
||||
struct GpsData8 {
|
||||
#[simconnect(name = "PLANE LATITUDE", unit = "degrees", unit = "degrees")]
|
||||
pub lat: f64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, SimConnectObject)]
|
||||
#[simconnect(period = "second", condition = "none")]
|
||||
struct GpsData9 {
|
||||
#[simconnect(nameX = "PLANE LATITUDE", unit = "degrees")]
|
||||
pub lat: f64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, SimConnectObject)]
|
||||
#[simconnect(period = "second", condition = "none")]
|
||||
struct GpsData10 {
|
||||
#[simconnect(name = "PLANE LATITUDE", unitX = "degrees")]
|
||||
pub lat: f64,
|
||||
}
|
||||
|
||||
fn main() {}
|
59
simconnect-sdk-derive/tests/03-field-attr-errors.stderr
Normal file
59
simconnect-sdk-derive/tests/03-field-attr-errors.stderr
Normal file
@@ -0,0 +1,59 @@
|
||||
error: expected attribute `#[simconnect(name = "...", unit = "...")]`
|
||||
--> tests/03-field-attr-errors.rs:7:5
|
||||
|
|
||||
7 | pub lat: f64,
|
||||
| ^^^^^^^^^^^^
|
||||
|
||||
error: expected attribute `#[simconnect(name = "...", unit = "...")]`
|
||||
--> tests/03-field-attr-errors.rs:13:7
|
||||
|
|
||||
13 | #[simconnect]
|
||||
| ^^^^^^^^^^
|
||||
|
||||
error: expected attribute `#[simconnect(name = "...", unit = "...")]`
|
||||
--> tests/03-field-attr-errors.rs:20:7
|
||||
|
|
||||
20 | #[simconnect()]
|
||||
| ^^^^^^^^^^^^
|
||||
|
||||
error: expected attribute `#[simconnect(name = "...", unit = "...")]`
|
||||
--> tests/03-field-attr-errors.rs:27:7
|
||||
|
|
||||
27 | #[simconnect(name = "PLANE LATITUDE")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: expected attribute `#[simconnect(name = "...", unit = "...")]`
|
||||
--> tests/03-field-attr-errors.rs:34:7
|
||||
|
|
||||
34 | #[simconnect(unit = "degrees")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: expected attribute `#[simconnect(name = "...", unit = "...")]`
|
||||
--> tests/03-field-attr-errors.rs:41:7
|
||||
|
|
||||
41 | #[simconnect(name = "PLANE LATITUDE", name = "PLANE LATITUDE")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: expected attribute `#[simconnect(name = "...", unit = "...")]`
|
||||
--> tests/03-field-attr-errors.rs:48:7
|
||||
|
|
||||
48 | #[simconnect(unit = "degrees", unit = "degrees")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: expected attribute `#[simconnect(name = "...", unit = "...")]`
|
||||
--> tests/03-field-attr-errors.rs:55:7
|
||||
|
|
||||
55 | #[simconnect(name = "PLANE LATITUDE", unit = "degrees", unit = "degrees")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: expected attribute `#[simconnect(name = "...", unit = "...")]`
|
||||
--> tests/03-field-attr-errors.rs:62:7
|
||||
|
|
||||
62 | #[simconnect(nameX = "PLANE LATITUDE", unit = "degrees")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: expected attribute `#[simconnect(name = "...", unit = "...")]`
|
||||
--> tests/03-field-attr-errors.rs:69:7
|
||||
|
|
||||
69 | #[simconnect(name = "PLANE LATITUDE", unitX = "degrees")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
27
simconnect-sdk-derive/tests/04-invalid-values.rs
Normal file
27
simconnect-sdk-derive/tests/04-invalid-values.rs
Normal file
@@ -0,0 +1,27 @@
|
||||
#![allow(unused_variables, dead_code)]
|
||||
use simconnect_sdk_derive::SimConnectObject;
|
||||
|
||||
#[derive(Debug, Clone, SimConnectObject)]
|
||||
#[simconnect(period = 123, condition = "none")]
|
||||
struct GpsData1 {}
|
||||
|
||||
#[derive(Debug, Clone, SimConnectObject)]
|
||||
#[simconnect(period = "second", condition = 123)]
|
||||
struct GpsData2 {}
|
||||
|
||||
#[derive(Debug, Clone, SimConnectObject)]
|
||||
#[simconnect(period = "X")]
|
||||
struct GpsData3 {}
|
||||
|
||||
#[derive(Debug, Clone, SimConnectObject)]
|
||||
#[simconnect(period = "second", condition = "X")]
|
||||
struct GpsData4 {}
|
||||
|
||||
#[derive(Debug, Clone, SimConnectObject)]
|
||||
#[simconnect(period = "second", condition = "none")]
|
||||
struct GpsData5 {
|
||||
#[simconnect(name = "PLANE LATITUDE", unit = "degrees")]
|
||||
pub lat: String,
|
||||
}
|
||||
|
||||
fn main() {}
|
30
simconnect-sdk-derive/tests/04-invalid-values.stderr
Normal file
30
simconnect-sdk-derive/tests/04-invalid-values.stderr
Normal file
@@ -0,0 +1,30 @@
|
||||
error: expected string, found Int(LitInt { token: 123 })
|
||||
--> tests/04-invalid-values.rs:5:14
|
||||
|
|
||||
5 | #[simconnect(period = 123, condition = "none")]
|
||||
| ^^^^^^^^^^^^
|
||||
|
||||
error: expected string, found Int(LitInt { token: 123 })
|
||||
--> tests/04-invalid-values.rs:9:33
|
||||
|
|
||||
9 | #[simconnect(period = "second", condition = 123)]
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
||||
error: `period` must be one of ['once', 'visual-frame', 'sim-frame', 'second']
|
||||
--> tests/04-invalid-values.rs:13:14
|
||||
|
|
||||
13 | #[simconnect(period = "X")]
|
||||
| ^^^^^^^^^^^^
|
||||
|
||||
error: `condition` must be one of ['none', 'changed']
|
||||
--> tests/04-invalid-values.rs:17:33
|
||||
|
|
||||
17 | #[simconnect(period = "second", condition = "X")]
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
||||
error: Field type must be one of ['f64', 'bool']
|
||||
--> tests/04-invalid-values.rs:23:5
|
||||
|
|
||||
23 | / #[simconnect(name = "PLANE LATITUDE", unit = "degrees")]
|
||||
24 | | pub lat: String,
|
||||
| |___________________^
|
8
simconnect-sdk-derive/tests/run.rs
Normal file
8
simconnect-sdk-derive/tests/run.rs
Normal file
@@ -0,0 +1,8 @@
|
||||
#[test]
|
||||
fn tests() {
|
||||
let t = trybuild::TestCases::new();
|
||||
t.pass("tests/01-parse.rs");
|
||||
t.compile_fail("tests/02-struct-attr-errors.rs");
|
||||
t.compile_fail("tests/03-field-attr-errors.rs");
|
||||
t.compile_fail("tests/04-invalid-values.rs");
|
||||
}
|
Reference in New Issue
Block a user