diff --git a/Cargo.toml b/Cargo.toml index 1c1134ea..1f0e2bbb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,6 +22,8 @@ xmltree = "0.8" anyhow = "1.0.19" thiserror = "1.0.5" rayon = "1.3.0" +once_cell = "1.3.1" +regex = "1" [dependencies.serde] version = "1.0" diff --git a/src/error.rs b/src/error.rs index 2c119faa..0ac94300 100644 --- a/src/error.rs +++ b/src/error.rs @@ -2,6 +2,8 @@ //! This module defines error types and messages for SVD parsing and encoding pub use anyhow::{Context, Result}; +use once_cell::sync::Lazy; +use regex::Regex; use xmltree::Element; #[allow(clippy::large_enum_variant)] @@ -72,25 +74,20 @@ pub enum NameError { Invalid(String, String), } -pub(crate) fn is_valid_name(name: &str) -> bool { - let mut chars = name.chars(); - if let Some(first) = chars.next() { - if !(first.is_ascii_alphabetic() || first == '_') { - return false; - } - for c in chars { - if !(c.is_ascii_alphanumeric() || c == '_' || c == '%') { - return false; - } - } - true +pub(crate) fn check_name(name: &str, tag: &str) -> Result<()> { + static PATTERN: Lazy = Lazy::new(|| Regex::new("^[_A-Za-z0-9]*$").unwrap()); + if PATTERN.is_match(name) { + Ok(()) } else { - false + Err(NameError::Invalid(name.to_string(), tag.to_string()).into()) } } -pub(crate) fn check_name(name: &str, tag: &str) -> Result<()> { - if is_valid_name(name) { +pub(crate) fn check_dimable_name(name: &str, tag: &str) -> Result<()> { + static PATTERN: Lazy = Lazy::new(|| { + Regex::new("^((%s)|(%s)[_A-Za-z]{1}[_A-Za-z0-9]*)|([_A-Za-z]{1}[_A-Za-z0-9]*(\\[%s\\])?)|([_A-Za-z]{1}[_A-Za-z0-9]*(%s)?[_A-Za-z0-9]*)$").unwrap() + }); + if PATTERN.is_match(name) { Ok(()) } else { Err(NameError::Invalid(name.to_string(), tag.to_string()).into()) diff --git a/src/svd/clusterinfo.rs b/src/svd/clusterinfo.rs index ec230048..a0923ac2 100644 --- a/src/svd/clusterinfo.rs +++ b/src/svd/clusterinfo.rs @@ -109,9 +109,9 @@ impl ClusterInfoBuilder { impl ClusterInfo { fn validate(self) -> Result { - check_name(&self.name, "name")?; + check_dimable_name(&self.name, "name")?; if let Some(name) = self.derived_from.as_ref() { - check_name(name, "derivedFrom")?; + check_dimable_name(name, "derivedFrom")?; } else { if self.children.is_empty() { return Err(SVDError::EmptyCluster)?; diff --git a/src/svd/fieldinfo.rs b/src/svd/fieldinfo.rs index fba522aa..76a480e3 100644 --- a/src/svd/fieldinfo.rs +++ b/src/svd/fieldinfo.rs @@ -127,9 +127,9 @@ impl FieldInfoBuilder { impl FieldInfo { fn validate(self) -> Result { - check_name(&self.name, "name")?; + check_dimable_name(&self.name, "name")?; if let Some(name) = self.derived_from.as_ref() { - check_name(name, "derivedFrom")?; + check_dimable_name(name, "derivedFrom")?; } for ev in &self.enumerated_values { ev.check_range(0..2_u32.pow(self.bit_range.width))?; diff --git a/src/svd/peripheral.rs b/src/svd/peripheral.rs index f415cff9..d8cfb326 100644 --- a/src/svd/peripheral.rs +++ b/src/svd/peripheral.rs @@ -159,9 +159,9 @@ impl PeripheralBuilder { impl Peripheral { fn validate(self) -> Result { // TODO - check_name(&self.name, "name")?; + check_dimable_name(&self.name, "name")?; if let Some(name) = self.derived_from.as_ref() { - check_name(name, "derivedFrom")?; + check_dimable_name(name, "derivedFrom")?; } else if let Some(registers) = self.registers.as_ref() { if registers.is_empty() { return Err(SVDError::EmptyRegisters)?; diff --git a/src/svd/registerinfo.rs b/src/svd/registerinfo.rs index 4d5aa1fc..8f777977 100644 --- a/src/svd/registerinfo.rs +++ b/src/svd/registerinfo.rs @@ -182,15 +182,15 @@ impl RegisterInfoBuilder { impl RegisterInfo { fn validate(self) -> Result { - check_name(&self.name, "name")?; + check_dimable_name(&self.name, "name")?; if let Some(name) = self.alternate_group.as_ref() { check_name(name, "alternateGroup")?; } if let Some(name) = self.alternate_register.as_ref() { - check_name(name, "alternateRegister")?; + check_dimable_name(name, "alternateRegister")?; } if let Some(name) = self.derived_from.as_ref() { - check_name(name, "derivedFrom")?; + check_dimable_name(name, "derivedFrom")?; } else if let Some(fields) = self.fields.as_ref() { if fields.is_empty() { return Err(SVDError::EmptyFields)?;