1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
//! CMSIS-SVD parser for Drone, an Embedded Operating System.
//!
//! # Documentation
//!
//! - [Drone Book](https://book.drone-os.com/)
//! - [API documentation](https://api.drone-os.com/drone-svd/0.14/)
//!
//! # Usage
//!
//! Add the crate to your `Cargo.toml` dependencies:
//!
//! ```toml
//! [dependencies]
//! drone-svd = { version = "0.14.0" }
//! ```

#![feature(bool_to_option)]
#![feature(cell_update)]
#![feature(generator_trait)]
#![feature(generators)]
#![warn(missing_docs, unsafe_op_in_unsafe_fn)]
#![warn(clippy::pedantic)]
#![allow(
    clippy::cast_possible_truncation,
    clippy::missing_errors_doc,
    clippy::missing_panics_doc,
    clippy::module_name_repetitions,
    clippy::must_use_candidate
)]

mod device;
mod register;
mod traverse;
mod variant;

pub use device::{Access, Device, Field, Peripheral, Register};

use self::{
    register::{generate_index, generate_registers},
    variant::trace_variants,
};
use anyhow::{anyhow, Result};
use std::{
    env,
    fs::File,
    io::{prelude::*, BufReader},
    mem,
    ops::Range,
    path::Path,
};

/// Options to configure how bindings are generated.
pub struct Config<'a> {
    macro_name: &'a str,
    bit_band: Option<Range<u32>>,
    exclude_peripherals: Vec<&'a str>,
}

impl<'a> Config<'a> {
    /// Creates a blank new set of options ready for configuration.
    pub fn new(macro_name: &'a str) -> Self {
        Self { macro_name, bit_band: None, exclude_peripherals: Vec::new() }
    }

    /// Extends the list of peripherals to exclude from generated bindings.
    pub fn exclude_peripherals(&mut self, exclude_peripherals: &[&'a str]) -> &mut Self {
        self.exclude_peripherals.extend(exclude_peripherals);
        self
    }

    /// Sets bit-band memory region.
    pub fn bit_band(&mut self, bit_band: Range<u32>) -> &mut Self {
        self.bit_band = Some(bit_band);
        self
    }

    /// Generates register bindings.
    pub fn generate_regs(
        self,
        output: &mut File,
        mut device: Device,
        pool_number: usize,
        pool_size: usize,
    ) -> Result<()> {
        normalize(&mut device);
        trace_variants(&mut device, &self)?;
        generate_registers(output, &device, pool_number, pool_size, &self)?;
        Ok(())
    }

    /// Generates registers index.
    pub fn generate_index(self, index_output: &mut File, mut device: Device) -> Result<()> {
        normalize(&mut device);
        trace_variants(&mut device, &self)?;
        generate_index(index_output, &device, &self)?;
        Ok(())
    }
}

/// Parse the SVD file at `path`.
pub fn parse<P: AsRef<Path>>(path: P) -> Result<Device> {
    let mut input = BufReader::new(File::open(path)?);
    let mut xml = String::new();
    input.read_to_string(&mut xml)?;
    serde_xml_rs::from_reader(xml.as_bytes()).map_err(|err| anyhow!("{}", err))
}

/// Instructs cargo to rerun the build script when RUSTFLAGS environment
/// variables changed.
pub fn rerun_if_env_changed() {
    for (var, _) in env::vars_os() {
        if let Some(var) = var.to_str() {
            if var.ends_with("RUSTFLAGS") {
                println!("cargo:rerun-if-env-changed={}", var);
            }
        }
    }
}

fn normalize(device: &mut Device) {
    device.peripherals.peripheral = mem::take(&mut device.peripherals.peripheral)
        .into_iter()
        .map(|(_, peripheral)| (peripheral.name.clone(), peripheral))
        .collect();
}