From dd1e21b97c1824c686a944e009e1179053313170 Mon Sep 17 00:00:00 2001 From: Jianfeng Jiang Date: Wed, 19 Oct 2022 11:34:47 +0800 Subject: [PATCH] add procedural macro typeflags --- src/Cargo.lock | 38 ++++++ src/Cargo.toml | 3 + src/kxos-typeflags-util/Cargo.toml | 8 ++ src/kxos-typeflags-util/src/assert.rs | 21 ++++ src/kxos-typeflags-util/src/bool.rs | 78 ++++++++++++ src/kxos-typeflags-util/src/if_.rs | 29 +++++ src/kxos-typeflags-util/src/lib.rs | 15 +++ src/kxos-typeflags-util/src/same.rs | 28 +++++ src/kxos-typeflags-util/src/set.rs | 107 +++++++++++++++++ src/kxos-typeflags/Cargo.toml | 15 +++ src/kxos-typeflags/src/flag_set.rs | 163 ++++++++++++++++++++++++++ src/kxos-typeflags/src/lib.rs | 63 ++++++++++ src/kxos-typeflags/src/type_flag.rs | 139 ++++++++++++++++++++++ src/kxos-typeflags/src/util.rs | 95 +++++++++++++++ 14 files changed, 802 insertions(+) create mode 100644 src/kxos-typeflags-util/Cargo.toml create mode 100644 src/kxos-typeflags-util/src/assert.rs create mode 100644 src/kxos-typeflags-util/src/bool.rs create mode 100644 src/kxos-typeflags-util/src/if_.rs create mode 100644 src/kxos-typeflags-util/src/lib.rs create mode 100644 src/kxos-typeflags-util/src/same.rs create mode 100644 src/kxos-typeflags-util/src/set.rs create mode 100644 src/kxos-typeflags/Cargo.toml create mode 100644 src/kxos-typeflags/src/flag_set.rs create mode 100644 src/kxos-typeflags/src/lib.rs create mode 100644 src/kxos-typeflags/src/type_flag.rs create mode 100644 src/kxos-typeflags/src/util.rs diff --git a/src/Cargo.lock b/src/Cargo.lock index 890bb973..6d3c457b 100644 --- a/src/Cargo.lock +++ b/src/Cargo.lock @@ -50,12 +50,27 @@ dependencies = [ "spin 0.7.1", ] +[[package]] +name = "either" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" + [[package]] name = "font8x8" version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e63201c624b8c8883921b1a1accc8916c4fa9dbfb15d122b26e4dde945b86bbf" +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + [[package]] name = "json" version = "0.12.4" @@ -117,6 +132,15 @@ dependencies = [ "spin 0.9.4", ] +[[package]] +name = "kxos-rights-proc" +version = "0.1.0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "kxos-std" version = "0.1.0" @@ -131,6 +155,20 @@ dependencies = [ "xmas-elf", ] +[[package]] +name = "kxos-typeflags" +version = "0.1.0" +dependencies = [ + "itertools", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "kxos-typeflags-util" +version = "0.1.0" + [[package]] name = "kxos-util" version = "0.1.0" diff --git a/src/Cargo.toml b/src/Cargo.toml index b1e0c20a..1c252408 100644 --- a/src/Cargo.toml +++ b/src/Cargo.toml @@ -18,6 +18,9 @@ members = [ "kxos-virtio", "kxos-util", "kxos-frame-pod-derive", + "kxos-typeflags", + "kxos-typeflags-util", + "kxos-rights-proc", ] [package.metadata.bootloader] diff --git a/src/kxos-typeflags-util/Cargo.toml b/src/kxos-typeflags-util/Cargo.toml new file mode 100644 index 00000000..f47df814 --- /dev/null +++ b/src/kxos-typeflags-util/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "kxos-typeflags-util" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/src/kxos-typeflags-util/src/assert.rs b/src/kxos-typeflags-util/src/assert.rs new file mode 100644 index 00000000..f4458a3e --- /dev/null +++ b/src/kxos-typeflags-util/src/assert.rs @@ -0,0 +1,21 @@ +//! define macro assert_type_same + +use crate::same::SameAs; + +pub type AssertTypeSame = >::Output; + +#[macro_export] +macro_rules! assert_type_same { + ($lhs:ty, $rhs:ty) => { + const _: $crate::assert::AssertTypeSame<$lhs, $rhs> = crate::True; + }; +} + +#[cfg(test)] +mod test { + #[test] + fn test() { + assert_type_same!(u16, u16); + assert_type_same!((), ()); + } +} diff --git a/src/kxos-typeflags-util/src/bool.rs b/src/kxos-typeflags-util/src/bool.rs new file mode 100644 index 00000000..d685a766 --- /dev/null +++ b/src/kxos-typeflags-util/src/bool.rs @@ -0,0 +1,78 @@ +//! Type level bools + +pub use std::ops::BitAnd as And; +pub use std::ops::BitOr as Or; +pub use std::ops::Not; + +pub trait Bool {} + +/// Type-level "true". +pub struct True; + +/// Type-level "false". +pub struct False; + +impl Bool for True {} +impl Bool for False {} + +impl Not for True { + type Output = False; + + fn not(self) -> Self::Output { + unimplemented!("not supposed to be used") + } +} + +impl Not for False { + type Output = True; + + fn not(self) -> Self::Output { + unimplemented!("not supposed to be used") + } +} + +impl And for True { + type Output = B; + + fn bitand(self, _rhs: B) -> Self::Output { + unimplemented!("not supposed to be used") + } +} + +impl And for False { + type Output = False; + + fn bitand(self, _rhs: B) -> Self::Output { + unimplemented!("not supposed to be used") + } +} + +impl Or for True { + type Output = True; + + fn bitor(self, _rhs: B) -> Self::Output { + unimplemented!("not supposed to be used") + } +} + +impl Or for False { + type Output = B; + + fn bitor(self, _rhs: B) -> Self::Output { + unimplemented!("not supposed to be used") + } +} + +pub type NotOp = ::Output; +pub type AndOp = >::Output; +pub type OrOp = >::Output; + +// In where clause, we can only use trait bounds, but not equal bounds. +// For certain situation, we need to do some comparison in where clause. +// e.g., we need to determine the result type of `SetContainOp` is `True` or `False`. +// Since Sometype == True is not allowed, We can use SomeType: IsTrue to determine the result. +pub trait IsTrue {} +pub trait IsFalse {} + +impl IsTrue for True {} +impl IsFalse for False {} diff --git a/src/kxos-typeflags-util/src/if_.rs b/src/kxos-typeflags-util/src/if_.rs new file mode 100644 index 00000000..4b144551 --- /dev/null +++ b/src/kxos-typeflags-util/src/if_.rs @@ -0,0 +1,29 @@ +//! Type Level If + +use crate::bool::{False, True}; + +pub trait If { + type Output; +} + +impl If for True { + type Output = B1; +} + +impl If for False { + type Output = B2; +} + +pub type IfOp = >::Output; + +#[cfg(test)] +mod test { + use super::*; + use crate::*; + + #[test] + fn test() { + assert_type_same!(IfOp, u32); + assert_type_same!(IfOp, usize); + } +} diff --git a/src/kxos-typeflags-util/src/lib.rs b/src/kxos-typeflags-util/src/lib.rs new file mode 100644 index 00000000..a55c8af6 --- /dev/null +++ b/src/kxos-typeflags-util/src/lib.rs @@ -0,0 +1,15 @@ +//! The content of this crate is from another project CapComp. +//! This crate defines common type level operations, like SameAsOp, and Bool type operations. +//! Besides, this crate defines operations to deal with type sets, like SetContain and SetInclude. +//! When use kxos-typeflags or kxos-rights-poc, this crate should also be added as a dependency. + +pub mod assert; +pub mod bool; +pub mod if_; +pub mod same; +pub mod set; + +pub use crate::bool::{And, AndOp, False, Not, NotOp, Or, OrOp, True, IsFalse, IsTrue}; +pub use crate::same::{SameAs, SameAsOp}; +pub use crate::set::{Cons, Nil, Set, SetContain, SetContainOp, SetInclude, SetIncludeOp}; +pub use assert::AssertTypeSame; diff --git a/src/kxos-typeflags-util/src/same.rs b/src/kxos-typeflags-util/src/same.rs new file mode 100644 index 00000000..66b19330 --- /dev/null +++ b/src/kxos-typeflags-util/src/same.rs @@ -0,0 +1,28 @@ +//! Traits used to check if two types are the same, returning a Bool. +//! This check happens at compile time + +use crate::bool::{Bool, False, True}; + +pub trait SameAs { + type Output: Bool; +} + +// A type is always same as itself +impl SameAs for T { + type Output = True; +} + +impl SameAs for True { + type Output = False; +} + +impl SameAs for False { + type Output = False; +} + +// How to implement self reflection? +// impl SameAs for U where T: SameAs, { +// type Output = >::Output; +// } + +pub type SameAsOp = >::Output; diff --git a/src/kxos-typeflags-util/src/set.rs b/src/kxos-typeflags-util/src/set.rs new file mode 100644 index 00000000..23cac701 --- /dev/null +++ b/src/kxos-typeflags-util/src/set.rs @@ -0,0 +1,107 @@ +//! Common types and traits to deal with type-level sets + +use std::marker::PhantomData; + +use crate::{ + False, OrOp, True, + SameAs, SameAsOp, + And, AndOp, + if_::{If, IfOp}, +}; + +use std::ops::BitOr as Or; + +/// A marker trait for type-level sets. +pub trait Set {} + +/// An non-empty type-level set. +pub struct Cons(PhantomData<(T, S)>); + +impl Cons { + pub fn new() -> Self { + Cons(PhantomData) + } +} + +/// An empty type-level set. +pub struct Nil; + +impl Set for Cons {} +impl Set for Nil {} + +/// A trait operator to check if `T` is a member of a type set; +pub trait SetContain { + type Output; +} + +pub type SetContainOp = >::Output; + +impl SetContain for Nil { + type Output = False; +} + +impl SetContain for Cons +where + S: Set + SetContain, + U: SameAs, + SameAsOp: Or>, +{ + type Output = OrOp, SetContainOp>; +} + +/// A trait operator to check if a set A includes a set B, i.e., A is a superset of B. +pub trait SetInclude { + type Output; +} + +pub type SetIncludeOp = >::Output; + +impl SetInclude for Nil { + type Output = True; +} + +impl SetInclude> for Nil { + type Output = False; +} + +impl SetInclude for Cons { + type Output = True; +} + +impl SetInclude> for Cons +where + SubS: Set, + SuperS: Set + SetInclude + SetContain, + SuperT: SameAs, + SetContainOp: And>, + SameAsOp: If< + SetIncludeOp, + AndOp, SetIncludeOp>, + >, +{ + type Output = IfOp< + SameAsOp, + SetIncludeOp, + AndOp, SetIncludeOp>, + >; +} + +#[cfg(test)] +mod test { + use crate::*; + + #[test] + fn test() { + assert_type_same!(SetContainOp, False>, False); + assert_type_same!(SetContainOp, True>, True); + + assert_type_same!( + SetIncludeOp>, Cons>, + True + ); + assert_type_same!( + SetIncludeOp>, Cons>, + False + ); + } +} diff --git a/src/kxos-typeflags/Cargo.toml b/src/kxos-typeflags/Cargo.toml new file mode 100644 index 00000000..6db6e61e --- /dev/null +++ b/src/kxos-typeflags/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "kxos-typeflags" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[lib] +proc-macro = true + +[dependencies] +proc-macro2 = "1.0" +quote = "1.0" +syn = {version = "1.0.90"} +itertools = "0.10.5" \ No newline at end of file diff --git a/src/kxos-typeflags/src/flag_set.rs b/src/kxos-typeflags/src/flag_set.rs new file mode 100644 index 00000000..3a7c181d --- /dev/null +++ b/src/kxos-typeflags/src/flag_set.rs @@ -0,0 +1,163 @@ +use itertools::Itertools; +use proc_macro2::{Ident, TokenStream}; +use quote::{quote, TokenStreamExt}; +use syn::Expr; + +use crate::type_flag::TypeFlagDef; + +const EMPTY_SET_NAME: &'static str = "::kxos_typeflags_util::Nil"; +const SET_NAME: &'static str = "::kxos_typeflags_util::Cons"; + +/// A flagSet represent the combination of differnt flag item. +/// e.g. [Read, Write], [Read], [] are all flag sets. +/// The order of flagItem does not matters. So flag sets with same sets of items should be viewed as the same set. +pub struct FlagSet { + items: Vec, +} + +impl FlagSet { + /// create a new empty flag set + pub fn new() -> Self { + FlagSet { items: Vec::new() } + } + + /// add a flag item + pub fn push_item(&mut self, flag_item: FlagItem) { + self.items.push(flag_item); + } + + /// the flag set string. debug use. + pub fn to_string(&self) -> String { + if self.items.len() == 0 { + return EMPTY_SET_NAME.to_string(); + } + + let mut res = EMPTY_SET_NAME.to_string(); + + for item in self.items.iter() { + let replace_set = format!( + "{}<{}, {}>", + SET_NAME, + item.ident.to_string(), + EMPTY_SET_NAME + ); + res = res.replace(EMPTY_SET_NAME, &replace_set); + } + res + } + + /// the tokens represents the flag set type name + pub fn type_name_tokens(&self) -> TokenStream { + let mut res = quote!(::kxos_typeflags_util::Nil); + + for item in self.items.iter() { + let ident = item.ident.clone(); + + // insert new item as head + let new_res = quote! { + ::kxos_typeflags_util::Cons<#ident, #res> + }; + res = new_res; + } + + res + } + + /// the number of items + pub fn len(&self) -> usize { + self.items.len() + } + + /// the tokens to impl main trait for the current flagset + pub fn impl_main_trait_tokens(&self, type_flags_def: &TypeFlagDef) -> TokenStream { + let trait_ident = type_flags_def.trait_ident(); + let name = self.type_name_tokens(); + let mut all_tokens = quote! ( + impl #trait_ident for #name + ); + all_tokens.append_all(self.inner_tokens(type_flags_def)); + all_tokens + } + + /// the impl main trait inner content + fn inner_tokens(&self, type_flags_def: &TypeFlagDef) -> TokenStream { + let ty = type_flags_def.val_type(); + let item_vals = self.items.iter().map(|item| item.val.clone()); + + // quote seems unable to use string for types. + // So we hardcode all types here. + if item_vals.len() == 0 { + quote!( + { + const BITS: #ty = 0 ; + fn new() -> Self { + ::kxos_typeflags_util::Nil + } + } + ) + } else { + quote!( + { + const BITS: #ty = #(#item_vals)|* ; + fn new() -> Self { + ::kxos_typeflags_util::Cons::new() + } + } + ) + } + } + + /// The token stream inside macro definition. We will generate a token stream for each permutation of items + /// since the user may use arbitrary order of items in macro. + pub fn macro_item_tokens(&self) -> Vec { + let res_type = self.type_name_tokens(); + // We first calculate the full permutations, + let item_permutations = self.items.iter().permutations(self.items.len()); + item_permutations + .map(|flag_items| { + let item_names = flag_items + .into_iter() + .map(|flag_item| flag_item.ident.clone()) + .collect::>(); + quote! ( + (#(#item_names),*) => { #res_type } + ) + }) + .collect() + } +} + +#[derive(Clone)] +pub struct FlagItem { + /// the user provided name + ident: Ident, + /// the user-provided val + val: Expr, +} + +/// generate all possible flag sets +pub fn generate_flag_sets(type_flag_def: &TypeFlagDef) -> Vec { + let flag_items = type_flag_def + .items_iter() + .map(|type_flag_item| { + let ident = type_flag_item.ident(); + let val = type_flag_item.val(); + FlagItem { ident, val } + }) + .collect::>(); + let flag_item_num = flag_items.len(); + let limit = 1 << flag_items.len(); + let mut res = Vec::with_capacity(limit); + + for i in 0..limit { + let mut flag_set = FlagSet::new(); + for j in 0..flag_item_num { + if (i >> j) & 0x1 == 1usize { + flag_set.push_item(flag_items[j].clone()); + } + } + res.push(flag_set); + } + + res +} diff --git a/src/kxos-typeflags/src/lib.rs b/src/kxos-typeflags/src/lib.rs new file mode 100644 index 00000000..9484ec9e --- /dev/null +++ b/src/kxos-typeflags/src/lib.rs @@ -0,0 +1,63 @@ +//!This crate defines the procedural macro typeflags to implement capability for kxos. +//! When using this crate, kxos-typeflags-util should also be added as dependency. +//! This is due to kxos-typeflgas is a proc-macro crate, which is only allowed to export proc-macro interfaces. +//! So we leave the common type-level operations and structures defined in kxos-typeflags-util. +//! +//! type_flag is used to define another declarive macro to define type set. +//! It can be used as the following example. +//! ```rust +//! type_flags! { +//! pub trait RightSet: u32 { +//! struct Read = 1 << 1; +//! struct Write = 1 << 2; +//! } +//! } +//! ``` +//! The code will generate a macro with the name as RightSet, we can use this macro to define typesets with different types. +//! Usage example: +//! ```rust +//! type O = RightSet![]; // Nil +//! type R = RightSet![Read]; // Cons +//! type W = RightSet![Write]; // Cons +//! type RW = RightSet![Read, Write]; // Cons> +//! type WR = RightSet![Write, Read]; // Cons> +//! ``` +//! +//! Test Example +//! ```rust +//! use kxos_typeflags_util::*; +//! assert_eq!(O::BITS, 0); +//! assert_eq!(R::BITS, 2); +//! assert_eq!(W::BITS, 4); +//! assert_eq!(RW::BITS, 6); +//! assert_eq!(WR::BITS, 6); +//! assert_type_same!(SameAsOp, False); +//! assert_type_same!(SameAsOp, True); +//! assert_type_same!(SameAsOp, True); +//! assert_type_same!(SameAsOp, True); +//! assert_type_same!(SetContainOp, False); +//! assert_type_same!(SetContainOp, True); +//! assert_type_same!(SetContainOp, False); +//! assert_type_same!(SetContainOp, True); +//! assert_type_same!(SetIncludeOp, True); +//! assert_type_same!(SetIncludeOp, False); +//! assert_type_same!(SetIncludeOp, True); +//! assert_type_same!(SetIncludeOp, False); +//! ``` + +#![feature(proc_macro_diagnostic)] +#![allow(dead_code)] + +use syn::parse_macro_input; + +use crate::{type_flag::TypeFlagDef, util::expand_type_flag}; + +mod flag_set; +mod type_flag; +mod util; + +#[proc_macro] +pub fn type_flags(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + let type_flags_def = parse_macro_input!(input as TypeFlagDef); + expand_type_flag(&type_flags_def).into() +} diff --git a/src/kxos-typeflags/src/type_flag.rs b/src/kxos-typeflags/src/type_flag.rs new file mode 100644 index 00000000..b9a0d321 --- /dev/null +++ b/src/kxos-typeflags/src/type_flag.rs @@ -0,0 +1,139 @@ +use proc_macro2::TokenStream; +use quote::quote; +use syn::{ + braced, + parse::{Parse, ParseStream}, + punctuated::Punctuated, + Expr, Ident, Token, Type, Visibility, +}; + +/// The content inside typeflag macro +pub struct TypeFlagDef { + ident: Ident, + vis: Visibility, + type_: Type, + items: Punctuated, +} + +/// struct item inside typeflag macro +#[derive(Clone)] +pub struct TypeFlagItem { + vis: Visibility, + ident: Ident, + value: Expr, +} + +impl Parse for TypeFlagDef { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let vis: Visibility = input.parse()?; + let _: Token![trait] = input.parse()?; + let ident: Ident = input.parse()?; + let _: Token![:] = input.parse()?; + let type_: Type = input.parse()?; + // read content inside brace + let content; + let _ = braced!(content in input); + let items = content.parse_terminated(TypeFlagItem::parse)?; + + let res = TypeFlagDef { + ident, + vis, + type_, + items, + }; + + Ok(res) + } +} + +impl Parse for TypeFlagItem { + fn parse(input: ParseStream) -> syn::Result { + let vis: Visibility = input.parse()?; + let _: Token![struct] = input.parse()?; + let ident: Ident = input.parse()?; + let _: Token![=] = input.parse()?; + let value: Expr = input.parse()?; + let res = TypeFlagItem { vis, ident, value }; + Ok(res) + } +} + +impl TypeFlagDef { + /// tokens to define the trait + pub fn trait_def_tokens(&self) -> TokenStream { + let vis = self.vis.clone(); + let ident = self.ident.clone(); + let type_ = self.type_.clone(); + quote!( + #vis trait #ident { + const BITS: #type_; + + fn new() -> Self; + } + ) + } + + /// tokens to define all structs + pub fn items_def_tokens(&self) -> Vec { + self.items + .iter() + .map(|type_flag_item| type_flag_item.item_def_tokens()) + .collect() + } + + /// return the items iter + pub fn items_iter(&self) -> syn::punctuated::Iter { + self.items.iter() + } + + /// the number of items + pub fn item_num(&self) -> usize { + self.items.len() + } + + /// get item at specific position + pub fn get_item(&self, index: usize) -> Option { + self.items.iter().nth(index).map(|item| item.clone()) + } + + /// the trait ident + pub fn trait_ident(&self) -> Ident { + self.ident.clone() + } + + /// the val type + pub fn val_type(&self) -> Type { + self.type_.clone() + } +} + +impl TypeFlagItem { + /// the token stream to define such item + fn item_def_tokens(&self) -> TokenStream { + let vis = self.vis.clone(); + let ident = self.ident.clone(); + quote!( + #vis struct #ident {} + ) + } + + /// the item's ident + pub fn ident(&self) -> Ident { + self.ident.clone() + } + + /// the expression of the items's value + pub fn val(&self) -> Expr { + self.value.clone() + } +} + +impl TypeFlagDef { + /// Debug use. Print all item's name. + pub fn debug(&self) { + println!("{}", self.ident.to_string()); + for type_flag_item in &self.items { + println!("{}", type_flag_item.ident.to_string()); + } + } +} diff --git a/src/kxos-typeflags/src/util.rs b/src/kxos-typeflags/src/util.rs new file mode 100644 index 00000000..bea77b2a --- /dev/null +++ b/src/kxos-typeflags/src/util.rs @@ -0,0 +1,95 @@ +use proc_macro2::TokenStream; +use quote::{quote, TokenStreamExt}; + +use crate::{ + flag_set::{generate_flag_sets, FlagSet}, + type_flag::TypeFlagDef, +}; + +pub fn expand_type_flag(type_flags_def: &TypeFlagDef) -> TokenStream { + let mut all_tokens = TokenStream::new(); + let import_util_tokens = import_util(); + all_tokens.append_all(import_util_tokens); + + let trait_and_items_def_tokens = trait_and_items_def(type_flags_def); + all_tokens.append_all(trait_and_items_def_tokens); + + let impl_same_as_tokens = impl_same_as(type_flags_def); + all_tokens.append_all(impl_same_as_tokens); + + let flag_sets = generate_flag_sets(&type_flags_def); + flag_sets.iter().for_each(|flag_set| { + let impl_main_trait_tokens = flag_set.impl_main_trait_tokens(type_flags_def); + all_tokens.append_all(impl_main_trait_tokens); + }); + + let export_declarive_macro_tokens = export_declarive_macro(type_flags_def, &flag_sets); + all_tokens.append_all(export_declarive_macro_tokens); + + all_tokens +} + +/// import crate kxos_typeflags_util +pub fn import_util() -> TokenStream { + quote!( + #[macro_use] + use ::kxos_typeflags_util::*; + ) +} + +/// define the main trait and all items +pub fn trait_and_items_def(type_flags_def: &TypeFlagDef) -> TokenStream { + let mut tokens = TokenStream::new(); + tokens.append_all(type_flags_def.trait_def_tokens()); + for item_def in type_flags_def.items_def_tokens() { + tokens.append_all(item_def); + } + tokens +} + +/// impl SameAs trait for each struct +pub fn impl_same_as(type_flags_def: &TypeFlagDef) -> TokenStream { + let mut all_tokens = TokenStream::new(); + let items_num = type_flags_def.item_num(); + + for i in 0..items_num { + let item1 = type_flags_def.get_item(i).unwrap(); + for j in 0..items_num { + if i == j { + // We don't need to impl SameAs for the same type + continue; + } + let item2 = type_flags_def.get_item(j).unwrap(); + let ident1 = item1.ident(); + let ident2 = item2.ident(); + let tokens = quote!( + impl ::kxos_typeflags_util::SameAs<#ident1> for #ident2 { + type Output = ::kxos_typeflags_util::False; + } + ); + all_tokens.append_all(tokens); + } + } + all_tokens +} + +/// export the declarive macro +pub fn export_declarive_macro(type_flags_def: &TypeFlagDef, flag_sets: &[FlagSet]) -> TokenStream { + let macro_ident = type_flags_def.trait_ident(); + let macro_item_tokens = flag_sets + .iter() + .map(|flag_set| flag_set.macro_item_tokens()) + .fold(Vec::new(), |mut left, mut new_item| { + left.append(&mut new_item); + left + }); + + let tokens = quote!( + #[macro_export] + macro_rules! #macro_ident { + #(#macro_item_tokens);* + } + ); + + tokens +}