use std::borrow::Cow;
use crate::quote::{ToTokens, TokenStreamExt};
use crate::svd::{Access, Cluster, Register, RegisterCluster};
use inflections::Inflect;
use proc_macro2::{Ident, Literal, Span, TokenStream};
use crate::errors::*;
pub const BITS_PER_BYTE: u32 = 8;
const BLACKLIST_CHARS: &[char] = &['(', ')', '[', ']', '/', ' ', '-'];
#[derive(Clone, Copy, PartialEq)]
pub enum Target {
CortexM,
Msp430,
RISCV,
None,
}
impl Target {
pub fn parse(s: &str) -> Result<Self> {
Ok(match s {
"cortex-m" => Target::CortexM,
"msp430" => Target::Msp430,
"riscv" => Target::RISCV,
"none" => Target::None,
_ => bail!("unknown target {}", s),
})
}
}
pub trait ToSanitizedPascalCase {
fn to_sanitized_pascal_case(&self) -> Cow<str>;
}
pub trait ToSanitizedUpperCase {
fn to_sanitized_upper_case(&self) -> Cow<str>;
}
pub trait ToSanitizedSnakeCase {
fn to_sanitized_snake_case(&self) -> Cow<str>;
}
impl ToSanitizedSnakeCase for str {
fn to_sanitized_snake_case(&self) -> Cow<str> {
macro_rules! keywords {
($s:expr, $($kw:ident),+,) => {
Cow::from(match &$s.to_lowercase()[..] {
$(stringify!($kw) => concat!(stringify!($kw), "_")),+,
_ => return Cow::from($s.to_snake_case())
})
}
}
let s = self.replace(BLACKLIST_CHARS, "");
match s.chars().next().unwrap_or('\0') {
'0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' => {
Cow::from(format!("_{}", s.to_snake_case()))
}
_ => {
keywords! {
s,
abstract,
alignof,
as,
r#async,
r#await,
become,
box,
break,
const,
continue,
crate,
do,
else,
enum,
extern,
false,
final,
fn,
for,
if,
impl,
in,
let,
loop,
macro,
match,
mod,
move,
mut,
offsetof,
override,
priv,
proc,
pub,
pure,
ref,
return,
self,
sizeof,
static,
struct,
super,
trait,
true,
r#try,
type,
typeof,
unsafe,
unsized,
use,
virtual,
where,
while,
yield,
set_bit,
clear_bit,
bit,
bits,
}
}
}
}
}
impl ToSanitizedUpperCase for str {
fn to_sanitized_upper_case(&self) -> Cow<str> {
let s = self.replace(BLACKLIST_CHARS, "");
match s.chars().next().unwrap_or('\0') {
'0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' => {
Cow::from(format!("_{}", s.to_upper_case()))
}
_ => Cow::from(s.to_upper_case()),
}
}
}
impl ToSanitizedPascalCase for str {
fn to_sanitized_pascal_case(&self) -> Cow<str> {
let s = self.replace(BLACKLIST_CHARS, "");
match s.chars().next().unwrap_or('\0') {
'0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' => {
Cow::from(format!("_{}", s.to_pascal_case()))
}
_ => Cow::from(s.to_pascal_case()),
}
}
}
pub fn respace(s: &str) -> String {
s.split_whitespace().collect::<Vec<_>>().join(" ")
}
pub fn escape_brackets(s: &str) -> String {
s.split('[')
.fold("".to_string(), |acc, x| {
if acc == "" {
x.to_string()
} else if acc.ends_with('\\') {
acc + "[" + &x.to_string()
} else {
acc + "\\[" + &x.to_string()
}
})
.split(']')
.fold("".to_string(), |acc, x| {
if acc == "" {
x.to_string()
} else if acc.ends_with('\\') {
acc + "]" + &x.to_string()
} else {
acc + "\\]" + &x.to_string()
}
})
}
pub fn name_of(register: &Register) -> Cow<str> {
match register {
Register::Single(info) => Cow::from(&info.name),
Register::Array(info, _) => {
if info.name.contains("[%s]") {
info.name.replace("[%s]", "").into()
} else {
info.name.replace("%s", "").into()
}
}
}
}
pub fn access_of(register: &Register) -> Access {
register.access.unwrap_or_else(|| {
if let Some(fields) = ®ister.fields {
if fields.iter().all(|f| f.access == Some(Access::ReadOnly)) {
Access::ReadOnly
} else if fields.iter().all(|f| f.access == Some(Access::WriteOnce)) {
Access::WriteOnce
} else if fields
.iter()
.all(|f| f.access == Some(Access::ReadWriteOnce))
{
Access::ReadWriteOnce
} else if fields
.iter()
.all(|f| f.access == Some(Access::WriteOnly) || f.access == Some(Access::WriteOnce))
{
Access::WriteOnly
} else {
Access::ReadWrite
}
} else {
Access::ReadWrite
}
})
}
pub fn hex(n: u64) -> TokenStream {
let (h4, h3, h2, h1) = (
(n >> 48) & 0xffff,
(n >> 32) & 0xffff,
(n >> 16) & 0xffff,
n & 0xffff,
);
syn::parse_str::<syn::Lit>(
&(if h4 != 0 {
format!("0x{:04x}_{:04x}_{:04x}_{:04x}", h4, h3, h2, h1)
} else if h3 != 0 {
format!("0x{:04x}_{:04x}_{:04x}", h3, h2, h1)
} else if h2 != 0 {
format!("0x{:04x}_{:04x}", h2, h1)
} else if h1 & 0xff00 != 0 {
format!("0x{:04x}", h1)
} else if h1 != 0 {
format!("0x{:02x}", h1 & 0xff)
} else {
"0".to_string()
}),
)
.unwrap()
.into_token_stream()
}
pub fn unsuffixed(n: u64) -> TokenStream {
let mut t = TokenStream::new();
t.append(Literal::u64_unsuffixed(n));
t
}
pub fn unsuffixed_or_bool(n: u64, width: u32) -> TokenStream {
if width == 1 {
let mut t = TokenStream::new();
t.append(Ident::new(
if n == 0 { "false" } else { "true" },
Span::call_site(),
));
t
} else {
unsuffixed(n)
}
}
pub trait U32Ext {
fn to_ty(&self) -> Result<Ident>;
fn to_ty_width(&self) -> Result<u32>;
}
impl U32Ext for u32 {
fn to_ty(&self) -> Result<Ident> {
let span = Span::call_site();
Ok(match *self {
1 => Ident::new("bool", span),
2..=8 => Ident::new("u8", span),
9..=16 => Ident::new("u16", span),
17..=32 => Ident::new("u32", span),
33..=64 => Ident::new("u64", span),
_ => {
return Err(
format!("can't convert {} bits into a Rust integral type", *self).into(),
)
}
})
}
fn to_ty_width(&self) -> Result<u32> {
Ok(match *self {
1 => 1,
2..=8 => 8,
9..=16 => 16,
17..=32 => 32,
33..=64 => 64,
_ => {
return Err(format!(
"can't convert {} bits into a Rust integral type width",
*self
)
.into())
}
})
}
}
pub fn only_clusters(ercs: &[RegisterCluster]) -> Vec<&Cluster> {
let clusters: Vec<&Cluster> = ercs
.iter()
.filter_map(|x| match x {
RegisterCluster::Cluster(x) => Some(x),
_ => None,
})
.collect();
clusters
}
pub fn only_registers(ercs: &[RegisterCluster]) -> Vec<&Register> {
let registers: Vec<&Register> = ercs
.iter()
.filter_map(|x| match x {
RegisterCluster::Register(x) => Some(x),
_ => None,
})
.collect();
registers
}
pub fn build_rs() -> TokenStream {
quote! {
use std::env;
use std::fs::File;
use std::io::Write;
use std::path::PathBuf;
fn main() {
if env::var_os("CARGO_FEATURE_RT").is_some() {
let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
File::create(out.join("device.x"))
.unwrap()
.write_all(include_bytes!("device.x"))
.unwrap();
println!("cargo:rustc-link-search={}", out.display());
println!("cargo:rerun-if-changed=device.x");
}
println!("cargo:rerun-if-changed=build.rs");
}
}
}