diff --git a/CHANGELOG.md b/CHANGELOG.md index 170abd96..d72e4aa3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +- Add support of reading SVD from YAML or JSON files instead of XML +- Fix ValidateLevel usage in lib.rs +- Use `svd-parser` v0.12.1 + ## [v0.20.0] - 2021-12-07 ### Fixed diff --git a/Cargo.toml b/Cargo.toml index ec7c725a..d31f0b9f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -45,11 +45,17 @@ quote = "1.0" proc-macro2 = "1.0" anyhow = "1.0" thiserror = "1.0" +serde_yaml = "0.8.21" +serde_json = "1.0.72" [dependencies.svd-parser] features = ["derive-from"] version = "0.12" +[dependencies.svd-rs] +features = ["serde"] +version = "0.12.1" + [dependencies.syn] version = "1.0" features = ["full","extra-traits"] diff --git a/src/generate/register.rs b/src/generate/register.rs index f49f04d7..406173db 100644 --- a/src/generate/register.rs +++ b/src/generate/register.rs @@ -303,8 +303,8 @@ pub fn fields( config: &Config, ) -> Result<()> { let span = Span::call_site(); - let can_read = [Access::ReadOnly, Access::ReadWriteOnce, Access::ReadWrite].contains(&access); - let can_write = access != Access::ReadOnly; + let can_read = access.can_read(); + let can_write = access.can_write(); // TODO enumeratedValues let inline = quote! { #[inline(always)] }; @@ -1263,7 +1263,7 @@ fn lookup_in_peripherals<'p>( all_peripherals: &'p [Peripheral], ) -> Result<(&'p EnumeratedValues, Option>)> { if let Some(peripheral) = all_peripherals.iter().find(|p| p.name == base_peripheral) { - let all_registers = peripheral.reg_iter().collect::>(); + let all_registers = peripheral.all_registers().collect::>(); lookup_in_peripheral( Some(base_peripheral), base_register, diff --git a/src/lib.rs b/src/lib.rs index d896a9bd..6ca95620 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -529,10 +529,10 @@ pub enum SvdError { } /// Generates rust code for the specified svd content. -pub fn generate(xml: &str, config: &Config) -> Result { +pub fn generate(input: &str, config: &Config) -> Result { use std::fmt::Write; - let device = svd_parser::parse(xml)?; + let device = crate::util::load_from(input, config)?; let mut device_x = String::new(); let items = generate::device::render(&device, config, &mut device_x).or(Err(SvdError::Render))?; diff --git a/src/main.rs b/src/main.rs index a7a43f19..f5f42a00 100755 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,7 @@ #![recursion_limit = "128"] use log::{error, info}; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use svd_parser::svd; mod generate; @@ -14,7 +14,7 @@ use std::process; use anyhow::{Context, Result}; use clap::{App, Arg}; -use crate::util::{build_rs, Config, Target}; +use crate::util::{build_rs, load_from, Config, Source, Target}; fn run() -> Result<()> { use clap_conf::prelude::*; @@ -84,6 +84,11 @@ fn run() -> Result<()> { .short("s") .help("Make advanced checks due to parsing SVD"), ) + .arg( + Arg::with_name("source_type") + .long("source_type") + .help("Specify file/stream format"), + ) .arg( Arg::with_name("log_level") .long("log") @@ -101,19 +106,19 @@ fn run() -> Result<()> { )) .get_matches(); - let xml = &mut String::new(); + let input = &mut String::new(); match matches.value_of("input") { Some(file) => { File::open(file) .context("Cannot open the SVD file")? - .read_to_string(xml) + .read_to_string(input) .context("Cannot read the SVD file")?; } None => { let stdin = std::io::stdin(); stdin .lock() - .read_to_string(xml) + .read_to_string(input) .context("Cannot read from stdin")?; } } @@ -146,6 +151,18 @@ fn run() -> Result<()> { cfg.bool_flag("ignore_groups", Filter::Arg) || cfg.bool_flag("ignore_groups", Filter::Conf); let strict = cfg.bool_flag("strict", Filter::Arg) || cfg.bool_flag("strict", Filter::Conf); + let mut source_type = cfg + .grab() + .arg("source_type") + .conf("source_type") + .done() + .and_then(|s| Source::from_str(&s)) + .unwrap_or_default(); + + if let Some(file) = matches.value_of("input") { + source_type = Source::from_path(Path::new(file)) + } + let config = Config { target, nightly, @@ -155,18 +172,11 @@ fn run() -> Result<()> { ignore_groups, strict, output_dir: path.clone(), - }; - - let mut parser_config = svd_parser::Config::default(); - parser_config.validate_level = if strict { - svd::ValidateLevel::Strict - } else { - svd::ValidateLevel::Weak + source_type, }; info!("Parsing device from SVD file"); - let device = svd_parser::parse_with_config(xml, &parser_config) - .with_context(|| "Error parsing SVD file".to_string())?; + let device = load_from(input, &config)?; let mut device_x = String::new(); info!("Rendering device"); diff --git a/src/util.rs b/src/util.rs index 6e01321a..bf3a4648 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,10 +1,10 @@ use std::borrow::Cow; -use crate::svd::{Access, Cluster, Register, RegisterCluster, RegisterInfo}; +use crate::svd::{Access, Cluster, Device, Register, RegisterCluster, RegisterInfo, ValidateLevel}; use inflections::Inflect; use proc_macro2::{Ident, Literal, Span, TokenStream}; use quote::{quote, ToTokens}; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use anyhow::{anyhow, bail, Context, Result}; @@ -14,6 +14,26 @@ pub const BITS_PER_BYTE: u32 = 8; /// that are not valid in Rust ident const BLACKLIST_CHARS: &[char] = &['(', ')', '[', ']', '/', ' ', '-']; +pub(crate) fn load_from(input: &str, config: &crate::util::Config) -> Result { + Ok(match config.source_type { + Source::Xml => { + let mut parser_config = svd_parser::Config::default(); + parser_config.validate_level = if config.strict { + ValidateLevel::Strict + } else { + ValidateLevel::Weak + }; + + svd_parser::parse_with_config(input, &parser_config) + .with_context(|| "Error parsing SVD XML file".to_string())? + } + Source::Yaml => serde_yaml::from_str(input) + .with_context(|| "Error parsing SVD YAML file".to_string())?, + Source::Json => serde_json::from_str(input) + .with_context(|| "Error parsing SVD JSON file".to_string())?, + }) +} + #[derive(Clone, PartialEq, Debug)] pub struct Config { pub target: Target, @@ -24,6 +44,7 @@ pub struct Config { pub ignore_groups: bool, pub strict: bool, pub output_dir: PathBuf, + pub source_type: Source, } impl Default for Config { @@ -37,6 +58,7 @@ impl Default for Config { ignore_groups: false, strict: false, output_dir: PathBuf::from("."), + source_type: Source::default(), } } } @@ -73,6 +95,36 @@ impl Default for Target { } } +#[derive(Clone, Copy, PartialEq, Debug)] +pub enum Source { + Xml, + Yaml, + Json, +} + +impl Default for Source { + fn default() -> Self { + Self::Xml + } +} + +impl Source { + pub fn from_str(s: &str) -> Option { + match s { + "yml" | "yaml" => Some(Self::Yaml), + "json" => Some(Self::Json), + "xml" => Some(Self::Xml), + _ => None, + } + } + pub fn from_path(path: &Path) -> Self { + path.extension() + .and_then(|e| e.to_str()) + .and_then(Self::from_str) + .unwrap_or_default() + } +} + pub trait ToSanitizedPascalCase { fn to_sanitized_pascal_case(&self) -> Cow; }