use super::{BuildError, Description, EmptyToNone, Name, SvdError, ValidateLevel};
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
#[derive(Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub struct EnumeratedValue {
pub name: String,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Option::is_none")
)]
pub description: Option<String>,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Option::is_none")
)]
pub value: Option<u64>,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Option::is_none")
)]
pub is_default: Option<bool>,
}
#[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)]
pub enum Error {
#[error("EnumeratedValue has no `value` or `is_default`")]
AbsentValue,
#[error("EnumeratedValue with `value` (passed {0:?}) should not have `is_default(True)`")]
ValueAndDefault(Option<u64>),
#[error("Value {0} out of range [{} - {}]", .1.start, .1.end - 1)]
OutOfRange(u64, core::ops::Range<u64>),
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct EnumeratedValueBuilder {
name: Option<String>,
description: Option<String>,
value: Option<u64>,
is_default: Option<bool>,
}
impl From<EnumeratedValue> for EnumeratedValueBuilder {
fn from(e: EnumeratedValue) -> Self {
Self {
name: Some(e.name),
description: e.description,
value: e.value,
is_default: e.is_default,
}
}
}
impl EnumeratedValueBuilder {
pub fn name(mut self, value: String) -> Self {
self.name = Some(value);
self
}
pub fn description(mut self, value: Option<String>) -> Self {
self.description = value;
self
}
pub fn value(mut self, value: Option<u64>) -> Self {
self.value = value;
self
}
#[allow(clippy::wrong_self_convention)]
pub fn is_default(mut self, value: Option<bool>) -> Self {
self.is_default = value;
self
}
pub fn build(self, lvl: ValidateLevel) -> Result<EnumeratedValue, SvdError> {
let ev = EnumeratedValue {
name: self
.name
.ok_or_else(|| BuildError::Uninitialized("name".to_string()))?,
description: self.description.empty_to_none(),
value: self.value,
is_default: self.is_default,
};
ev.validate(lvl)?;
Ok(ev)
}
}
impl EnumeratedValue {
pub fn is_default(&self) -> bool {
matches!(self.is_default, Some(true))
}
pub fn builder() -> EnumeratedValueBuilder {
EnumeratedValueBuilder::default()
}
pub fn modify_from(
&mut self,
builder: EnumeratedValueBuilder,
lvl: ValidateLevel,
) -> Result<(), SvdError> {
if let Some(name) = builder.name {
self.name = name;
}
if builder.description.is_some() {
self.description = builder.description.empty_to_none();
}
if builder.value.is_some() {
self.value = builder.value;
}
if builder.is_default.is_some() {
self.is_default = builder.is_default;
}
self.validate(lvl)
}
pub fn validate(&self, lvl: ValidateLevel) -> Result<(), SvdError> {
if !lvl.is_disabled() {
if lvl.is_strict() {
super::check_name(&self.name, "name")?;
}
match (self.value.is_some(), self.is_default()) {
(false, false) => Err(Error::AbsentValue.into()),
(true, true) if lvl.is_strict() => Err(Error::ValueAndDefault(self.value).into()),
_ => Ok(()),
}
} else {
Ok(())
}
}
pub(crate) fn check_range(&self, range: &core::ops::Range<u64>) -> Result<(), SvdError> {
match &self.value {
Some(x) if !range.contains(x) => Err(Error::OutOfRange(*x, range.clone()).into()),
_ => Ok(()),
}
}
}
impl Name for EnumeratedValue {
fn name(&self) -> &str {
&self.name
}
}
impl Description for EnumeratedValue {
fn description(&self) -> Option<&str> {
self.description.as_deref()
}
}