use std::borrow::Cow;
use std::cmp::Ordering;
use std::collections::HashMap;
use crate::svd::{
array::names, Cluster, ClusterInfo, DeriveFrom, DimElement, Peripheral, Register,
RegisterCluster,
};
use log::{debug, trace, warn};
use proc_macro2::{Ident, Punct, Spacing, Span, TokenStream};
use quote::{quote, ToTokens};
use syn::{parse_str, Token};
use crate::util::{
self, handle_cluster_error, handle_reg_error, unsuffixed, Config, FullName,
ToSanitizedSnakeCase, ToSanitizedUpperCase, BITS_PER_BYTE,
};
use anyhow::{anyhow, bail, Context, Result};
use crate::generate::register;
pub fn render(
p_original: &Peripheral,
all_peripherals: &[Peripheral],
config: &Config,
) -> Result<TokenStream> {
let mut out = TokenStream::new();
let p_derivedfrom = p_original
.derived_from
.as_ref()
.and_then(|s| all_peripherals.iter().find(|x| x.name == *s));
let p_merged = p_derivedfrom.map(|ancestor| p_original.derive_from(ancestor));
let p = p_merged.as_ref().unwrap_or(p_original);
if let (Some(df), None) = (p_original.derived_from.as_ref(), &p_derivedfrom) {
return Err(anyhow!(
"Couldn't find derivedFrom original: {} for {}, skipping",
df,
p_original.name
));
}
let name = util::name_of(p, config.ignore_groups);
let span = Span::call_site();
let name_str = name.to_sanitized_upper_case();
let name_pc = Ident::new(&name_str, span);
let address = util::hex(p.base_address as u64);
let description = util::respace(p.description.as_ref().unwrap_or(&p.name));
let name_sc = Ident::new(&name.to_sanitized_snake_case(), span);
let (derive_regs, base) = if let (Some(df), None) = (p_derivedfrom, &p_original.registers) {
(true, Ident::new(&df.name.to_sanitized_snake_case(), span))
} else {
(false, name_sc.clone())
};
match p_original {
Peripheral::Array(p, dim) => {
let names: Vec<Cow<str>> = names(p, dim).map(|n| n.into()).collect();
let names_str = names.iter().map(|n| n.to_sanitized_upper_case());
let names_pc = names_str.clone().map(|n| Ident::new(&n, span));
let addresses =
(0..=dim.dim).map(|i| util::hex(p.base_address + (i * dim.dim_increment) as u64));
out.extend(quote! {
#(
#[doc = #description]
pub struct #names_pc { _marker: PhantomData<*const ()> }
unsafe impl Send for #names_pc {}
impl #names_pc {
pub const PTR: *const #base::RegisterBlock = #addresses as *const _;
#[inline(always)]
pub const fn ptr() -> *const #base::RegisterBlock {
Self::PTR
}
}
impl Deref for #names_pc {
type Target = #base::RegisterBlock;
#[inline(always)]
fn deref(&self) -> &Self::Target {
unsafe { &*Self::PTR }
}
}
impl core::fmt::Debug for #names_pc {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
f.debug_struct(#names_str).finish()
}
}
)*
});
}
_ => {
out.extend(quote! {
#[doc = #description]
pub struct #name_pc { _marker: PhantomData<*const ()> }
unsafe impl Send for #name_pc {}
impl #name_pc {
pub const PTR: *const #base::RegisterBlock = #address as *const _;
#[inline(always)]
pub const fn ptr() -> *const #base::RegisterBlock {
Self::PTR
}
}
impl Deref for #name_pc {
type Target = #base::RegisterBlock;
#[inline(always)]
fn deref(&self) -> &Self::Target {
unsafe { &*Self::PTR }
}
}
impl core::fmt::Debug for #name_pc {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
f.debug_struct(#name_str).finish()
}
}
});
}
}
if derive_regs {
out.extend(quote! {
#[doc = #description]
pub use #base as #name_sc;
});
return Ok(out);
}
let ercs_in = p.registers.as_ref().map(|x| x.as_ref()).unwrap_or(&[][..]);
let mut erc_map = HashMap::new();
for erc in ercs_in {
erc_map.insert(util::erc_name(erc), erc);
}
let mut ercs = Vec::with_capacity(ercs_in.len());
for erc in ercs_in {
ercs.push(derive_register_cluster(erc, &erc_map)?.into_owned());
}
let registers: &[&Register] = &util::only_registers(&ercs)[..];
let clusters = util::only_clusters(&ercs);
if registers.is_empty() && clusters.is_empty() {
return Ok(TokenStream::new());
}
debug!(
"Pushing {} register or cluster blocks into output",
ercs.len()
);
let mut mod_items = TokenStream::new();
mod_items.extend(register_or_cluster_block(&ercs, None, config)?);
debug!("Pushing cluster information into output");
for c in &clusters {
trace!("Cluster: {}", c.name);
mod_items.extend(cluster_block(c, p, all_peripherals, config)?);
}
debug!("Pushing register information into output");
for reg in registers {
trace!("Register: {}", reg.name);
match register::render(reg, registers, p, all_peripherals, config) {
Ok(rendered_reg) => mod_items.extend(rendered_reg),
Err(e) => {
let res: Result<TokenStream> = Err(e);
return handle_reg_error("Error rendering register", *reg, res);
}
};
}
let description = util::escape_brackets(
util::respace(p.description.as_ref().unwrap_or(&name.as_ref().to_owned())).as_ref(),
);
let open = Punct::new('{', Spacing::Alone);
let close = Punct::new('}', Spacing::Alone);
out.extend(quote! {
#[doc = #description]
pub mod #name_sc #open
});
out.extend(mod_items);
close.to_tokens(&mut out);
Ok(out)
}
fn derive_register_cluster<'a>(
erc: &'a RegisterCluster,
erc_map: &'a HashMap<&'a String, &'a RegisterCluster>,
) -> Result<Cow<'a, RegisterCluster>> {
Ok(if let Some(derived) = util::erc_derived_from(erc) {
let ancestor = erc_map.get(derived).ok_or_else(|| {
anyhow!(
"register/cluster {} derivedFrom missing register/cluster {}",
util::erc_name(erc),
derived
)
})?;
let ancestor = derive_register_cluster(ancestor, erc_map)?;
use RegisterCluster::*;
match (erc, ancestor.as_ref()) {
(Register(reg), Register(other_reg)) => {
Cow::Owned(Register(reg.derive_from(other_reg)))
}
(Cluster(cluster), Cluster(other_cluster)) => {
Cow::Owned(Cluster(cluster.derive_from(other_cluster)))
}
_ => {
return Err(anyhow!(
"{} can't be derived from {}",
util::erc_name(erc),
util::erc_name(&ancestor)
));
}
}
} else {
Cow::Borrowed(erc)
})
}
#[derive(Clone, Debug)]
struct RegisterBlockField {
field: syn::Field,
description: String,
offset: u32,
size: u32,
accessors: Option<TokenStream>,
}
#[derive(Clone, Debug)]
struct Region {
rbfs: Vec<RegisterBlockField>,
offset: u32,
end: u32,
pub ident: Option<String>,
}
impl Region {
fn shortest_ident(&self) -> Option<String> {
let mut idents: Vec<_> = self
.rbfs
.iter()
.filter_map(|f| f.field.ident.as_ref().map(|ident| ident.to_string()))
.collect();
if idents.is_empty() {
return None;
}
idents.sort_by(|a, b| {
match a.len().cmp(&b.len()) {
Ordering::Equal => a.cmp(b),
cmp => cmp,
}
});
Some(idents[0].to_owned())
}
fn common_ident(&self) -> Option<String> {
fn split_keep(text: &str) -> Vec<&str> {
let mut result = Vec::new();
let mut last = 0;
for (index, matched) in
text.match_indices(|c: char| c.is_numeric() || !c.is_alphabetic())
{
if last != index {
result.push(&text[last..index]);
}
result.push(matched);
last = index + matched.len();
}
if last < text.len() {
result.push(&text[last..]);
}
result
}
let idents: Vec<_> = self
.rbfs
.iter()
.filter_map(|f| f.field.ident.as_ref().map(|ident| ident.to_string()))
.collect();
if idents.is_empty() {
return None;
}
let x: Vec<_> = idents.iter().map(|i| split_keep(i)).collect();
let mut index = 0;
let first = &x[0];
'outer: while index < first.len() {
for ident_match in x.iter().skip(1) {
if let Some(match_) = ident_match.get(index) {
if match_ != &first[index] {
break 'outer;
}
} else {
break 'outer;
}
}
index += 1;
}
if index <= 1 {
None
} else {
Some(match first.get(index) {
Some(elem) if elem.chars().all(|c| c.is_numeric()) => {
first.iter().take(index).cloned().collect()
}
_ => first.iter().take(index - 1).cloned().collect(),
})
}
}
fn compute_ident(&self) -> Option<String> {
if let Some(ident) = self.common_ident() {
Some(ident)
} else {
self.shortest_ident()
}
}
fn is_union(&self) -> bool {
self.rbfs.len() > 1
}
}
#[derive(Default, Debug)]
struct FieldRegions {
regions: Vec<Region>,
}
impl FieldRegions {
fn add(&mut self, rbf: &RegisterBlockField) -> Result<()> {
let mut indices = Vec::new();
let rbf_start = rbf.offset;
let rbf_end = rbf_start + (rbf.size + BITS_PER_BYTE - 1) / BITS_PER_BYTE;
let mut new_region = Region {
rbfs: vec![rbf.clone()],
offset: rbf.offset,
end: rbf_end,
ident: None,
};
for (idx, f) in self.regions.iter_mut().enumerate() {
let f_start = f.offset;
let f_end = f.end;
let begin = f_start.max(rbf_start);
let end = f_end.min(rbf_end);
if end > begin {
indices.push(idx);
new_region.offset = new_region.offset.min(f_start);
new_region.end = new_region.end.max(f_end);
new_region.rbfs.append(&mut f.rbfs);
}
}
for idx in indices.iter().rev() {
self.regions.remove(*idx);
}
new_region.rbfs.sort_by_key(|f| f.offset);
let idx = self
.regions
.binary_search_by_key(&new_region.offset, |r| r.offset);
match idx {
Ok(idx) => {
bail!(
"we shouldn't exist in the vec, but are at idx {} {:#?}\n{:#?}",
idx,
new_region,
self.regions
);
}
Err(idx) => self.regions.insert(idx, new_region),
};
Ok(())
}
pub fn resolve_idents(&mut self) -> Result<()> {
let idents: Vec<_> = {
self.regions
.iter_mut()
.filter(|r| r.rbfs.len() > 1)
.map(|r| {
r.ident = r.compute_ident();
r.ident.clone()
})
.collect()
};
self.regions
.iter_mut()
.filter(|r| r.ident.is_some())
.filter(|r| {
r.rbfs.len() > 1 && (idents.iter().filter(|&ident| ident == &r.ident).count() > 1)
})
.for_each(|r| {
let new_ident = r.shortest_ident();
warn!(
"Found type name conflict with region {:?}, renamed to {:?}",
r.ident, new_ident
);
r.ident = new_ident;
});
Ok(())
}
}
fn make_comment(size: u32, offset: u32, description: &str) -> String {
if size > 32 {
format!(
"0x{:02x}..0x{:02x} - {}",
offset,
offset + size / 8,
util::escape_brackets(&util::respace(description)),
)
} else {
format!(
"0x{:02x} - {}",
offset,
util::escape_brackets(&util::respace(description)),
)
}
}
fn register_or_cluster_block(
ercs: &[RegisterCluster],
name: Option<&str>,
config: &Config,
) -> Result<TokenStream> {
let mut rbfs = TokenStream::new();
let mut accessors = TokenStream::new();
let ercs_expanded =
expand(ercs, name, config).with_context(|| "Could not expand register or cluster block")?;
let mut regions = FieldRegions::default();
for reg_block_field in &ercs_expanded {
regions.add(reg_block_field)?;
if let Some(ts) = ®_block_field.accessors {
accessors.extend(ts.clone());
}
}
regions.resolve_idents()?;
let mut last_end = 0;
let span = Span::call_site();
for (i, region) in regions.regions.iter().enumerate() {
let pad = region.offset - last_end;
if pad != 0 {
let name = Ident::new(&format!("_reserved{}", i), span);
let pad = util::hex(pad as u64);
rbfs.extend(quote! {
#name : [u8; #pad],
});
}
let mut region_rbfs = TokenStream::new();
let is_region_a_union = region.is_union();
for reg_block_field in ®ion.rbfs {
let comment = make_comment(
reg_block_field.size,
reg_block_field.offset,
®_block_field.description,
);
if is_region_a_union {
let name = ®_block_field.field.ident;
let ty = ®_block_field.field.ty;
let offset = reg_block_field.offset as usize;
accessors.extend(quote! {
#[doc = #comment]
#[inline(always)]
pub fn #name(&self) -> &#ty {
unsafe {
&*(((self as *const Self) as *const u8).add(#offset) as *const #ty)
}
}
});
} else {
region_rbfs.extend(quote! {
#[doc = #comment]
});
reg_block_field.field.to_tokens(&mut region_rbfs);
Punct::new(',', Spacing::Alone).to_tokens(&mut region_rbfs);
}
}
if !is_region_a_union {
rbfs.extend(region_rbfs);
} else {
let name = Ident::new(
&format!(
"_reserved_{}_{}",
i,
region
.compute_ident()
.unwrap_or_else(|| format!("{}_{}", region.offset, region.end))
),
span,
);
let pad = util::hex((region.end - region.offset) as u64);
rbfs.extend(quote! {
#name: [u8; #pad],
})
}
last_end = region.end;
}
let name = Ident::new(
&match name {
Some(name) => name.to_sanitized_upper_case(),
None => "RegisterBlock".into(),
},
span,
);
let accessors = if !accessors.is_empty() {
quote! {
impl #name {
#accessors
}
}
} else {
quote! {}
};
Ok(quote! {
#[repr(C)]
pub struct #name {
#rbfs
}
#accessors
})
}
fn expand(
ercs: &[RegisterCluster],
name: Option<&str>,
config: &Config,
) -> Result<Vec<RegisterBlockField>> {
let mut ercs_expanded = vec![];
debug!("Expanding registers or clusters into Register Block Fields");
for erc in ercs {
match &erc {
RegisterCluster::Register(register) => match expand_register(register, name, config) {
Ok(expanded_reg) => {
trace!("Register: {}", register.name);
ercs_expanded.extend(expanded_reg);
}
Err(e) => {
let res = Err(e);
return handle_reg_error("Error expanding register", register, res);
}
},
RegisterCluster::Cluster(cluster) => match expand_cluster(cluster, name, config) {
Ok(expanded_cluster) => {
trace!("Cluster: {}", cluster.name);
ercs_expanded.extend(expanded_cluster);
}
Err(e) => {
let res = Err(e);
return handle_cluster_error("Error expanding register cluster", cluster, res);
}
},
};
}
ercs_expanded.sort_by_key(|x| x.offset);
Ok(ercs_expanded)
}
fn cluster_size_in_bits(cluster: &Cluster, config: &Config) -> Result<u32> {
match cluster {
Cluster::Single(info) => cluster_info_size_in_bits(info, config),
Cluster::Array(info, dim) => {
if dim.dim == 0 {
return Ok(0); }
let last_offset = (dim.dim - 1) * dim.dim_increment * BITS_PER_BYTE;
let last_size = cluster_info_size_in_bits(info, config);
Ok(last_offset + last_size?)
}
}
}
fn cluster_info_size_in_bits(info: &ClusterInfo, config: &Config) -> Result<u32> {
let mut size = 0;
for c in &info.children {
let end = match c {
RegisterCluster::Register(reg) => {
let reg_size: u32 = expand_register(reg, None, config)?
.iter()
.map(|rbf| rbf.size)
.sum();
(reg.address_offset * BITS_PER_BYTE) + reg_size
}
RegisterCluster::Cluster(clust) => {
(clust.address_offset * BITS_PER_BYTE) + cluster_size_in_bits(clust, config)?
}
};
size = size.max(end);
}
Ok(size)
}
fn expand_cluster(
cluster: &Cluster,
name: Option<&str>,
config: &Config,
) -> Result<Vec<RegisterBlockField>> {
let mut cluster_expanded = vec![];
let cluster_size = cluster_info_size_in_bits(cluster, config)
.with_context(|| format!("Cluster {} has no determinable `size` field", cluster.name))?;
match cluster {
Cluster::Single(info) => cluster_expanded.push(RegisterBlockField {
field: convert_svd_cluster(cluster, name)?,
description: info.description.as_ref().unwrap_or(&info.name).into(),
offset: info.address_offset,
size: cluster_size,
accessors: None,
}),
Cluster::Array(info, array_info) => {
let sequential_addresses =
(array_info.dim == 1) || (cluster_size == array_info.dim_increment * BITS_PER_BYTE);
let sequential_indexes = array_info.dim_index.as_ref().map_or(true, |dim_index| {
dim_index
.iter()
.map(|element| element.parse::<u32>())
.eq((0..array_info.dim).map(Ok))
});
let convert_list = match config.keep_list {
true => match &array_info.dim_name {
Some(dim_name) => dim_name.contains("[%s]"),
None => info.name.contains("[%s]"),
},
false => true,
};
let array_convertible = sequential_addresses && convert_list;
if array_convertible {
if sequential_indexes {
cluster_expanded.push(RegisterBlockField {
field: convert_svd_cluster(cluster, name)?,
description: info.description.as_ref().unwrap_or(&info.name).into(),
offset: info.address_offset,
size: cluster_size * array_info.dim,
accessors: None,
});
} else {
let mut accessors = TokenStream::new();
let nb_name = util::replace_suffix(&info.name, "");
let ty = name_to_ty(&nb_name, name)?;
let nb_name_cs =
Ident::new(&nb_name.to_sanitized_snake_case(), Span::call_site());
let description = info.description.as_ref().unwrap_or(&info.name);
for (i, idx) in array_info.indexes().enumerate() {
let idx_name = Ident::new(
&util::replace_suffix(&info.name, &idx).to_sanitized_snake_case(),
Span::call_site(),
);
let comment = make_comment(
cluster_size,
info.address_offset + (i as u32) * cluster_size / 8,
description,
);
let i = unsuffixed(i as _);
accessors.extend(quote! {
#[doc = #comment]
#[inline(always)]
pub fn #idx_name(&self) -> &#ty {
&self.#nb_name_cs[#i]
}
});
}
cluster_expanded.push(RegisterBlockField {
field: convert_svd_cluster(cluster, name)?,
description: description.into(),
offset: info.address_offset,
size: cluster_size * array_info.dim,
accessors: Some(accessors),
});
}
} else if sequential_indexes && config.const_generic {
cluster_expanded.push(array_proxy(info, array_info, name)?);
} else {
for (field_num, field) in expand_svd_cluster(cluster, name)?.iter().enumerate() {
cluster_expanded.push(RegisterBlockField {
field: field.clone(),
description: info.description.as_ref().unwrap_or(&info.name).into(),
offset: info.address_offset + field_num as u32 * array_info.dim_increment,
size: cluster_size,
accessors: None,
});
}
}
}
}
Ok(cluster_expanded)
}
fn expand_register(
register: &Register,
name: Option<&str>,
config: &Config,
) -> Result<Vec<RegisterBlockField>> {
let mut register_expanded = vec![];
let register_size = register
.properties
.size
.ok_or_else(|| anyhow!("Register {} has no `size` field", register.name))?;
match register {
Register::Single(info) => register_expanded.push(RegisterBlockField {
field: convert_svd_register(register, name, config.ignore_groups)
.with_context(|| "syn error occured")?,
description: info.description.clone().unwrap_or_default(),
offset: info.address_offset,
size: register_size,
accessors: None,
}),
Register::Array(info, array_info) => {
let sequential_addresses = (array_info.dim == 1)
|| (register_size == array_info.dim_increment * BITS_PER_BYTE);
let sequential_indexes = array_info.dim_index.as_ref().map_or(true, |dim_index| {
dim_index
.iter()
.map(|element| element.parse::<u32>())
.eq((0..array_info.dim).map(Ok))
});
let convert_list = match config.keep_list {
true => match &array_info.dim_name {
Some(dim_name) => dim_name.contains("[%s]"),
None => info.name.contains("[%s]"),
},
false => true,
};
let array_convertible = sequential_addresses && convert_list;
if array_convertible {
if sequential_indexes {
register_expanded.push(RegisterBlockField {
field: convert_svd_register(register, name, config.ignore_groups)?,
description: info.description.clone().unwrap_or_default(),
offset: info.address_offset,
size: register_size * array_info.dim,
accessors: None,
});
} else {
let mut accessors = TokenStream::new();
let nb_name = util::replace_suffix(&info.fullname(config.ignore_groups), "");
let ty = name_to_wrapped_ty(&nb_name, name)?;
let nb_name_cs =
Ident::new(&nb_name.to_sanitized_snake_case(), Span::call_site());
let description = info.description.clone().unwrap_or_default();
for (i, idx) in array_info.indexes().enumerate() {
let idx_name = Ident::new(
&util::replace_suffix(&info.fullname(config.ignore_groups), &idx)
.to_sanitized_snake_case(),
Span::call_site(),
);
let comment = make_comment(
register_size,
info.address_offset + (i as u32) * register_size / 8,
&description,
);
let i = unsuffixed(i as _);
accessors.extend(quote! {
#[doc = #comment]
#[inline(always)]
pub fn #idx_name(&self) -> &#ty {
&self.#nb_name_cs[#i]
}
});
}
register_expanded.push(RegisterBlockField {
field: convert_svd_register(register, name, config.ignore_groups)?,
description,
offset: info.address_offset,
size: register_size * array_info.dim,
accessors: Some(accessors),
});
}
} else {
for (field_num, field) in expand_svd_register(register, name, config.ignore_groups)?
.iter()
.enumerate()
{
register_expanded.push(RegisterBlockField {
field: field.clone(),
description: info.description.clone().unwrap_or_default(),
offset: info.address_offset + field_num as u32 * array_info.dim_increment,
size: register_size,
accessors: None,
});
}
}
}
}
Ok(register_expanded)
}
fn cluster_block(
c: &Cluster,
p: &Peripheral,
all_peripherals: &[Peripheral],
config: &Config,
) -> Result<TokenStream> {
let mut mod_items = TokenStream::new();
let description =
util::escape_brackets(util::respace(c.description.as_ref().unwrap_or(&c.name)).as_ref());
let mod_name = util::replace_suffix(
match c {
Cluster::Single(info) => &info.name,
Cluster::Array(info, _ai) => &info.name,
},
"",
);
let name_sc = Ident::new(&mod_name.to_sanitized_snake_case(), Span::call_site());
let reg_block = register_or_cluster_block(&c.children, Some(&mod_name), config)?;
let registers = util::only_registers(&c.children);
for reg in ®isters {
match register::render(reg, ®isters, p, all_peripherals, config) {
Ok(rendered_reg) => mod_items.extend(rendered_reg),
Err(e) => {
let res: Result<TokenStream> = Err(e);
return handle_reg_error(
"Error generating register definition for a register cluster",
*reg,
res,
);
}
};
}
let clusters = util::only_clusters(&c.children);
for c in &clusters {
mod_items.extend(cluster_block(c, p, all_peripherals, config)?);
}
Ok(quote! {
#reg_block
#[doc = #description]
pub mod #name_sc {
#mod_items
}
})
}
fn expand_svd_register(
register: &Register,
name: Option<&str>,
ignore_group: bool,
) -> Result<Vec<syn::Field>> {
let mut out = vec![];
match register {
Register::Single(_info) => out.push(convert_svd_register(register, name, ignore_group)?),
Register::Array(info, array_info) => {
let ty_name = util::replace_suffix(&info.fullname(ignore_group), "");
for idx in array_info.indexes() {
let nb_name = util::replace_suffix(&info.fullname(ignore_group), &idx);
let ty = name_to_wrapped_ty(&ty_name, name)?;
out.push(new_syn_field(&nb_name.to_sanitized_snake_case(), ty));
}
}
}
Ok(out)
}
fn convert_svd_register(
register: &Register,
name: Option<&str>,
ignore_group: bool,
) -> Result<syn::Field> {
Ok(match register {
Register::Single(info) => {
let info_name = info.fullname(ignore_group);
new_syn_field(
&info_name.to_sanitized_snake_case(),
name_to_wrapped_ty(&info_name, name)
.with_context(|| format!("Error converting info name {}", info_name))?,
)
}
Register::Array(info, array_info) => {
let nb_name = util::replace_suffix(&info.fullname(ignore_group), "");
let ty = syn::Type::Array(parse_str::<syn::TypeArray>(&format!(
"[{};{}]",
name_to_wrapped_ty_str(&nb_name, name),
u64::from(array_info.dim)
))?);
new_syn_field(&nb_name.to_sanitized_snake_case(), ty)
}
})
}
fn array_proxy(
info: &ClusterInfo,
array_info: &DimElement,
name: Option<&str>,
) -> Result<RegisterBlockField, syn::Error> {
let ty_name = util::replace_suffix(&info.name, "");
let tys = name_to_ty_str(&ty_name, name);
let ap_path = parse_str::<syn::TypePath>(&format!(
"crate::ArrayProxy<{}, {}, {}>",
tys,
array_info.dim,
util::hex(array_info.dim_increment as u64)
))?;
Ok(RegisterBlockField {
field: new_syn_field(&ty_name.to_sanitized_snake_case(), ap_path.into()),
description: info.description.as_ref().unwrap_or(&info.name).into(),
offset: info.address_offset,
size: 0,
accessors: None,
})
}
fn expand_svd_cluster(
cluster: &Cluster,
name: Option<&str>,
) -> Result<Vec<syn::Field>, syn::Error> {
let mut out = vec![];
match &cluster {
Cluster::Single(_info) => out.push(convert_svd_cluster(cluster, name)?),
Cluster::Array(info, array_info) => {
let ty_name = util::replace_suffix(&info.name, "");
for idx in array_info.indexes() {
let nb_name = util::replace_suffix(&info.name, &idx);
let ty = name_to_ty(&ty_name, name)?;
out.push(new_syn_field(&nb_name.to_sanitized_snake_case(), ty));
}
}
}
Ok(out)
}
fn convert_svd_cluster(cluster: &Cluster, name: Option<&str>) -> Result<syn::Field, syn::Error> {
Ok(match cluster {
Cluster::Single(info) => {
let ty_name = util::replace_suffix(&info.name, "");
let ty = name_to_ty(&ty_name, name)?;
new_syn_field(&info.name.to_sanitized_snake_case(), ty)
}
Cluster::Array(info, array_info) => {
let ty_name = util::replace_suffix(&info.name, "");
let ty = syn::Type::Array(parse_str::<syn::TypeArray>(&format!(
"[{};{}]",
name_to_ty_str(&ty_name, name),
u64::from(array_info.dim)
))?);
new_syn_field(&ty_name.to_sanitized_snake_case(), ty)
}
})
}
fn new_syn_field(ident: &str, ty: syn::Type) -> syn::Field {
let span = Span::call_site();
syn::Field {
ident: Some(Ident::new(ident, span)),
vis: syn::Visibility::Public(syn::VisPublic {
pub_token: Token,
}),
attrs: vec![],
colon_token: Some(Token),
ty,
}
}
fn name_to_ty_str<'a, 'b>(name: &'a str, ns: Option<&'b str>) -> Cow<'a, str> {
if let Some(ns) = ns {
Cow::Owned(
String::from("self::")
+ &ns.to_sanitized_snake_case()
+ "::"
+ &name.to_sanitized_upper_case(),
)
} else {
name.to_sanitized_upper_case()
}
}
fn name_to_ty(name: &str, ns: Option<&str>) -> Result<syn::Type, syn::Error> {
let ident = name_to_ty_str(name, ns);
Ok(syn::Type::Path(parse_str::<syn::TypePath>(&ident)?))
}
fn name_to_wrapped_ty_str(name: &str, ns: Option<&str>) -> String {
if let Some(ns) = ns {
format!(
"crate::Reg<self::{}::{}::{}_SPEC>",
&ns.to_sanitized_snake_case(),
&name.to_sanitized_snake_case(),
&name.to_sanitized_upper_case(),
)
} else {
format!(
"crate::Reg<{}::{}_SPEC>",
&name.to_sanitized_snake_case(),
&name.to_sanitized_upper_case(),
)
}
}
fn name_to_wrapped_ty(name: &str, ns: Option<&str>) -> Result<syn::Type> {
let ident = name_to_wrapped_ty_str(name, ns);
match parse_str::<syn::TypePath>(&ident) {
Ok(path) => Ok(syn::Type::Path(path)),
Err(e) => {
let mut res = Err(e.into());
res = res.with_context(|| {
format!("Determining syn::TypePath from ident \"{}\" failed", ident)
});
res
}
}
}