use darling::FromDeriveInput;
use proc_macro2::TokenStream;
use quote::quote;
use syn::DeriveInput;
use crate::attrs::{Ros2FieldOpts, Ros2TypeOpts, parse_fields};
fn generate_field_type_expr(field_type: &syn::Type, field_opts: &Ros2FieldOpts) -> TokenStream {
if let Some(ref ros2_type) = field_opts.ros2_type {
let type_id = match ros2_type.as_str() {
"byte" | "octet" => quote! { ros2_types::FIELD_TYPE_BYTE },
"char" => quote! { ros2_types::FIELD_TYPE_CHAR },
"wstring" => quote! { ros2_types::FIELD_TYPE_WSTRING },
_ => quote! { ros2_types::FIELD_TYPE_UINT8 }, };
if field_opts.sequence {
if let Some(capacity) = field_opts.capacity {
return quote! { ros2_types::types::FieldType::bounded_sequence(#type_id, #capacity) };
} else {
return quote! { ros2_types::types::FieldType::sequence(#type_id) };
}
}
if let syn::Type::Array(array) = field_type {
let len = &array.len;
return quote! { ros2_types::types::FieldType::array(#type_id, #len as u64) };
}
return match ros2_type.as_str() {
"byte" | "octet" => {
quote! { ros2_types::types::FieldType::primitive(ros2_types::FIELD_TYPE_BYTE) }
}
"char" => {
quote! { ros2_types::types::FieldType::primitive(ros2_types::FIELD_TYPE_CHAR) }
}
"wstring" => {
if let Some(capacity) = field_opts.capacity {
quote! { ros2_types::types::FieldType::bounded_wstring(#capacity) }
} else {
quote! { ros2_types::types::FieldType::primitive(ros2_types::FIELD_TYPE_WSTRING) }
}
}
_ => quote! { <#field_type as ros2_types::RosFieldType>::ros_field_type() },
};
}
if field_opts.sequence && field_opts.string {
let string_type_id = if field_opts.string_capacity.is_some() {
quote! { ros2_types::FIELD_TYPE_BOUNDED_STRING }
} else {
quote! { ros2_types::FIELD_TYPE_STRING }
};
let string_cap_val = field_opts.string_capacity.unwrap_or(0);
if let Some(seq_capacity) = field_opts.capacity {
return quote! {
ros2_types::types::FieldType::bounded_sequence_with_string_capacity(
#string_type_id,
#seq_capacity,
#string_cap_val
)
};
} else {
return quote! {
ros2_types::types::FieldType::sequence_with_string_capacity(#string_type_id, #string_cap_val)
};
}
}
if field_opts.sequence && field_opts.wstring {
let wstring_type_id = if field_opts.string_capacity.is_some() {
quote! { ros2_types::FIELD_TYPE_BOUNDED_WSTRING }
} else {
quote! { ros2_types::FIELD_TYPE_WSTRING }
};
let string_cap_val = field_opts.string_capacity.unwrap_or(0);
if let Some(seq_capacity) = field_opts.capacity {
return quote! {
ros2_types::types::FieldType::bounded_sequence_with_string_capacity(
#wstring_type_id,
#seq_capacity,
#string_cap_val
)
};
} else {
return quote! {
ros2_types::types::FieldType::sequence_with_string_capacity(#wstring_type_id, #string_cap_val)
};
}
}
if field_opts.string
&& let Some(capacity) = field_opts.capacity
{
return quote! { ros2_types::types::FieldType::bounded_string(#capacity) };
}
if field_opts.wstring {
if let Some(capacity) = field_opts.capacity {
return quote! { ros2_types::types::FieldType::bounded_wstring(#capacity) };
} else {
return quote! { ros2_types::types::FieldType::primitive(ros2_types::FIELD_TYPE_WSTRING) };
}
}
if field_opts.sequence
&& let Some(capacity) = field_opts.capacity
{
if let syn::Type::Path(type_path) = field_type
&& let Some(segment) = type_path.path.segments.last()
&& let syn::PathArguments::AngleBracketed(args) = &segment.arguments
&& let Some(syn::GenericArgument::Type(inner_ty)) = args.args.first()
{
return quote! {
{
let inner = <#inner_ty as ros2_types::RosFieldType>::ros_field_type();
if inner.type_id == ros2_types::FIELD_TYPE_NESTED_TYPE {
ros2_types::types::FieldType::nested_bounded_sequence(&inner.nested_type_name, #capacity)
} else {
ros2_types::types::FieldType::bounded_sequence(inner.type_id, #capacity)
}
}
};
}
return quote! { <#field_type as ros2_types::RosFieldType>::ros_field_type() };
}
quote! { <#field_type as ros2_types::RosFieldType>::ros_field_type() }
}
fn generate_referenced_types_expr(
field_type: &syn::Type,
field_opts: &Ros2FieldOpts,
) -> TokenStream {
if field_opts.string || field_opts.wstring {
return quote! { Vec::new() };
}
if field_opts.ros2_type.is_some() {
return quote! { Vec::new() };
}
quote! { <#field_type as ros2_types::RosFieldType>::referenced_types() }
}
pub fn derive_type_description_impl(input: DeriveInput) -> Result<TokenStream, syn::Error> {
let opts = Ros2TypeOpts::from_derive_input(&input)
.map_err(|e| syn::Error::new_spanned(&input, e.to_string()))?;
let name = &opts.ident;
let package = &opts.package;
let interface_type = &opts.interface_type;
let generics = &opts.generics;
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
let type_name = format!("{}/{}/{}", package, interface_type, name);
let field_opts = parse_fields(&input)?;
let field_conversions: Vec<_> = field_opts
.iter()
.map(|f| {
let field_name_raw = f.ident.as_ref().unwrap().to_string();
let field_name = field_name_raw.strip_prefix("r#").unwrap_or(&field_name_raw);
let field_type = &f.ty;
let field_type_expr = generate_field_type_expr(field_type, f);
if let Some(ref default_value) = f.default {
quote! {
ros2_types::types::Field::with_default(
#field_name,
#field_type_expr,
#default_value
)
}
} else {
quote! {
ros2_types::types::Field::new(
#field_name,
#field_type_expr
)
}
}
})
.collect();
let fields_vec = if field_opts.is_empty() {
quote! {
vec![
ros2_types::types::Field::new(
"structure_needs_at_least_one_member",
ros2_types::types::FieldType::primitive(ros2_types::FIELD_TYPE_UINT8)
)
]
}
} else {
quote! {
vec![
#(#field_conversions),*
]
}
};
let referenced_types: Vec<_> = field_opts
.iter()
.map(|f| {
let field_type = &f.ty;
generate_referenced_types_expr(field_type, f)
})
.collect();
let expanded = quote! {
impl #impl_generics ros2_types::TypeDescription for #name #ty_generics #where_clause {
fn type_description() -> ros2_types::types::TypeDescriptionMsg {
let type_desc = ros2_types::types::IndividualTypeDescription::new(
#type_name,
#fields_vec
);
let nested_collections: Vec<Vec<ros2_types::types::IndividualTypeDescription>> = vec![#(#referenced_types),*];
let all_refs: Vec<ros2_types::types::IndividualTypeDescription> = nested_collections.into_iter().flatten().collect();
let mut seen = std::collections::HashSet::new();
let mut unique_refs = Vec::new();
for ref_desc in all_refs {
if seen.insert(ref_desc.type_name.clone()) {
unique_refs.push(ref_desc);
}
}
ros2_types::types::TypeDescriptionMsg::new(type_desc, unique_refs)
}
fn message_type_name() -> ros2_types::MessageTypeName {
ros2_types::MessageTypeName::new(
#interface_type,
#package,
stringify!(#name)
)
}
}
impl #impl_generics ros2_types::RosFieldType for #name #ty_generics #where_clause {
fn ros_field_type() -> ros2_types::types::FieldType {
ros2_types::types::FieldType::nested(#type_name)
}
fn referenced_types() -> Vec<ros2_types::types::IndividualTypeDescription> {
let desc = <Self as ros2_types::TypeDescription>::type_description();
let mut types = vec![desc.type_description];
types.extend(desc.referenced_type_descriptions);
types
}
}
};
Ok(expanded)
}