Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

serde YAML/JSON input #556

Merged
merged 2 commits into from
Jan 17, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -91,3 +91,24 @@ jobs:
OPTIONS: ${{ matrix.options }}
TRAVIS_OS_NAME: ${{ matrix.TRAVIS_OS_NAME }}
run: bash ci/script.sh
ci-serde:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
override: true
components: rustfmt
- name: Install svdtools
uses: actions-rs/install@v0.1
with:
crate: svdtools
version: 0.2.0
use-tool-cache: true
- name: Run CI script
run: |
wget https://stm32-rs.github.io/stm32-rs/stm32f411.svd.patched
svdtools convert --input-format xml stm32f411.svd.patched stm32f411.yaml
cargo run --release -- -i stm32f411.yaml
9 changes: 5 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,21 @@ and this project adheres to [Semantic Versioning](http://semver.org/).

## [Unreleased]

### Added

- Support of reading SVD from YAML or JSON files instead of XML

### Changed

- Use `svd-parser` v0.13.1
- Replace suffix in fields' name before converting to snake case when generating methods #563
- MIPS API now re-exports `mips_rt::interrupt` when the `rt` feature is enabled

### Fixed

- Fix ValidateLevel usage in lib.rs
- Parenthesizing `#offset_calc` to avoid clippy's warning of operator precedence

### Changed

- MIPS API now re-exports `mips_rt::interrupt` when the `rt` feature is enabled

## [v0.20.0] - 2021-12-07

### Fixed
Expand Down
6 changes: 6 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,17 @@ quote = "1.0"
proc-macro2 = "1.0"
anyhow = "1.0"
thiserror = "1.0"
serde_json = "1.0.75"
serde_yaml = "0.8.23"

[dependencies.svd-parser]
features = ["derive-from"]
version = "0.13.1"

[dependencies.svd-rs]
features = ["serde"]
version = "0.13.0"

[dependencies.syn]
version = "1.0"
features = ["full","extra-traits"]
35 changes: 30 additions & 5 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -502,8 +502,8 @@
use quote::quote;
use svd_parser::svd;

mod generate;
mod util;
pub mod generate;
pub mod util;

pub use crate::util::{Config, Target};

Expand All @@ -519,7 +519,8 @@ pub struct DeviceSpecific {
pub build_rs: String,
}

use anyhow::Result;
use anyhow::{Context, Result};

#[derive(Debug, thiserror::Error)]
pub enum SvdError {
#[error("Cannot format crate")]
Expand All @@ -529,10 +530,10 @@ pub enum SvdError {
}

/// Generates rust code for the specified svd content.
pub fn generate(xml: &str, config: &Config) -> Result<Generation> {
pub fn generate(input: &str, config: &Config) -> Result<Generation> {
use std::fmt::Write;

let device = svd_parser::parse(xml)?;
let device = load_from(input, config)?;
let mut device_x = String::new();
let items =
generate::device::render(&device, config, &mut device_x).or(Err(SvdError::Render))?;
Expand Down Expand Up @@ -562,6 +563,30 @@ pub fn generate(xml: &str, config: &Config) -> Result<Generation> {
})
}

/// Load a [Device] from a string slice with given [config](crate::util::Config).
pub fn load_from(input: &str, config: &crate::util::Config) -> Result<svd::Device> {
use self::util::SourceType;
use svd_parser::ValidateLevel;

Ok(match config.source_type {
SourceType::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())?
}
SourceType::Yaml => serde_yaml::from_str(input)
.with_context(|| "Error parsing SVD YAML file".to_string())?,
SourceType::Json => serde_json::from_str(input)
.with_context(|| "Error parsing SVD JSON file".to_string())?,
})
}

/// Assigns a handler to an interrupt
///
/// **NOTE** The `interrupt!` macro on Cortex-M and MSP430 device crates is closer in syntax to the
Expand Down
45 changes: 27 additions & 18 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
#![recursion_limit = "128"]

use log::{error, info};
use std::path::PathBuf;
use svd_parser::svd;

mod generate;
mod util;
use std::path::{Path, PathBuf};

use std::fs::File;
use std::io::Write;
Expand All @@ -14,7 +10,10 @@ use std::process;
use anyhow::{Context, Result};
use clap::{App, Arg};

use crate::util::{build_rs, Config, Target};
use svd2rust::{
generate, load_from,
util::{build_rs, Config, SourceType, Target},
};

fn run() -> Result<()> {
use clap_conf::prelude::*;
Expand Down Expand Up @@ -84,6 +83,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")
Expand All @@ -101,19 +105,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")?;
}
}
Expand Down Expand Up @@ -146,6 +150,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| SourceType::from_extension(&s))
.unwrap_or_default();

if let Some(file) = matches.value_of("input") {
source_type = SourceType::from_path(Path::new(file))
}

let config = Config {
target,
nightly,
Expand All @@ -155,18 +171,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");
Expand Down
35 changes: 34 additions & 1 deletion src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use crate::svd::{
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};

Expand All @@ -26,6 +26,7 @@ pub struct Config {
pub ignore_groups: bool,
pub strict: bool,
pub output_dir: PathBuf,
pub source_type: SourceType,
}

impl Default for Config {
Expand All @@ -39,6 +40,7 @@ impl Default for Config {
ignore_groups: false,
strict: false,
output_dir: PathBuf::from("."),
source_type: SourceType::default(),
}
}
}
Expand Down Expand Up @@ -75,6 +77,37 @@ impl Default for Target {
}
}

#[derive(Clone, Copy, PartialEq, Debug)]
pub enum SourceType {
Xml,
Yaml,
Json,
}

impl Default for SourceType {
fn default() -> Self {
Self::Xml
}
}

impl SourceType {
/// Make a new [`Source`] from a given extension.
pub fn from_extension(s: &str) -> Option<Self> {
match s {
"yml" | "yaml" => Some(Self::Yaml),
"json" => Some(Self::Json),
"svd" | "xml" => Some(Self::Xml),
_ => None,
}
}
pub fn from_path(path: &Path) -> Self {
path.extension()
.and_then(|e| e.to_str())
.and_then(Self::from_extension)
.unwrap_or_default()
}
}

pub trait ToSanitizedPascalCase {
fn to_sanitized_pascal_case(&self) -> Cow<str>;
}
Expand Down