zerodds_cdr_derive/
lib.rs1#![allow(clippy::expect_used)]
29
30extern crate proc_macro;
31
32use proc_macro::TokenStream;
33use quote::{ToTokens, quote};
34use syn::{Attribute, Data, DeriveInput, Fields, Lit, Meta, parse_macro_input};
35
36#[proc_macro_derive(DdsType, attributes(dds))]
38pub fn derive_dds_type(input: TokenStream) -> TokenStream {
39 let ast = parse_macro_input!(input as DeriveInput);
40 expand(&ast).unwrap_or_else(|e| e.to_compile_error()).into()
41}
42
43fn expand(ast: &DeriveInput) -> syn::Result<proc_macro2::TokenStream> {
44 let name = &ast.ident;
45 let name_str = name.to_string();
46
47 let Data::Struct(data) = &ast.data else {
48 return Err(syn::Error::new_spanned(
49 ast,
50 "DdsType derive supports plain structs only",
51 ));
52 };
53
54 let fields = match &data.fields {
55 Fields::Named(named) => named.named.iter().collect::<Vec<_>>(),
56 _ => {
57 return Err(syn::Error::new_spanned(
58 &data.fields,
59 "DdsType derive supports named fields only",
60 ));
61 }
62 };
63
64 let opts = parse_struct_opts(&ast.attrs)?;
65 let type_name = opts.type_name.unwrap_or(name_str);
66
67 let mut keyed_fields = Vec::new();
69 let mut all_fields = Vec::new();
70 for f in &fields {
71 let opt = parse_field_opts(&f.attrs)?;
72 let ident = f.ident.as_ref().expect("named field has ident");
73 all_fields.push(ident.clone());
74 if opt.key {
75 keyed_fields.push(ident.clone());
76 }
77 }
78 let is_keyed = !keyed_fields.is_empty();
79
80 let encode_field_lines = all_fields.iter().map(|ident| {
81 quote! {
82 ::zerodds_cdr::CdrEncode::encode(&self.#ident, &mut writer)?;
83 }
84 });
85 let decode_field_lines = all_fields.iter().map(|ident| {
86 quote! {
87 #ident: ::zerodds_cdr::CdrDecode::decode(&mut reader)?,
88 }
89 });
90 let key_field_lines = keyed_fields.iter().map(|ident| {
94 quote! {
95 {
96 let mut __bw = ::zerodds_cdr::BufferWriter::new(
97 ::zerodds_cdr::Endianness::Big,
98 );
99 ::zerodds_cdr::CdrEncode::encode(&self.#ident, &mut __bw)?;
100 holder.write_bytes(&__bw.into_bytes());
101 }
102 }
103 });
104
105 let key_holder_method = if is_keyed {
106 quote! {
107 fn encode_key_holder_be(
108 &self,
109 holder: &mut ::zerodds_cdr::PlainCdr2BeKeyHolder,
110 ) {
111 let _ = (|| -> ::core::result::Result<(), ::zerodds_cdr::EncodeError> {
112 #( #key_field_lines )*
113 ::core::result::Result::Ok(())
114 })();
115 }
116 }
117 } else {
118 quote! {}
119 };
120
121 let extensibility_const = if is_keyed {
122 quote! {
123 const EXTENSIBILITY: ::zerodds_dcps::Extensibility =
124 ::zerodds_dcps::Extensibility::Final;
125 const HAS_KEY: bool = true;
126 }
127 } else {
128 quote! {
129 const EXTENSIBILITY: ::zerodds_dcps::Extensibility =
130 ::zerodds_dcps::Extensibility::Final;
131 }
132 };
133
134 let expanded = quote! {
135 impl ::zerodds_dcps::DdsType for #name {
136 const TYPE_NAME: &'static str = #type_name;
137 #extensibility_const
138
139 fn encode(&self, out: &mut ::std::vec::Vec<u8>)
140 -> ::core::result::Result<(), ::zerodds_dcps::EncodeError>
141 {
142 let mut writer = ::zerodds_cdr::BufferWriter::new(
143 ::zerodds_cdr::Endianness::Little,
144 );
145 #( #encode_field_lines )*
146 out.extend_from_slice(&writer.into_bytes());
147 ::core::result::Result::Ok(())
148 }
149
150 fn decode(bytes: &[u8])
151 -> ::core::result::Result<Self, ::zerodds_dcps::DecodeError>
152 {
153 let mut reader = ::zerodds_cdr::BufferReader::new(
154 bytes,
155 ::zerodds_cdr::Endianness::Little,
156 );
157 ::core::result::Result::Ok(Self {
158 #( #decode_field_lines )*
159 })
160 }
161
162 #key_holder_method
163 }
164 };
165
166 Ok(expanded)
167}
168
169#[derive(Default)]
170struct StructOpts {
171 type_name: Option<String>,
172}
173
174fn parse_struct_opts(attrs: &[Attribute]) -> syn::Result<StructOpts> {
175 let mut out = StructOpts::default();
176 for attr in attrs {
177 if !attr.path().is_ident("dds") {
178 continue;
179 }
180 attr.parse_nested_meta(|meta| {
181 if meta.path.is_ident("type_name") {
182 let v: Lit = meta.value()?.parse()?;
183 if let Lit::Str(s) = v {
184 out.type_name = Some(s.value());
185 }
186 }
187 Ok(())
188 })?;
189 }
190 Ok(out)
191}
192
193#[derive(Default)]
194struct FieldOpts {
195 key: bool,
196}
197
198fn parse_field_opts(attrs: &[Attribute]) -> syn::Result<FieldOpts> {
199 let mut out = FieldOpts::default();
200 for attr in attrs {
201 if !attr.path().is_ident("dds") {
202 continue;
203 }
204 if let Meta::List(list) = &attr.meta {
205 let toks = list.tokens.clone().into_token_stream().to_string();
206 if toks.split(',').any(|t| t.trim() == "key") {
207 out.key = true;
208 }
209 }
210 }
211 Ok(out)
212}