use super::{
BuildError, Cpu, Description, EmptyToNone, Name, Peripheral, RegisterProperties, SvdError,
ValidateLevel,
};
#[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)]
pub enum Error {
#[error("Device must contain at least one peripheral")]
EmptyDevice,
}
#[cfg_attr(
feature = "serde",
derive(serde::Deserialize, serde::Serialize),
serde(rename_all = "camelCase")
)]
#[derive(Clone, Debug, PartialEq)]
#[non_exhaustive]
pub struct Device {
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Option::is_none")
)]
pub vendor: Option<String>,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Option::is_none", rename = "vendorID")
)]
pub vendor_id: Option<String>,
pub name: String,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Option::is_none")
)]
pub series: Option<String>,
pub version: String,
pub description: String,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Option::is_none")
)]
pub license_text: Option<String>,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Option::is_none")
)]
pub cpu: Option<Cpu>,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Option::is_none")
)]
pub header_system_filename: Option<String>,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Option::is_none")
)]
pub header_definitions_prefix: Option<String>,
pub address_unit_bits: u32,
pub width: u32,
#[cfg_attr(feature = "serde", serde(flatten))]
pub default_register_properties: RegisterProperties,
pub peripherals: Vec<Peripheral>,
#[cfg_attr(feature = "serde", serde(skip, default = "default_xmlns_xs"))]
pub xmlns_xs: String,
#[cfg_attr(
feature = "serde",
serde(skip, default = "default_no_namespace_schema_location")
)]
pub no_namespace_schema_location: String,
#[cfg_attr(feature = "serde", serde(skip, default = "default_schema_version"))]
pub schema_version: String,
}
fn default_xmlns_xs() -> String {
"http://www.w3.org/2001/XMLSchema-instance".into()
}
fn default_no_namespace_schema_location() -> String {
format!(
"CMSIS-SVD_Schema_{}.xsd",
default_schema_version().replace('.', "_")
)
}
fn default_schema_version() -> String {
"1.1".into()
}
#[derive(Clone, Debug, Default)]
pub struct DeviceBuilder {
vendor: Option<String>,
vendor_id: Option<String>,
name: Option<String>,
series: Option<String>,
version: Option<String>,
description: Option<String>,
license_text: Option<String>,
cpu: Option<Cpu>,
header_system_filename: Option<String>,
header_definitions_prefix: Option<String>,
address_unit_bits: Option<u32>,
width: Option<u32>,
default_register_properties: RegisterProperties,
peripherals: Option<Vec<Peripheral>>,
xmlns_xs: Option<String>,
no_namespace_schema_location: Option<String>,
schema_version: Option<String>,
}
impl From<Device> for DeviceBuilder {
fn from(d: Device) -> Self {
Self {
vendor: d.vendor,
vendor_id: d.vendor_id,
name: Some(d.name),
series: d.series,
version: Some(d.version),
description: Some(d.description),
license_text: d.license_text,
cpu: d.cpu,
header_system_filename: d.header_system_filename,
header_definitions_prefix: d.header_definitions_prefix,
address_unit_bits: Some(d.address_unit_bits),
width: Some(d.width),
default_register_properties: d.default_register_properties,
peripherals: Some(d.peripherals),
xmlns_xs: Some(d.xmlns_xs),
no_namespace_schema_location: Some(d.no_namespace_schema_location),
schema_version: Some(d.schema_version),
}
}
}
impl DeviceBuilder {
pub fn vendor(mut self, value: Option<String>) -> Self {
self.vendor = value;
self
}
pub fn vendor_id(mut self, value: Option<String>) -> Self {
self.vendor_id = value;
self
}
pub fn name(mut self, value: String) -> Self {
self.name = Some(value);
self
}
pub fn series(mut self, value: Option<String>) -> Self {
self.series = value;
self
}
pub fn version(mut self, value: String) -> Self {
self.version = Some(value);
self
}
pub fn description(mut self, value: String) -> Self {
self.description = Some(value);
self
}
pub fn license_text(mut self, value: Option<String>) -> Self {
self.license_text = value;
self
}
pub fn cpu(mut self, value: Option<Cpu>) -> Self {
self.cpu = value;
self
}
pub fn header_system_filename(mut self, value: Option<String>) -> Self {
self.header_system_filename = value;
self
}
pub fn header_definitions_prefix(mut self, value: Option<String>) -> Self {
self.header_definitions_prefix = value;
self
}
pub fn address_unit_bits(mut self, value: u32) -> Self {
self.address_unit_bits = Some(value);
self
}
pub fn width(mut self, value: u32) -> Self {
self.width = Some(value);
self
}
pub fn default_register_properties(mut self, value: RegisterProperties) -> Self {
self.default_register_properties = value;
self
}
pub fn peripherals(mut self, value: Vec<Peripheral>) -> Self {
self.peripherals = Some(value);
self
}
pub fn xmlns_xs(mut self, value: String) -> Self {
self.xmlns_xs = Some(value);
self
}
pub fn no_namespace_schema_location(mut self, value: String) -> Self {
self.no_namespace_schema_location = Some(value);
self
}
pub fn schema_version(mut self, value: String) -> Self {
self.schema_version = Some(value);
self
}
pub fn build(self, lvl: ValidateLevel) -> Result<Device, SvdError> {
let schema_version = self.schema_version.unwrap_or_else(default_schema_version);
let device = Device {
vendor: self.vendor,
vendor_id: self.vendor_id,
name: self
.name
.ok_or_else(|| BuildError::Uninitialized("name".to_string()))?,
series: self.series,
version: self
.version
.or_else(|| {
if !lvl.is_strict() {
Some("1.0".into())
} else {
None
}
})
.ok_or_else(|| BuildError::Uninitialized("version".to_string()))?,
description: self
.description
.or_else(|| {
if !lvl.is_strict() {
Some("".into())
} else {
None
}
})
.ok_or_else(|| BuildError::Uninitialized("description".to_string()))?,
license_text: self.license_text,
cpu: self.cpu,
header_system_filename: self.header_system_filename,
header_definitions_prefix: self.header_definitions_prefix,
address_unit_bits: self
.address_unit_bits
.or_else(|| if !lvl.is_strict() { Some(8) } else { None })
.ok_or_else(|| BuildError::Uninitialized("addressUnitBits".to_string()))?,
width: self
.width
.or_else(|| if !lvl.is_strict() { Some(32) } else { None })
.ok_or_else(|| BuildError::Uninitialized("width".to_string()))?,
default_register_properties: self.default_register_properties.build(lvl)?,
peripherals: self
.peripherals
.ok_or_else(|| BuildError::Uninitialized("peripherals".to_string()))?,
xmlns_xs: self.xmlns_xs.unwrap_or_else(default_xmlns_xs),
no_namespace_schema_location: self
.no_namespace_schema_location
.unwrap_or_else(default_no_namespace_schema_location),
schema_version,
};
device.validate(lvl)?;
Ok(device)
}
}
impl Device {
pub fn builder() -> DeviceBuilder {
DeviceBuilder::default()
}
pub fn modify_from(
&mut self,
builder: DeviceBuilder,
lvl: ValidateLevel,
) -> Result<(), SvdError> {
if builder.vendor.is_some() {
self.vendor = builder.vendor.empty_to_none();
}
if builder.vendor_id.is_some() {
self.vendor_id = builder.vendor_id.empty_to_none();
}
if let Some(name) = builder.name {
self.name = name;
}
if builder.series.is_some() {
self.series = builder.series.empty_to_none();
}
if let Some(version) = builder.version {
self.version = version;
}
if let Some(description) = builder.description {
self.description = description;
}
if builder.license_text.is_some() {
self.license_text = builder.license_text.empty_to_none();
}
if builder.cpu.is_some() {
self.cpu = builder.cpu;
}
if builder.header_system_filename.is_some() {
self.header_system_filename = builder.header_system_filename.empty_to_none();
}
if builder.header_definitions_prefix.is_some() {
self.header_definitions_prefix = builder.header_definitions_prefix.empty_to_none();
}
if let Some(address_unit_bits) = builder.address_unit_bits {
self.address_unit_bits = address_unit_bits;
}
if let Some(width) = builder.width {
self.width = width;
}
self.default_register_properties
.modify_from(builder.default_register_properties, lvl)?;
if let Some(peripherals) = builder.peripherals {
self.peripherals = peripherals;
}
if let Some(xmlns_xs) = builder.xmlns_xs {
self.xmlns_xs = xmlns_xs;
}
if let Some(no_namespace_schema_location) = builder.no_namespace_schema_location {
self.no_namespace_schema_location = no_namespace_schema_location;
}
if let Some(schema_version) = builder.schema_version {
self.schema_version = schema_version;
}
self.validate(lvl)
}
pub fn validate(&self, lvl: ValidateLevel) -> Result<(), SvdError> {
if !lvl.is_disabled() {
if self.peripherals.is_empty() {
return Err(Error::EmptyDevice.into());
}
}
Ok(())
}
pub fn validate_all(&self, lvl: ValidateLevel) -> Result<(), SvdError> {
if let Some(cpu) = self.cpu.as_ref() {
cpu.validate(lvl)?;
}
self.default_register_properties.validate(lvl)?;
for p in &self.peripherals {
p.validate_all(lvl)?;
}
self.validate(lvl)
}
pub fn get_peripheral(&self, name: &str) -> Option<&Peripheral> {
self.peripherals.iter().find(|f| f.name == name)
}
pub fn get_mut_peripheral(&mut self, name: &str) -> Option<&mut Peripheral> {
self.peripherals.iter_mut().find(|f| f.name == name)
}
}
impl Name for Device {
fn name(&self) -> &str {
&self.name
}
}
impl Description for Device {
fn description(&self) -> Option<&str> {
Some(&self.description)
}
}