Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Write Builders manually #101

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions src/elementext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ impl ElementExt for Element {
match child.get_text() {
Err(e) => match e.downcast_ref() {
// if tag is empty just ignore it
Some(SVDError::EmptyTag(_, _)) => Ok(None),
Some(ParseError::EmptyTag(_, _)) => Ok(None),
_ => return Err(e),
},
Ok(s) => Ok(Some(s.to_owned())),
Expand All @@ -53,7 +53,7 @@ impl ElementExt for Element {
K: core::fmt::Display + Clone,
{
self.get_child_text_opt(k.clone())?
.ok_or_else(|| SVDError::MissingTag(self.clone(), format!("{}", k)).into())
.ok_or_else(|| ParseError::MissingTag(self.clone(), format!("{}", k)).into())
}

/// Get text contained by an XML Element
Expand All @@ -63,22 +63,22 @@ impl ElementExt for Element {
// FIXME: Doesn't look good because SVDError doesn't format by itself. We already
// capture the element and this information can be used for getting the name
// This would fix ParseError
None => Err(SVDError::EmptyTag(self.clone(), self.name.clone()).into()),
None => Err(ParseError::EmptyTag(self.clone(), self.name.clone()).into()),
}
}

/// Get a named child element from an XML Element
fn get_child_elem<'a>(&'a self, n: &str) -> Result<&'a Element> {
match self.get_child(n) {
Some(s) => Ok(s),
None => Err(SVDError::MissingTag(self.clone(), n.to_string()).into()),
None => Err(ParseError::MissingTag(self.clone(), n.to_string()).into()),
}
}

/// Get a u32 value from a named child element
fn get_child_u32(&self, n: &str) -> Result<u32> {
let s = self.get_child_elem(n)?;
u32::parse(&s).context(SVDError::ParseError(self.clone()))
u32::parse(&s).context(ParseError::Other(self.clone()))
}

/// Get a bool value from a named child element
Expand Down
145 changes: 114 additions & 31 deletions src/error.rs
Original file line number Diff line number Diff line change
@@ -1,52 +1,135 @@
//! SVD Errors.
//! This module defines error types and messages for SVD parsing and encoding

pub use anyhow::{Context, Result};
pub use anyhow::{anyhow, Context, Result};
use thiserror::Error;
use xmltree::Element;

