#![allow(unused)]
use darling::{FromDeriveInput, FromField};
use proc_macro2::TokenStream;
use quote::quote;
use syn::{DataStruct, DeriveInput, Field, Generics, Ident, Type};
#[derive(FromDeriveInput, Default, Debug)]
#[darling(attributes(behavior))]
struct StructAttributes {
#[darling(default)]
no_create: bool,
#[darling(default)]
no_register: bool,
#[darling(default)]
no_register_with: bool,
#[darling(default)]
groot2: bool,
}
#[derive(FromField, Default, Debug)]
#[darling(attributes(behavior))]
struct StructFieldAttributes {
#[darling(default)]
parameter: bool,
default: Option<syn::Expr>,
#[darling(default)]
portlist: bool,
}
fn collect_field_informations(
data: &mut DataStruct,
) -> (
Vec<Field>, // collects the 'parameter' field definitions
Vec<Ident>, // collects the 'parmeter' names only
Vec<Ident>, // collects the internal defined types
Vec<Ident>, // collects the 'default' fields
Vec<syn::Expr>, // collects the values for 'default' fields
Vec<Ident>, // collects the non 'default' fields
Vec<Ident>, // collects the non 'default' field types
Option<syn::Ident>, // the optional name of the portlist field
) {
let mut parameters = Vec::new();
let mut arguments = Vec::new();
let mut internal_types = Vec::new();
let mut default_idents = Vec::new();
let mut default_values = Vec::new();
let mut no_default_idents = Vec::new();
let mut no_default_types = Vec::new();
let mut portlist_ident = None;
for field in data.fields.iter() {
let attrs = match StructFieldAttributes::from_field(field) {
Ok(value) => value,
Err(err) => panic!("parsing field attributes failed: {err}"),
};
let field_name = field
.ident
.as_ref()
.expect("no field name")
.clone();
let field_type = match field.ty.clone() {
Type::Path(type_path) => {
let ident = if type_path.path.leading_colon.is_none() {
type_path.path.segments[0].ident.clone()
} else {
unreachable!("a Path should always have an Ident")
};
ident
}
_ => unreachable!("found a non Path where only a Path is expected"),
};
if attrs.parameter {
if attrs.portlist {
panic!("portlist attribute cannot be used with any other attribute")
}
let mut param = field.clone();
param.attrs = Vec::new();
param.vis = syn::Visibility::Inherited;
param.mutability = syn::FieldMutability::None;
parameters.push(param);
arguments.push(field_name.clone());
if let Some(default) = attrs.default {
default_idents.push(field_name);
default_values.push(default);
} else {
internal_types.push(field_type.clone());
}
} else if let Some(default) = attrs.default {
if attrs.portlist {
panic!("portlist attribute cannot be used with any other attribute")
}
default_idents.push(field_name);
default_values.push(default);
} else {
if attrs.portlist {
portlist_ident = Some(field_name)
} else {
no_default_idents.push(field_name);
no_default_types.push(field_type.clone());
}
}
}
assert_eq!(parameters.len(), arguments.len());
assert_eq!(parameters.len(), internal_types.len());
assert_eq!(default_idents.len(), default_values.len());
assert_eq!(no_default_idents.len(), no_default_types.len());
(
parameters,
arguments,
internal_types,
default_idents,
default_values,
no_default_idents,
no_default_types,
portlist_ident,
)
}
fn handle_struct(
kind_token: TokenStream,
ident: &Ident,
generics: &Generics,
mut attribs: StructAttributes,
data: &mut DataStruct,
) -> TokenStream {
let derived: TokenStream = "#[automatically_derived]".parse().unwrap();
let no_documentation: TokenStream = "#[allow(missing_docs)]".parse().unwrap();
let (impl_generics, type_generics, where_clause) = generics.split_for_impl();
let (parameters, arguments, types, default_idents, default_values, no_default_idents, no_default_types, portlist_ident) =
collect_field_informations(data);
if parameters.len() == 0 {
attribs.no_register_with = true;
}
let impl_block = if attribs.no_create && attribs.no_register && attribs.no_register_with {
quote! {}
} else {
let fn_create_fn = if attribs.no_create {
quote! {}
} else {
if let Some(portlist_ident) = portlist_ident.clone() {
quote! {
fn create_fn(#(#parameters),*) -> alloc::boxed::Box<behaviortree_core::BehaviorCreationFn> {
alloc::boxed::Box::new(move |blackboard| alloc::boxed::Box::new(Self {
#(#arguments: #arguments.clone(),)*
#portlist_ident : Self::port_collection_init(&blackboard).unwrap(),
#(#default_idents : #default_values,)*
#(#no_default_idents : #no_default_types::default(),)*
}))
}
}
} else {
quote! {
fn create_fn(#(#parameters),*) -> alloc::boxed::Box<behaviortree_core::BehaviorCreationFn> {
alloc::boxed::Box::new(move |blackboard| alloc::boxed::Box::new(Self {
#(#arguments: #arguments.clone(),)*
#(#default_idents : #default_values,)*
#(#no_default_idents : #no_default_types::default(),)*
}))
}
}
}
};
let groot2 = attribs.groot2;
let fn_register = if attribs.no_register {
quote! {}
} else {
quote! {
pub fn register(registry: &mut impl behaviortree_core::behavior_traits::BehaviorRegistry, name: &str) -> Result<(), behaviortree_core::error::Error> {
let bhvr_desc = behaviortree_core::behavior_description::BehaviorDescription::new(name, name, #groot2);
let bhvr_creation_fn = Self::create_fn(#(#types::default()),*);
registry.add_behavior(bhvr_desc, bhvr_creation_fn)
}
}
};
let fn_register_with = if attribs.no_register_with {
quote! {}
} else {
quote! {
pub fn register_with(registry: &mut impl behaviortree_core::behavior_traits::BehaviorRegistry, name: &str, #(#parameters),*) -> Result<(), behaviortree_core::error::Error> {
let bhvr_desc = behaviortree_core::behavior_description::BehaviorDescription::new(name, name, #groot2);
let bhvr_creation_fn = Self::create_fn(#(#arguments),*);
registry.add_behavior(bhvr_desc, bhvr_creation_fn)
}
}
};
quote! {
#derived
#no_documentation
impl #impl_generics #ident #type_generics #where_clause {
#fn_create_fn
#fn_register
#fn_register_with
}
}
};
let provided_ports_block = if let Some(portlist_ident) = portlist_ident.clone() {
quote! {
#derived
#no_documentation
impl #impl_generics dataport::PortCollectionProvider for #ident #type_generics #where_clause {
fn provided_ports(&self) -> &impl dataport::PortCollectionAccessors {
&self.#portlist_ident
}
fn provided_ports_mut(&mut self) -> &mut impl dataport::PortCollectionAccessors {
&mut self.#portlist_ident
}
fn port_collection(&self) -> &impl dataport::PortCollection {
&self.#portlist_ident
}
}
}
} else {
quote! {
#derived
#no_documentation
impl #impl_generics dataport::PortCollectionProvider for #ident #type_generics #where_clause {}
}
};
let portlist = if let Some(portlist_ident) = portlist_ident {
quote! {
fn portlist(&self) -> &dyn dataport::PortList {
&self.#portlist_ident
}
fn portlist_mut(&mut self) -> &mut dyn dataport::PortList {
&mut self.#portlist_ident
}
}
} else {
quote! {
fn portlist(&self) -> &dyn dataport::PortList {
dataport::EMPTY_PORT_ARRAY.get()
}
fn portlist_mut(&mut self) -> &mut dyn dataport::PortList {
dataport::EMPTY_PORT_ARRAY.get_mut()
}
}
};
quote! {
#derived
#no_documentation
impl #impl_generics behaviortree_core::behavior_traits::BehaviorExecution for #ident #type_generics #where_clause {
fn as_any(&self) -> &dyn ::core::any::Any { self }
fn as_any_mut(&mut self) -> &mut dyn ::core::any::Any { self }
fn kind(&self) -> behaviortree_core::behavior_kind::BehaviorKind { #kind_token }
#portlist
}
#impl_block
#provided_ports_block
}
}
pub fn derive_behavior_impl(input: TokenStream, kind_token: TokenStream) -> TokenStream {
match syn::parse2::<DeriveInput>(input) {
Ok(mut derive_input) => {
let ident = &derive_input.ident;
let generics = &derive_input.generics;
let attribs = match StructAttributes::from_derive_input(&derive_input) {
Ok(result) => result,
Err(err) => panic!("parsing derive attributes failed: {err}"),
};
match &mut derive_input.data {
syn::Data::Struct(data) => handle_struct(kind_token, ident, generics, attribs, data),
_ => unimplemented!("only 'structs' are supported by now"),
}
}
Err(err) => panic!("parsing derive input failed: {err}"),
}
}