-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added examples and proc macros
- Loading branch information
Showing
16 changed files
with
795 additions
and
119 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
[package] | ||
name = "mil_std_1553b_derive" | ||
version = "0.4.0" | ||
edition = "2021" | ||
license = "MIT" | ||
readme = "README.md" | ||
description = "MIL STD 1553B proc macros" | ||
authors = ["Michael House <[email protected]>"] | ||
|
||
[lib] | ||
proc-macro = true | ||
|
||
[dependencies] | ||
syn = { version = "1.0", features = ["full"]} | ||
quote = "1.0" | ||
proc-macro-error = "1.0.4" | ||
proc-macro2 = "1.0.79" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# MIL STD 1553B Derives | ||
|
||
Provides proc-macros for deriving mil_std_1553b types. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
use proc_macro::TokenStream; | ||
use proc_macro_error::OptionExt; | ||
|
||
pub struct FieldArgs { | ||
name: Option<String>, | ||
value: Option<String> | ||
} | ||
|
||
impl FieldArgs { | ||
pub fn new(input: TokenStream) -> Self { | ||
let mut items = input.into_iter(); | ||
|
||
let name = items | ||
.next() | ||
.map(|t| t.to_string()); | ||
|
||
// skip the comma | ||
items.next(); | ||
|
||
let value = items | ||
.next() | ||
.map(|t| t | ||
.to_string() | ||
.trim_start_matches("0b") | ||
.to_string()); | ||
|
||
Self { name, value } | ||
} | ||
|
||
pub fn name(&self) -> String { | ||
self.name | ||
.clone() | ||
.expect_or_abort("Need an identifier for the field") | ||
} | ||
|
||
pub fn mask(&self) -> u16 { | ||
self.value | ||
.clone() | ||
.and_then(|v| u16::from_str_radix(&v,2).ok()) | ||
.expect_or_abort("Need a mask for the field") | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,180 @@ | ||
use proc_macro::{Span, TokenStream}; | ||
use proc_macro_error::{abort, abort_call_site}; | ||
use quote::{quote, ToTokens}; | ||
use syn::{parse_macro_input, DeriveInput, Ident, ItemFn}; | ||
|
||
mod word; | ||
mod field; | ||
|
||
use field::FieldArgs; | ||
use word::FieldKind; | ||
|
||
/// Derive the `Word` trait and associated From implementations | ||
/// | ||
/// This trait requires that the `Default` trait is implemented | ||
/// as well. To avoid this, implement the Word trait manually. | ||
/// | ||
/// ### Example | ||
/// | ||
/// ```rust | ||
/// #[derive(Default, Word)] | ||
/// struct MyWord { | ||
/// | ||
/// #[data] | ||
/// buffer: [u8; 2], | ||
/// | ||
/// #[parity] | ||
/// parity_bit: u8, | ||
/// | ||
/// // you can have any other | ||
/// // fields you need | ||
/// count: u8, | ||
/// } | ||
/// ``` | ||
#[proc_macro_derive(Word, attributes(data,parity))] | ||
#[proc_macro_error::proc_macro_error] | ||
pub fn word_derive(input: TokenStream) -> TokenStream { | ||
let input = parse_macro_input!(input as DeriveInput); | ||
|
||
let item = match input.data { | ||
syn::Data::Struct(s) => s, | ||
_ => abort_call_site!("Word derive is only valid on a struct") | ||
}; | ||
|
||
let fields = match item.fields { | ||
syn::Fields::Named(n) => n, | ||
_ => abort_call_site!("Word derive requires named fields") | ||
}; | ||
|
||
let mut data_field: FieldKind = FieldKind::None; | ||
let mut parity_field: FieldKind = FieldKind::None; | ||
|
||
for named in fields.named { | ||
match word::parse_field(&named) { | ||
|
||
word::FieldKind::Data(v) if data_field.is_some() => | ||
abort!(v, "Duplicate #[data] fields"), | ||
|
||
word::FieldKind::Parity(v) if parity_field.is_some() => | ||
abort!(v, "Duplicate #[parity] fields"), | ||
|
||
k if k.is_data() => data_field = k, | ||
k if k.is_parity() => parity_field = k, | ||
_ => continue | ||
} | ||
} | ||
|
||
if data_field.is_none() { | ||
abort!(data_field.ident(),"Need a '#[data]' field with type '[u8;2]'"); | ||
} | ||
|
||
if parity_field.is_none() { | ||
abort!(parity_field.ident(),"Need a '#[parity]' field with type 'u8'"); | ||
} | ||
|
||
let self_type = input.ident; | ||
|
||
let data_ident = data_field.ident(); | ||
|
||
|
||
let word_impl = word::impl_word(&data_field, &parity_field, &self_type); | ||
|
||
quote!( | ||
#word_impl | ||
|
||
impl From<&mil_std_1553b::DataWord> for #self_type { | ||
fn from(word: &mil_std_1553b::DataWord) -> Self { | ||
use mil_std_1553b::Word; | ||
#self_type::new() | ||
.with_bytes(word.as_bytes()) | ||
.with_parity(word.parity()) | ||
} | ||
} | ||
|
||
impl From<&#self_type> for mil_std_1553b::DataWord { | ||
fn from(word: &#self_type) -> Self { | ||
use mil_std_1553b::Word; | ||
mil_std_1553b::DataWord::new() | ||
.with_bytes(word.as_bytes()) | ||
.with_parity(word.parity()) | ||
} | ||
} | ||
|
||
impl From<mil_std_1553b::DataWord> for #self_type { | ||
fn from(word: mil_std_1553b::DataWord) -> Self { | ||
Self::from(&word) | ||
} | ||
} | ||
|
||
impl From<#self_type> for mil_std_1553b::DataWord { | ||
fn from(word: #self_type) -> Self { | ||
Self::from(&word) | ||
} | ||
} | ||
|
||
impl From<&#self_type> for u16 { | ||
fn from(word: &#self_type) -> Self { | ||
u16::from_be_bytes(word.#data_ident) | ||
} | ||
} | ||
|
||
impl From<#self_type> for u16 { | ||
fn from(word: #self_type) -> Self { | ||
u16::from_be_bytes(word.#data_ident) | ||
} | ||
} | ||
).to_token_stream().into() | ||
} | ||
|
||
/// Implement a parser for a word field | ||
/// | ||
/// ### Arguments | ||
/// | ||
/// * name - A const name for the field (e.g. 'MY_FIELD') | ||
/// * mask - A u16 bit mask for the field (e.g. '0b10000000000000000') | ||
/// | ||
/// ### Example | ||
/// | ||
/// ```rust | ||
/// | ||
/// #[field(MY_FIELD, 0b1000000000000000)] | ||
/// pub fn get_my_field_value(&self) -> u8 { | ||
/// Self::MY_FIELD.get(self) | ||
/// } | ||
/// | ||
/// ``` | ||
/// | ||
/// Will generate the following code: | ||
/// | ||
/// ```rust | ||
/// | ||
/// pub const MY_FIELD_MASK: u16 = 32768u16; | ||
/// pub const MY_FIELD: Field = Field::from(Self::MY_FIELD_MASK); | ||
/// pub fn get_my_field_value(&self) -> u8 { | ||
/// Self::MY_FIELD.get(self) | ||
/// } | ||
/// | ||
/// ``` | ||
#[proc_macro_attribute] | ||
#[proc_macro_error::proc_macro_error] | ||
pub fn field(attr: TokenStream, item: TokenStream) -> TokenStream { | ||
let args = FieldArgs::new(attr); | ||
let item = parse_macro_input!(item as ItemFn); | ||
|
||
let name = args.name(); | ||
let mask = args.mask(); | ||
|
||
let mask_label = format!("{}_MASK",name); | ||
let mask_ident = Ident::new(&mask_label, Span::call_site().into()); | ||
|
||
let field_label = format!("{}",name); | ||
let field_ident = Ident::new(&field_label, Span::call_site().into()); | ||
|
||
quote!( | ||
pub const #mask_ident: u16 = #mask; | ||
|
||
pub const #field_ident: Field = Field::from(Self::#mask_ident); | ||
|
||
#item | ||
).to_token_stream().into() | ||
} |
Oops, something went wrong.