#[allow(clippy::large_enum_variant)]
#[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)]
pub enum SVDError {
#[error("Unknown endianness `{0}`")]
UnknownEndian(String),
// TODO: Needs context
// TODO: Better name
#[error("Expected a <{1}> tag, found none")]
MissingTag(Element, String),
#[derive(Clone, Debug, PartialEq, Eq, Error)]
pub enum ParseError {
#[error("Expected content in <{1}> tag, found none")]
EmptyTag(Element, String),
#[error("ParseError")]
ParseError(Element),
#[error("Expected a <{1}> tag, found none")]
MissingTag(Element, String),
#[error("NameMismatch")]
NameMismatch(Element),
#[error("unknown access variant '{1}' found")]
UnknownAccessType(Element, String),
#[error("Bit range invalid, {1:?}")]
InvalidBitRange(Element, InvalidBitRange),
#[error("Unknown write constraint")]
UnknownWriteConstraint(Element),
#[error("Multiple wc found")]
MoreThanOneWriteConstraint(Element),
#[error("Unknown usage variant")]
UnknownUsageVariant(Element),
#[error("Expected a <{1}>, found ...")]
NotExpectedTag(Element, String),
#[error("Invalid RegisterCluster (expected register or cluster), found {1}")]
InvalidRegisterCluster(Element, String),
#[error("Invalid modifiedWriteValues variant, found {1}")]
InvalidModifiedWriteValues(Element, String),
#[error("The content of the element could not be parsed to a boolean value {1}: {2}")]
InvalidBooleanValue(Element, String, core::str::ParseBoolError),
#[error("encoding method not implemented for svd object {0}")]
EncodeNotImplemented(String),
#[error("Error parsing SVD XML")]
FileParseError,
#[error("ParseError")]
Other(Element),
}

#[derive(Clone, Debug, PartialEq, Eq, Error)]
pub enum BuildError {
#[error("`{0}` must be initialized")]
Uninitialized(String),
}

#[derive(Clone, Debug, PartialEq, Eq, Error)]
pub enum AccessTypeError {
#[error("unknown access variant '{1}' found")]
Unknown(Element, String),
}

#[derive(Clone, Debug, PartialEq, Eq, Error)]
pub enum BitRangeError {
#[error("Bit range invalid, {1:?}")]
Invalid(Element, InvalidBitRange),
}

// TODO: Consider making into an Error
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum InvalidBitRange {
Empty,
Syntax,
ParseError,
MsbLsb,
}

#[derive(Clone, Debug, PartialEq, Eq, Error)]
pub enum EndianError {
#[error("Unknown endianness `{0}`")]
Unknown(String),
}

#[derive(Clone, Debug, PartialEq, Eq, Error)]
pub enum ModifiedWriteValuesError {
#[error("Invalid modifiedWriteValues variant, found {1}")]
Invalid(Element, String),
}

#[derive(Clone, Debug, PartialEq, Eq, Error)]
pub enum DeviceError {
#[error("Device must contain at least one peripheral")]
Empty,
}

#[derive(Clone, Debug, PartialEq, Eq, Error)]
pub enum PeripheralError {
#[error("Peripheral have `registers` tag, but it is empty")]
EmptyRegisters,
}

#[derive(Clone, Debug, PartialEq, Eq, Error)]
pub enum ClusterError {
#[error("Cluster must contain at least one Register or Cluster")]
Empty,
}

#[derive(Clone, Debug, PartialEq, Eq, Error)]
pub enum RegisterError {
#[error("Register have `fields` tag, but it is empty")]
EmptyFields,
}

#[derive(Clone, Debug, PartialEq, Eq, Error)]
pub enum RegisterClusterError {
#[error("Invalid RegisterCluster (expected register or cluster), found {1}")]
Invalid(Element, String),
}

#[derive(Clone, Debug, PartialEq, Eq, Error)]
pub enum UsageVariantError {
#[error("Unknown usage variant")]
Unknown(Element),
}

#[derive(Clone, Debug, PartialEq, Eq, Error)]
pub enum WriteConstraintError {
#[error("Unknown write constraint")]
Unknown(Element),
#[error("Multiple wc found")]
MoreThanOne(Element),
}

#[derive(Clone, Debug, PartialEq, Eq, Error)]
pub enum NameError {
#[error("Name `{0}` in tag `{1}` contains unexpected symbol")]
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
} else {
false
}
}

pub(crate) fn check_name(name: &str, tag: &str) -> Result<()> {
if is_valid_name(name) {
Ok(())
} else {
Err(NameError::Invalid(name.to_string(), tag.to_string()).into())
}
}
4 changes: 4 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,10 @@ pub(crate) fn new_element(name: &str, text: Option<String>) -> Element {
}
}

pub trait Build {
type Builder;
}

