use std::collections::BTreeMap;
use std::fs::read;
use can_dbc::{AttributeValue, Dbc, MessageId};
use proc_macro2::TokenStream;
use quote::{TokenStreamExt, quote};
use syn::spanned::Spanned;
use syn::{Data, DeriveInput, Fields, Ident, Path, Result};
use crate::signal::SignalInfo;
use crate::{MessageInfo, parse_attr};
pub(crate) struct DeriveData<'a> {
#[allow(dead_code)]
name: &'a Ident,
dbc: Dbc,
messages: BTreeMap<String, MessageInfo<'a>>,
derives: Vec<Path>,
use_long_signals: bool,
}
impl<'a> DeriveData<'a> {
pub(crate) fn from(input: &'a DeriveInput) -> Result<Self> {
let dbc_file = parse_attr(&input.attrs, "dbc_file")
.expect("Missing #[dbc_file = <filename>] attribute");
let contents = read(&dbc_file)
.unwrap_or_else(|_| panic!("Could not read {dbc_file}"));
let contents = str::from_utf8(&contents)
.unwrap_or_else(|_| panic!("Could not read {dbc_file}"));
let dbc = match Dbc::try_from(contents) {
Ok(dbc) => dbc,
Err(e) => {
panic!("Unable to parse {dbc_file}: {e:?}");
}
};
let derives =
if let Some(traits) = parse_attr(&input.attrs, "dbc_derive") {
traits
.split(',')
.filter_map(|t| syn::parse_str::<Path>(t).ok())
.collect()
} else {
vec![]
};
let use_long_signals =
if let Some(yes) = parse_attr(&input.attrs, "dbc_long_signals") {
yes == "true"
} else {
false
};
let mut messages: BTreeMap<String, MessageInfo<'_>> =
BTreeMap::default();
match &input.data {
Data::Struct(data) => match &data.fields {
Fields::Named(fields) => {
for field in &fields.named {
if let Some(info) =
MessageInfo::from_struct_field(&dbc, field)
{
messages.insert(info.ident.to_string(), info);
} else {
return Err(syn::Error::new(
field.span(),
"Unknown message",
));
}
}
}
Fields::Unnamed(_) | Fields::Unit => unimplemented!(),
},
Data::Enum(data) => {
for variant in &data.variants {
if let Some(info) =
MessageInfo::from_enum_variant(&dbc, variant)
{
messages.insert(info.ident.to_string(), info);
} else {
return Err(syn::Error::new(
variant.span(),
"Unknown message",
));
}
}
}
Data::Union(_) => unimplemented!(),
}
Ok(Self {
name: &input.ident,
dbc,
messages,
derives,
use_long_signals,
})
}
fn signal_attr_string(
dbc: &Dbc,
id: MessageId,
signal_name: &str,
attr_name: &str,
) -> Option<String> {
if let Some(attr) = dbc.attribute_values_signal.iter().find(|a| {
a.message_id == id
&& a.signal_name == signal_name
&& a.name == attr_name
}) {
return match &attr.value {
AttributeValue::String(s) => Some(s.clone()),
_ => None,
};
}
None
}
pub(crate) fn build(self) -> TokenStream {
let mut out = TokenStream::new();
for (name, message) in &self.messages {
let m = self
.dbc
.messages
.get(message.index)
.unwrap_or_else(|| panic!("Unknown message {name}"));
let mut signals: Vec<Ident> = vec![];
let mut types: Vec<Ident> = vec![];
let mut docs: Vec<String> = vec![];
let mut infos: Vec<SignalInfo> = vec![];
let mut values = TokenStream::new();
for s in &m.signals {
if !message.use_signal(&s.name) {
continue;
}
let name = if self.use_long_signals
&& let Some(long_name) = Self::signal_attr_string(
&self.dbc,
m.id,
&s.name,
"SystemSignalLongSymbol",
) {
long_name
} else {
s.name.clone()
};
let signal = SignalInfo::new(s, name, message);
signals.push(signal.ident.clone());
types.push(signal.ntype.clone());
docs.push(signal.get_doc_string(&self.dbc, m.id, &mut values));
infos.push(signal);
}
let id = message.id;
let extended = message.extended;
let dlc = m.size as usize;
let dlc8 = dlc as u8;
let ident = message.ident;
let mut decoders = TokenStream::new();
let mut encoders = TokenStream::new();
for info in &infos {
decoders.append_all(info.gen_decoder());
encoders.append_all(info.gen_encoder());
}
let cycle_time = if let Some(c) = message.cycle_time {
quote! {
pub const CYCLE_TIME: usize = #c;
}
} else {
quote! {}
};
let cycle_time_doc = if let Some(c) = message.cycle_time {
&format!(", cycle time {c}ms")
} else {
""
};
let doc = format!(
"{} ID {} (0x{:X}){}",
if extended { "Extended" } else { "Standard" },
id,
id,
cycle_time_doc,
);
out.append_all(quote! {
#[automatically_derived]
#[allow(non_snake_case)]
#[allow(non_camel_case_types)]
#[derive(Default, Copy, Clone)]
});
for t in &self.derives {
out.append_all(quote! {
#[derive(#t)]
});
}
out.append_all(quote! {
#[doc = #doc]
pub struct #ident {
#(
#[doc = #docs]
pub #signals: #types
),*
}
impl #ident {
pub const ID: u32 = #id;
pub const DLC: u8 = #dlc8;
pub const EXTENDED: bool = #extended;
#cycle_time
#values
pub fn decode(&mut self, pdu: &[u8])
-> bool {
if pdu.len() != #dlc {
return false
}
#decoders
true
}
pub fn encode(&self, pdu: &mut [u8])
-> bool {
if pdu.len() != #dlc {
return false
}
#encoders
true
}
}
impl TryFrom<&[u8]> for #ident {
type Error = ();
fn try_from(data: &[u8]) -> Result<Self, Self::Error> {
let mut pdu = Self::default();
if pdu.decode(data) {
Ok(pdu)
} else {
Err(())
}
}
}
});
}
out
}
}