1pub use svd::ValidateLevel;
27pub use svd_rs as svd;
28
29pub use anyhow::Context;
30use roxmltree::{Document, Node, NodeId};
31pub mod elementext;
33use crate::elementext::ElementExt;
34pub mod types;
36
37#[derive(Clone, Copy, Debug, Default)]
38#[non_exhaustive]
39pub struct Config {
41 pub validate_level: ValidateLevel,
43 #[cfg(feature = "expand")]
44 pub expand: bool,
47 #[cfg(feature = "expand")]
48 pub expand_properties: bool,
50 pub ignore_enums: bool,
52}
53
54impl Config {
55 pub fn validate_level(mut self, lvl: ValidateLevel) -> Self {
57 self.validate_level = lvl;
58 self
59 }
60
61 #[cfg(feature = "expand")]
62 pub fn expand(mut self, val: bool) -> Self {
64 self.expand = val;
65 self
66 }
67
68 #[cfg(feature = "expand")]
69 pub fn expand_properties(mut self, val: bool) -> Self {
72 self.expand_properties = val;
73 self
74 }
75
76 pub fn ignore_enums(mut self, val: bool) -> Self {
78 self.ignore_enums = val;
79 self
80 }
81}
82
83pub trait Parse {
85 type Object;
87 type Error;
89 type Config;
91 fn parse(elem: &Node, config: &Self::Config) -> Result<Self::Object, Self::Error>;
93}
94
95pub fn optional<T>(n: &str, e: &Node, config: &T::Config) -> Result<Option<T::Object>, SVDErrorAt>
99where
100 T: Parse<Error = SVDErrorAt>,
101{
102 let child = match e.get_child(n) {
103 Some(c) => c,
104 None => return Ok(None),
105 };
106
107 match T::parse(&child, config) {
108 Ok(r) => Ok(Some(r)),
109 Err(e) => Err(e),
110 }
111}
112
113use crate::svd::Device;
114pub fn parse(xml: &str) -> anyhow::Result<Device> {
116 parse_with_config(xml, &Config::default())
117}
118pub fn parse_with_config(xml: &str, config: &Config) -> anyhow::Result<Device> {
120 fn get_name<'a>(node: &'a Node) -> Option<&'a str> {
121 node.children()
122 .find(|t| t.has_tag_name("name"))
123 .and_then(|t| t.text())
124 }
125
126 let xml = trim_utf8_bom(xml);
127 let tree = Document::parse(xml)?;
128 let root = tree.root();
129 let xmldevice = root
130 .get_child("device")
131 .ok_or_else(|| SVDError::MissingTag("device".to_string()).at(root.id()))?;
132
133 #[allow(unused_mut)]
134 let mut device = match Device::parse(&xmldevice, config) {
135 Ok(o) => Ok(o),
136 Err(e) => {
137 let id = e.id;
138 let node = tree.get_node(id).unwrap();
139 let pos = tree.text_pos_at(node.range().start);
140 let tagname = node.tag_name().name();
141 let mut res = Err(e.into());
142 if tagname.is_empty() {
143 res = res.with_context(|| format!("at {}", pos))
144 } else if let Some(name) = get_name(&node) {
145 res = res.with_context(|| format!("Parsing {} `{}` at {}", tagname, name, pos))
146 } else {
147 res = res.with_context(|| format!("Parsing unknown {} at {}", tagname, pos))
148 }
149 for parent in node.ancestors().skip(1) {
150 if parent.id() == NodeId::new(0) {
151 break;
152 }
153 let tagname = parent.tag_name().name();
154 match tagname {
155 "device" | "peripheral" | "register" | "field" | "enumeratedValue"
156 | "interrupt" => {
157 if let Some(name) = get_name(&parent) {
158 res = res.with_context(|| format!("In {} `{}`", tagname, name));
159 } else {
160 res = res.with_context(|| format!("In unknown {}", tagname));
161 }
162 }
163 _ => {}
164 }
165 }
166 res
167 }
168 }?;
169
170 #[cfg(feature = "expand")]
171 if config.expand_properties {
172 expand::expand_properties(&mut device);
173 }
174
175 #[cfg(feature = "expand")]
176 if config.expand {
177 device = expand::expand(&device)?;
178 }
179 Ok(device)
180}
181
182fn trim_utf8_bom(s: &str) -> &str {
184 if s.len() > 2 && s.as_bytes().starts_with(b"\xef\xbb\xbf") {
185 &s[3..]
186 } else {
187 s
188 }
189}
190
191mod array;
192use array::parse_array;
193
194mod access;
195mod addressblock;
196mod bitrange;
197mod cluster;
198mod cpu;
199mod datatype;
200mod device;
201mod dimelement;
202mod endian;
203mod enumeratedvalue;
204mod enumeratedvalues;
205mod field;
206mod interrupt;
207mod modifiedwritevalues;
208mod peripheral;
209mod protection;
210mod readaction;
211mod register;
212mod registercluster;
213mod registerproperties;
214mod usage;
215mod writeconstraint;
216
217#[cfg(feature = "expand")]
218pub mod expand;
219
220#[cfg(feature = "expand")]
221pub use expand::{expand, expand_properties};
222#[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)]
224pub enum SVDError {
225 #[error("{0}")]
226 Svd(#[from] svd::SvdError),
227 #[error("Expected a <{0}> tag, found none")]
228 MissingTag(String),
229 #[error("Expected content in <{0}> tag, found none")]
230 EmptyTag(String),
231 #[error("Failed to parse `{0}`")]
232 ParseInt(#[from] std::num::ParseIntError),
233 #[error("Unknown endianness `{0}`")]
234 UnknownEndian(String),
235 #[error("unknown access variant '{0}' found")]
236 UnknownAccessType(String),
237 #[error("Bit range invalid, {0:?}")]
238 InvalidBitRange(bitrange::InvalidBitRange),
239 #[error("Unknown write constraint")]
240 UnknownWriteConstraint,
241 #[error("Multiple wc found")]
242 MoreThanOneWriteConstraint,
243 #[error("Unknown usage variant")]
244 UnknownUsageVariant,
245 #[error("Unknown usage variant for addressBlock")]
246 UnknownAddressBlockUsageVariant,
247 #[error("Expected a <{0}>, found ...")]
248 NotExpectedTag(String),
249 #[error("Invalid RegisterCluster (expected register or cluster), found {0}")]
250 InvalidRegisterCluster(String),
251 #[error("Invalid datatype variant, found {0}")]
252 InvalidDatatype(String),
253 #[error("Invalid modifiedWriteValues variant, found {0}")]
254 InvalidModifiedWriteValues(String),
255 #[error("Invalid readAction variant, found {0}")]
256 InvalidReadAction(String),
257 #[error("Invalid protection variant, found {0}")]
258 InvalidProtection(String),
259 #[error("The content of the element could not be parsed to a boolean value {0}: {1}")]
260 InvalidBooleanValue(String, core::str::ParseBoolError),
261 #[error("dimIndex tag must contain {0} indexes, found {1}")]
262 IncorrectDimIndexesCount(usize, usize),
263 #[error("Failed to parse dimIndex")]
264 DimIndexParse,
265 #[error("Name `{0}` in tag `{1}` is missing a %s placeholder")]
266 MissingPlaceholder(String, String),
267}
268
269#[derive(Clone, Debug, PartialEq)]
270pub struct SVDErrorAt {
271 error: SVDError,
272 id: NodeId,
273}
274
275impl std::fmt::Display for SVDErrorAt {
276 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
277 self.error.fmt(f)
278 }
279}
280
281impl std::error::Error for SVDErrorAt {}
282
283impl SVDError {
284 pub fn at(self, id: NodeId) -> SVDErrorAt {
285 SVDErrorAt { error: self, id }
286 }
287}
288
289pub(crate) fn check_has_placeholder(name: &str, tag: &str) -> Result<(), SVDError> {
290 if name.contains("%s") {
291 Ok(())
292 } else {
293 Err(SVDError::MissingPlaceholder(
294 name.to_string(),
295 tag.to_string(),
296 ))
297 }
298}
299
300#[test]
301fn test_trim_utf8_bom_from_str() {
302 let bom_str = std::str::from_utf8(b"\xef\xbb\xbfxyz").unwrap();
304 assert_eq!("xyz", trim_utf8_bom(bom_str));
305 assert_eq!("xyz", trim_utf8_bom("xyz"));
306}