Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add examples #66

Merged
merged 6 commits into from
Mar 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,11 @@ categories = [
"aerospace::protocols",
"no-std::no-alloc",
"parser-implementations"
]
]

[features]
default = [ "derive" ]
derive = [ "dep:mil_std_1553b_derive" ]

[dependencies]
mil_std_1553b_derive = { path = "crates/mil_std_1553b_derive", optional = true }
46 changes: 46 additions & 0 deletions crates/mil_std_1553b_derive/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 17 additions & 0 deletions crates/mil_std_1553b_derive/Cargo.toml
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"
3 changes: 3 additions & 0 deletions crates/mil_std_1553b_derive/README.md
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.
43 changes: 43 additions & 0 deletions crates/mil_std_1553b_derive/src/field.rs
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")
}

}
180 changes: 180 additions & 0 deletions crates/mil_std_1553b_derive/src/lib.rs
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()
}
Loading
Loading