1extern crate proc_macro;
62use can_dbc::{ByteOrder, MessageId, Signal, ValueType, DBC};
63use proc_macro2::TokenStream;
64use quote::{quote, TokenStreamExt};
65use std::{collections::BTreeMap, fs::read};
66use syn::{
67 parse_macro_input, Attribute, Data, DeriveInput, Expr, Fields, Ident, Lit,
68 Meta, Result, Type,
69};
70
71struct DeriveData<'a> {
72 #[allow(dead_code)]
74 name: &'a Ident,
75 dbc: can_dbc::DBC,
77 messages: BTreeMap<String, MessageInfo<'a>>,
79}
80
81struct MessageInfo<'a> {
82 ident: &'a Ident,
83 attrs: &'a Vec<Attribute>,
84}
85
86struct SignalFilter {
88 names: Vec<String>,
89}
90
91impl SignalFilter {
92 fn new(message: &MessageInfo) -> Self {
94 let mut names: Vec<String> = vec![];
95 if let Some(attrs) = parse_attr(message.attrs, "dbc_signals") {
96 let list = attrs.split(",");
97 for name in list {
98 let name = name.trim();
99 names.push(name.to_string());
100 }
101 }
102 Self { names }
103 }
104
105 fn use_signal(&self, name: impl Into<String>) -> bool {
108 if self.names.is_empty() {
109 return true;
110 }
111 let name = name.into();
112 self.names.contains(&name)
113 }
114}
115
116struct SignalInfo<'a> {
118 signal: &'a Signal,
119 ident: Ident,
120 ntype: Ident,
121 utype: Ident,
122 start: usize,
123 width: usize,
124 nwidth: usize,
125 scale: f32,
126 signed: bool,
127}
128
129impl<'a> SignalInfo<'a> {
130 fn new(signal: &'a Signal, message: &MessageInfo) -> Self {
131 let name = signal.name();
133 let signed = matches!(signal.value_type(), ValueType::Signed);
134 let width = *signal.signal_size() as usize;
135 let scale = *signal.factor() as f32;
136
137 let nwidth = match width {
139 1 => 1,
140 2..=8 => 8,
141 9..=16 => 16,
142 17..=32 => 32,
143 _ => 64,
144 };
145
146 let utype = if width == 1 {
147 "bool"
148 } else {
149 &format!("{}{}", if signed { "i" } else { "u" }, nwidth)
150 };
151
152 let ntype = if scale == 1.0 { utype } else { "f32" };
154
155 Self {
156 signal,
157 ident: Ident::new(name, message.ident.span()),
158 ntype: Ident::new(ntype, message.ident.span()),
159 utype: Ident::new(utype, message.ident.span()),
160 start: *signal.start_bit() as usize,
161 scale,
162 signed,
163 width,
164 nwidth,
165 }
166 }
167
168 fn gen_bits(&self) -> TokenStream {
170 let low = self.start / 8;
171 let left = self.start % 8;
172 let high = (self.start + self.width - 1) / 8;
173 let right = (self.start + self.width) % 8;
174 let utype = &self.utype;
175 let le = self.signal.byte_order() == &ByteOrder::LittleEndian;
176
177 let mut ts = TokenStream::new();
178 if self.width == self.nwidth && left == 0 {
179 let ext = if le {
181 Ident::new("from_le_bytes", utype.span())
182 } else {
183 Ident::new("from_be_bytes", utype.span())
184 };
185 let tokens = match self.width {
186 8 => quote! {
187 #utype::#ext([pdu[#low]])
188 },
189 16 => quote! {
190 #utype::#ext([pdu[#low],
191 pdu[#low + 1]])
192 },
193 32 => quote! {
194 #utype::#ext([pdu[#low + 0],
195 pdu[#low + 1],
196 pdu[#low + 2],
197 pdu[#low + 3]])
198 },
199 64 => quote! {
204 #utype::#ext([pdu[#low + 0],
205 pdu[#low + 1],
206 pdu[#low + 2],
207 pdu[#low + 3],
208 pdu[#low + 4],
209 pdu[#low + 5],
210 pdu[#low + 6],
211 pdu[#low + 7],
212 ])
213 },
214 _ => unimplemented!(),
215 };
216 ts.append_all(tokens);
217 } else {
218 if le {
219 let count = high - low;
220 for o in 0..=count {
221 let byte = low + o;
222 if o == 0 {
223 ts.append_all(quote! {
225 let v = pdu[#byte] as #utype;
226 });
227 if left != 0 {
228 if count == 0 {
229 ts.append_all(quote! {
230 let v = (v >> #left) & ((1 << #left) - 1);
231 });
232 } else {
233 ts.append_all(quote! {
234 let v = v >> #left;
235 });
236 }
237 }
238 } else {
239 let shift = (o * 8) - left;
240 if o == count && right != 0 {
241 ts.append_all(quote! {
242 let v = v | (((pdu[#byte]
243 & ((1 << #right) - 1))
244 as #utype) << #shift);
245 });
246 } else {
247 ts.append_all(quote! {
248 let v = v | ((pdu[#byte] as #utype) << #shift);
249 });
250 }
251 }
252 }
253 } else {
254 let mut rem = self.width;
256 let mut byte = low;
257 while rem > 0 {
258 if byte == low {
259 ts.append_all(quote! {
261 let v = pdu[#byte] as #utype;
262 });
263 if rem < 8 {
264 let mask = rem - 1;
266 let shift = left + 1 - rem;
267 ts.append_all(quote! {
268 let mask: #utype = (1 << #mask)
269 | ((1 << #mask) - 1);
270 let v = (v >> #shift) & mask;
271 });
272 rem = 0;
273 } else {
274 let mask = left;
276 let shift = rem - left - 1;
277 if mask < 7 {
278 ts.append_all(quote! {
279 let mask: #utype = (1 << #mask)
280 | ((1 << #mask) - 1);
281 let v = (v & mask) << #shift;
282 });
283 } else {
284 ts.append_all(quote! {
285 let v = v << #shift;
286 });
287 }
288 rem -= left + 1;
289 }
290 byte += 1;
291 } else {
292 if rem < 8 {
293 let shift = 8 - rem;
295 ts.append_all(quote! {
296 let v = v |
297 ((pdu[#byte] as #utype) >> #shift);
298 });
299 rem = 0;
300 } else {
301 rem -= 8;
302 ts.append_all(quote! {
303 let v = v |
304 ((pdu[#byte] as #utype) << #rem);
305 });
306 byte += 1;
307 }
308 };
309 }
310 }
311 if self.signed && self.width < self.nwidth {
314 let mask = self.width - 1;
315 ts.append_all(quote! {
316 let mask: #utype = (1 << #mask);
317 let v = if (v & mask) != 0 {
318 let mask = mask | (mask - 1);
319 v | !mask
320 } else {
321 v
322 };
323 });
324 }
325 ts.append_all(quote! { v });
326 }
327 quote! { { #ts } }
328 }
329
330 fn gen_decoder(&self) -> TokenStream {
331 let name = &self.ident;
332 if self.width == 1 {
333 let byte = self.start / 8;
335 let bit = self.start % 8;
336 quote! {
337 self.#name = (pdu[#byte] & (1 << #bit)) != 0;
338 }
339 } else {
340 let value = self.gen_bits();
341 let ntype = &self.ntype;
342 if !self.is_float() {
343 quote! {
344 self.#name = #value as #ntype;
345 }
346 } else {
347 let scale = self.scale;
348 let offset = *self.signal.offset() as f32;
349 quote! {
350 self.#name = ((#value as f32) * #scale) + #offset;
351 }
352 }
353 }
354 }
355
356 fn is_float(&self) -> bool {
357 self.scale != 1.0
358 }
359}
360
361impl<'a> DeriveData<'a> {
362 fn from(input: &'a DeriveInput) -> Result<Self> {
363 let dbc_file = parse_attr(&input.attrs, "dbc_file")
365 .expect("No DBC file specified");
366 let contents = read(&dbc_file).expect("Could not read DBC");
367 let dbc = DBC::from_slice(&contents).expect("Could not parse DBC");
368
369 let mut messages: BTreeMap<String, MessageInfo<'_>> =
371 Default::default();
372 match &input.data {
373 Data::Struct(data) => match &data.fields {
374 Fields::Named(fields) => {
375 for field in &fields.named {
376 let stype = match &field.ty {
377 Type::Path(v) => v,
378 _ => unimplemented!(),
379 };
380 let ident = &stype.path.segments[0].ident;
381 messages.insert(
382 ident.to_string(),
383 MessageInfo {
384 ident,
385 attrs: &field.attrs,
386 },
387 );
388 }
389 }
390 Fields::Unnamed(_) | Fields::Unit => unimplemented!(),
391 },
392 _ => unimplemented!(),
393 }
394
395 Ok(Self {
396 name: &input.ident,
397 dbc,
398 messages,
399 })
400 }
401
402 fn build(self) -> TokenStream {
403 let mut out = TokenStream::new();
404
405 for (name, message) in self.messages.iter() {
406 let m = self
407 .dbc
408 .messages()
409 .iter()
410 .find(|m| *m.message_name() == *name)
411 .unwrap_or_else(|| panic!("Unknown message {name}"));
412
413 let filter = SignalFilter::new(message);
414
415 let mut signals: Vec<Ident> = vec![];
416 let mut types: Vec<Ident> = vec![];
417 let mut infos: Vec<SignalInfo> = vec![];
418 for s in m.signals().iter() {
419 if !filter.use_signal(s.name()) {
420 continue;
421 }
422
423 let signal = SignalInfo::new(s, message);
424 signals.push(signal.ident.clone());
425 types.push(signal.ntype.clone());
426 infos.push(signal);
427 }
428
429 let (id, extended) = match *m.message_id() {
430 MessageId::Standard(id) => (id as u32, false),
431 MessageId::Extended(id) => (id, true),
432 };
433
434 let dlc = *m.message_size() as usize;
435 let dlc8 = dlc as u8;
436 let ident = message.ident;
437
438 let mut decoders = TokenStream::new();
440 for info in infos.iter() {
441 decoders.append_all(info.gen_decoder());
442 }
443
444 out.append_all(quote! {
445 #[allow(dead_code)]
446 #[allow(non_snake_case)]
447 #[derive(Default)]
448 pub struct #ident {
449 #(
450 pub #signals: #types
451 ),*
452 }
453
454 impl #ident {
455 const ID: u32 = #id;
456 const DLC: u8 = #dlc8;
457 const EXTENDED: bool = #extended;
458
459 pub fn decode(&mut self, pdu: &[u8])
460 -> bool {
461 if pdu.len() != #dlc {
462 return false
463 }
464 #decoders
465 true
466 }
467 }
468 });
469 }
470 out
471 }
472}
473
474#[proc_macro_derive(DbcData, attributes(dbc_file, dbc_signals))]
475pub fn dbc_data_derive(
476 input: proc_macro::TokenStream,
477) -> proc_macro::TokenStream {
478 derive_data(&parse_macro_input!(input as DeriveInput))
479 .unwrap_or_else(|err| err.to_compile_error())
480 .into()
481}
482
483fn derive_data(input: &DeriveInput) -> Result<TokenStream> {
484 Ok(DeriveData::from(input)?.build())
485}
486
487fn parse_attr(attrs: &[Attribute], name: &str) -> Option<String> {
488 let attr = attrs
489 .iter()
490 .filter(|a| {
491 a.path().segments.len() == 1 && a.path().segments[0].ident == name
492 })
493 .nth(0)?;
494
495 let expr = match &attr.meta {
496 Meta::NameValue(n) => Some(&n.value),
497 _ => None,
498 };
499
500 match &expr {
501 Some(Expr::Lit(e)) => match &e.lit {
502 Lit::Str(s) => Some(s.value()),
503 _ => None,
504 },
505 _ => None,
506 }
507}