Skip to content

Commit 83c4908

Browse files
bors[bot]burrbull
andauthored
Merge #115
115: Write Builders manually 2 r=Emilgardis a=burrbull #101 without changes in Error hierarchy. Should slightly be easier to review. Co-authored-by: Andrey Zgarbul <[email protected]>
2 parents 608088c + 96b4bcc commit 83c4908

15 files changed

+989
-275
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
77

88
## [Unreleased]
99

10+
- Builder pattern now is used for creating structures
11+
- Peripherals are processed in parallel with `rayon`
12+
- Serde now skips empty tags
13+
- Fix: bug in `addressUnitBits`
14+
1015
## [v0.9.0] - 2019-11-17
1116

1217
- [breaking-change] make `ClusterInfo` `description` optional

src/error.rs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,14 @@ pub enum SVDError {
4141
EncodeNotImplemented(String),
4242
#[error("Error parsing SVD XML")]
4343
FileParseError,
44+
#[error("Device must contain at least one peripheral")]
45+
EmptyDevice,
46+
#[error("Peripheral have `registers` tag, but it is empty")]
47+
EmptyRegisters,
48+
#[error("Cluster must contain at least one Register or Cluster")]
49+
EmptyCluster,
50+
#[error("Register have `fields` tag, but it is empty")]
51+
EmptyFields,
4452
}
4553

4654
// TODO: Consider making into an Error
@@ -49,4 +57,42 @@ pub enum InvalidBitRange {
4957
Syntax,
5058
ParseError,
5159
MsbLsb,
60+
Empty,
61+
}
62+
63+
#[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)]
64+
pub enum BuildError {
65+
#[error("`{0}` must be initialized")]
66+
Uninitialized(String),
67+
}
68+
69+
#[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)]
70+
pub enum NameError {
71+
#[error("Name `{0}` in tag `{1}` contains unexpected symbol")]
72+
Invalid(String, String),
73+
}
74+
75+
pub(crate) fn is_valid_name(name: &str) -> bool {
76+
let mut chars = name.chars();
77+
if let Some(first) = chars.next() {
78+
if !(first.is_ascii_alphabetic() || first == '_') {
79+
return false;
80+
}
81+
for c in chars {
82+
if !(c.is_ascii_alphanumeric() || c == '_' || c == '%') {
83+
return false;
84+
}
85+
}
86+
true
87+
} else {
88+
false
89+
}
90+
}
91+
92+
pub(crate) fn check_name(name: &str, tag: &str) -> Result<()> {
93+
if is_valid_name(name) {
94+
Ok(())
95+
} else {
96+
Err(NameError::Invalid(name.to_string(), tag.to_string()).into())
97+
}
5298
}

src/svd/bitrange.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,18 +45,18 @@ impl Parse for BitRange {
4545
let text = range
4646
.text
4747
.as_ref()
48-
.ok_or_else(|| anyhow::anyhow!("text missing"))?; // TODO: Make into a proper error, text empty or something similar
49-
// TODO: If the `InvalidBitRange` enum was an error we could context into here somehow so that
50-
// the output would be similar to the parse error
48+
.ok_or_else(|| SVDError::InvalidBitRange(tree.clone(), InvalidBitRange::Empty))?;
5149
if !text.starts_with('[') {
5250
return Err(
5351
SVDError::InvalidBitRange(tree.clone(), InvalidBitRange::Syntax).into(),
54-
); // TODO: Maybe have a MissingOpen/MissingClosing variant
52+
);
53+
// TODO: Maybe have a MissingOpen/MissingClosing variant
5554
}
5655
if !text.ends_with(']') {
5756
return Err(
5857
SVDError::InvalidBitRange(tree.clone(), InvalidBitRange::Syntax).into(),
59-
); // TODO: Maybe have a MissingOpen/MissingClosing variant
58+
);
59+
// TODO: Maybe have a MissingOpen/MissingClosing variant
6060
}
6161

6262
let mut parts = text[1..text.len() - 1].split(':');

src/svd/clusterinfo.rs

Lines changed: 85 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,81 @@ pub struct ClusterInfo {
4646
_extensible: (),
4747
}
4848