/// Generic test helper function
/// Takes an array of (item, xml) pairs where the item implements
/// Parse and Encode and tests object encoding and decoding
Expand Down
2 changes: 1 addition & 1 deletion src/svd/access.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ impl Parse for Access {
"read-writeOnce" => Ok(Access::ReadWriteOnce),
"write-only" => Ok(Access::WriteOnly),
"writeOnce" => Ok(Access::WriteOnce),
_ => Err(SVDError::UnknownAccessType(tree.clone(), text).into()),
_ => Err(AccessTypeError::Unknown(tree.clone(), text).into()),
}
}
}
Expand Down
36 changes: 14 additions & 22 deletions src/svd/bitrange.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,39 +45,31 @@ impl Parse for BitRange {
let text = range
.text
.as_ref()
.ok_or_else(|| anyhow::anyhow!("text missing"))?; // TODO: Make into a proper error, text empty or something similar
// TODO: If the `InvalidBitRange` enum was an error we could context into here somehow so that
// the output would be similar to the parse error
.ok_or_else(|| BitRangeError::Invalid(tree.clone(), InvalidBitRange::Empty))?;
if !text.starts_with('[') {
return Err(
SVDError::InvalidBitRange(tree.clone(), InvalidBitRange::Syntax).into(),
); // TODO: Maybe have a MissingOpen/MissingClosing variant
return Err(BitRangeError::Invalid(tree.clone(), InvalidBitRange::Syntax).into());
// TODO: Maybe have a MissingOpen/MissingClosing variant
}
if !text.ends_with(']') {
return Err(
SVDError::InvalidBitRange(tree.clone(), InvalidBitRange::Syntax).into(),
); // TODO: Maybe have a MissingOpen/MissingClosing variant
return Err(BitRangeError::Invalid(tree.clone(), InvalidBitRange::Syntax).into());
// TODO: Maybe have a MissingOpen/MissingClosing variant
}

let mut parts = text[1..text.len() - 1].split(':');
(
parts
.next()
.ok_or_else(|| {
SVDError::InvalidBitRange(tree.clone(), InvalidBitRange::Syntax)
})?
.ok_or_else(|| BitRangeError::Invalid(tree.clone(), InvalidBitRange::Syntax))?
.parse::<u32>()
.with_context(|| {
SVDError::InvalidBitRange(tree.clone(), InvalidBitRange::ParseError)
BitRangeError::Invalid(tree.clone(), InvalidBitRange::ParseError)
})?,
parts
.next()
.ok_or_else(|| {
SVDError::InvalidBitRange(tree.clone(), InvalidBitRange::Syntax)
})?
.ok_or_else(|| BitRangeError::Invalid(tree.clone(), InvalidBitRange::Syntax))?
.parse::<u32>()
.with_context(|| {
SVDError::InvalidBitRange(tree.clone(), InvalidBitRange::ParseError)
BitRangeError::Invalid(tree.clone(), InvalidBitRange::ParseError)
})?,
BitRangeType::BitRange,
)
Expand All @@ -86,10 +78,10 @@ impl Parse for BitRange {
(
// TODO: `u32::parse` should not hide it's errors
u32::parse(msb).with_context(|| {
SVDError::InvalidBitRange(tree.clone(), InvalidBitRange::MsbLsb)
BitRangeError::Invalid(tree.clone(), InvalidBitRange::MsbLsb)
})?,
u32::parse(lsb).with_context(|| {
SVDError::InvalidBitRange(tree.clone(), InvalidBitRange::MsbLsb)
BitRangeError::Invalid(tree.clone(), InvalidBitRange::MsbLsb)
})?,
BitRangeType::MsbLsb,
)
Expand All @@ -102,15 +94,15 @@ impl Parse for BitRange {
// TODO: capture that error comes from offset/width tag
// TODO: `u32::parse` should not hide it's errors
offset: u32::parse(offset).with_context(|| {
SVDError::InvalidBitRange(tree.clone(), InvalidBitRange::ParseError)
BitRangeError::Invalid(tree.clone(), InvalidBitRange::ParseError)
})?,
width: u32::parse(width).with_context(|| {
SVDError::InvalidBitRange(tree.clone(), InvalidBitRange::ParseError)
BitRangeError::Invalid(tree.clone(), InvalidBitRange::ParseError)
})?,
range_type: BitRangeType::OffsetWidth,
});
} else {
return Err(SVDError::InvalidBitRange(tree.clone(), InvalidBitRange::Syntax).into());
return Err(BitRangeError::Invalid(tree.clone(), InvalidBitRange::Syntax).into());
};

Ok(Self {
Expand Down
Loading