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