49+
#[derive(Clone, Debug, Default, PartialEq)]
50+
pub struct ClusterInfoBuilder {
51+
name: Option<String>,
52+
address_offset: Option<u32>,
53+
derived_from: Option<String>,
54+
description: Option<String>,
55+
header_struct_name: Option<String>,
56+
default_register_properties: RegisterProperties,
57+
children: Option<Vec<RegisterCluster>>,
58+
}
59+
60+
impl ClusterInfoBuilder {
61+
pub fn name(mut self, value: String) -> Self {
62+
self.name = Some(value);
63+
self
64+
}
65+
pub fn address_offset(mut self, value: u32) -> Self {
66+
self.address_offset = Some(value);
67+
self
68+
}
69+
pub fn derived_from(mut self, value: Option<String>) -> Self {
70+
self.derived_from = value;
71+
self
72+
}
73+
pub fn description(mut self, value: Option<String>) -> Self {
74+
self.description = value;
75+
self
76+
}
77+
pub fn header_struct_name(mut self, value: Option<String>) -> Self {
78+
self.header_struct_name = value;
79+
self
80+
}
81+
pub fn default_register_properties(mut self, value: RegisterProperties) -> Self {
82+
self.default_register_properties = value;
83+
self
84+
}
85+
pub fn children(mut self, value: Vec<RegisterCluster>) -> Self {
86+
self.children = Some(value);
87+
self
88+
}
89+
pub fn build(self) -> Result<ClusterInfo> {
90+
(ClusterInfo {
91+
name: self
92+
.name
93+
.ok_or_else(|| BuildError::Uninitialized("name".to_string()))?,
94+
address_offset: self
95+
.address_offset
96+
.ok_or_else(|| BuildError::Uninitialized("address_offset".to_string()))?,
97+
derived_from: self.derived_from,
98+
description: self.description,
99+
header_struct_name: self.header_struct_name,
100+
default_register_properties: self.default_register_properties,
101+
children: self
102+
.children
103+
.ok_or_else(|| BuildError::Uninitialized("children".to_string()))?,
104+
_extensible: (),
105+
})
106+
.validate()
107+
}
108+
}
109+
110+
impl ClusterInfo {
111+
fn validate(self) -> Result<Self> {
112+
check_name(&self.name, "name")?;
113+
if let Some(name) = self.derived_from.as_ref() {
114+
check_name(name, "derivedFrom")?;
115+
} else {
116+
if self.children.is_empty() {
117+
return Err(SVDError::EmptyCluster)?;
118+
}
119+
}
120+
Ok(self)
121+
}
122+
}
123+
49124
impl Parse for ClusterInfo {
50125
type Object = Self;
51126
type Error = anyhow::Error;
@@ -58,24 +133,23 @@ impl Parse for ClusterInfo {
58133

59134
impl ClusterInfo {
60135
fn _parse(tree: &Element, name: String) -> Result<Self> {
61-
Ok(Self {
62-
name, // TODO: Handle naming of cluster
63-
derived_from: tree.attributes.get("derivedFrom").map(|s| s.to_owned()),
64-
description: tree.get_child_text_opt("description")?,
65-
header_struct_name: tree.get_child_text_opt("headerStructName")?,
66-
address_offset: tree.get_child_u32("addressOffset")?,
67-
default_register_properties: RegisterProperties::parse(tree)?,
68-
children: {
136+
ClusterInfoBuilder::default()
137+
.name(name)
138+
.derived_from(tree.attributes.get("derivedFrom").map(|s| s.to_owned()))
139+
.description(tree.get_child_text_opt("description")?)
140+
.header_struct_name(tree.get_child_text_opt("headerStructName")?)
141+
.address_offset(tree.get_child_u32("addressOffset")?)
142+
.default_register_properties(RegisterProperties::parse(tree)?)
143+
.children({
69144
let children: Result<Vec<_>, _> = tree
70145
.children
71146
.iter()
72147
.filter(|t| t.name == "register" || t.name == "cluster")
73148
.map(RegisterCluster::parse)
74149
.collect();
75150
children?
76-
},
77-
_extensible: (),
78-
})
151+
})
152+
.build()
79153
}
80154
}
81155

src/svd/cpu.rs

Lines changed: 97 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,84 @@ pub struct Cpu {
3636
pub has_vendor_systick: bool,
3737

3838
// Reserve the right to add more fields to this struct
39-
pub(crate) _extensible: (),
39+
#[cfg_attr(feature = "serde", serde(skip))]
40+
_extensible: (),
41+
}
42+
43+
#[derive(Clone, Debug, Default, PartialEq)]
44+
pub struct CpuBuilder {
45+
name: Option<String>,
46+
revision: Option<String>,
47+
endian: Option<Endian>,
48+
mpu_present: Option<bool>,
49+
fpu_present: Option<bool>,
50+
nvic_priority_bits: Option<u32>,
51+
has_vendor_systick: Option<bool>,
52+
}
53+
54+
impl CpuBuilder {
55+
pub fn name(mut self, value: String) -> Self {
56+
self.name = Some(value);
57+
self
58+
}
59+
pub fn revision(mut self, value: String) -> Self {
60+
self.revision = Some(value);
61+
self
62+
}
63+
pub fn endian(mut self, value: Endian) -> Self {
64+
self.endian = Some(value);
65+
self
66+
}
67+
pub fn mpu_present(mut self, value: bool) -> Self {
68+
self.mpu_present = Some(value);
69+
self
70+
}
71+
pub fn fpu_present(mut self, value: bool) -> Self {
72+
self.fpu_present = Some(value);
73+
self
74+
}
75+
pub fn nvic_priority_bits(mut self, value: u32) -> Self {
76+
self.nvic_priority_bits = Some(value);
77+
self
78+
}
79+
pub fn has_vendor_systick(mut self, value: bool) -> Self {
80+
self.has_vendor_systick = Some(value);
81+
self
82+
}
83+
pub fn build(self) -> Result<Cpu> {
84+
(Cpu {
85+
name: self
86+
.name
87+
.ok_or_else(|| BuildError::Uninitialized("name".to_string()))?,
88+
revision: self
89+
.revision
90+
.ok_or_else(|| BuildError::Uninitialized("revision".to_string()))?,
91+
endian: self
92+
.endian
93+
.ok_or_else(|| BuildError::Uninitialized("endian".to_string()))?,
94+
mpu_present: self
95+
.mpu_present
96+
.ok_or_else(|| BuildError::Uninitialized("mpu_present".to_string()))?,
97+
fpu_present: self
98+
.fpu_present
99+
.ok_or_else(|| BuildError::Uninitialized("fpu_present".to_string()))?,
100+
nvic_priority_bits: self
101+
.nvic_priority_bits
102+
.ok_or_else(|| BuildError::Uninitialized("nvic_priority_bits".to_string()))?,
103+
has_vendor_systick: self
104+
.has_vendor_systick
105+
.ok_or_else(|| BuildError::Uninitialized("has_vendor_systick".to_string()))?,
106+
_extensible: (),
107+
})
108+
.validate()
109+
}
110+
}
111+
112+
impl Cpu {
113+
fn validate(self) -> Result<Self> {
114+
// TODO
115+
Ok(self)
116+
}
40117
}
41118

42119
impl Parse for Cpu {
@@ -48,16 +125,15 @@ impl Parse for Cpu {
48125
return Err(SVDError::NameMismatch(tree.clone()).into());
49126
}
50127

51-
Ok(Self {
52-
name: tree.get_child_text("name")?,
53-
revision: tree.get_child_text("revision")?,
54-
endian: Endian::parse(tree.get_child_elem("endian")?)?,
55-
mpu_present: tree.get_child_bool("mpuPresent")?,
56-
fpu_present: tree.get_child_bool("fpuPresent")?,
57-
nvic_priority_bits: tree.get_child_u32("nvicPrioBits")?,
58-
has_vendor_systick: tree.get_child_bool("vendorSystickConfig")?,
59-
_extensible: (),
60-
})
128+
CpuBuilder::default()
129+
.name(tree.get_child_text("name")?)
130+
.revision(tree.get_child_text("revision")?)
131+
.endian(Endian::parse(tree.get_child_elem("endian")?)?)
132+
.mpu_present(tree.get_child_bool("mpuPresent")?)
133+
.fpu_present(tree.get_child_bool("fpuPresent")?)
134+
.nvic_priority_bits(tree.get_child_u32("nvicPrioBits")?)
135+
.has_vendor_systick(tree.get_child_bool("vendorSystickConfig")?)
136+
.build()
61137
}
62138
}
63139

@@ -104,16 +180,16 @@ mod tests {
104180
#[test]
105181
fn decode_encode() {
106182
let tests = vec![(
107-
Cpu {
108-
name: String::from("EFM32JG12B500F512GM48"),
109-
revision: String::from("5.1.1"),
110-
endian: Endian::Little,
111-
mpu_present: true,
112-
fpu_present: true,
113-
nvic_priority_bits: 8,
114-
has_vendor_systick: false,
115-
_extensible: (),
116-
},
183+
CpuBuilder::default()
184+
.name("EFM32JG12B500F512GM48".to_string())
185+
.revision("5.1.1".to_string())
186+
.endian(Endian::Little)
187+
.mpu_present(true)
188+
.fpu_present(true)
189+
.nvic_priority_bits(8)
190+
.has_vendor_systick(false)
191+
.build()
192+
.unwrap(),
117193
"
118194
<cpu>
119195
<name>EFM32JG12B500F512GM48</name>

0 commit comments

Comments
 (0)