use super::{BuildError, EmptyToNone, EnumeratedValue, SvdError, ValidateLevel};
use std::borrow::Cow;
use std::ops::RangeInclusive;
#[cfg_attr(
feature = "serde",
derive(serde::Deserialize, serde::Serialize),
serde(rename_all = "camelCase")
)]
#[derive(Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub struct DimElement {
pub dim: u32,
pub dim_increment: u32,
#[cfg_attr(
feature = "serde",
serde(
deserialize_with = "ser_de::deserialize_dim_index",
serialize_with = "ser_de::serialize_dim_index",
skip_serializing_if = "Option::is_none"
)
)]
pub dim_index: Option<Vec<String>>,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Option::is_none")
)]
pub dim_name: Option<String>,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Option::is_none")
)]
pub dim_array_index: Option<DimArrayIndex>,
}
#[cfg_attr(
feature = "serde",
derive(serde::Deserialize, serde::Serialize),
serde(rename_all = "camelCase")
)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct DimArrayIndex {
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Option::is_none")
)]
pub header_enum_name: Option<String>,
pub values: Vec<EnumeratedValue>,
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct DimElementBuilder {
dim: Option<u32>,
dim_increment: Option<u32>,
dim_index: Option<Vec<String>>,
dim_name: Option<String>,
dim_array_index: Option<DimArrayIndex>,
}
impl From<DimElement> for DimElementBuilder {
fn from(d: DimElement) -> Self {
Self {
dim: Some(d.dim),
dim_increment: Some(d.dim_increment),
dim_index: d.dim_index,
dim_name: d.dim_name,
dim_array_index: d.dim_array_index,
}
}
}
impl DimElementBuilder {
pub fn dim(mut self, value: u32) -> Self {
self.dim = Some(value);
self
}
pub fn dim_increment(mut self, value: u32) -> Self {
self.dim_increment = Some(value);
self
}
pub fn dim_index(mut self, value: Option<Vec<String>>) -> Self {
self.dim_index = value;
self
}
pub fn dim_name(mut self, value: Option<String>) -> Self {
self.dim_name = value;
self
}
pub fn dim_array_index(mut self, value: Option<DimArrayIndex>) -> Self {
self.dim_array_index = value;
self
}
pub fn build(self, lvl: ValidateLevel) -> Result<DimElement, SvdError> {
let de = DimElement {
dim: self
.dim
.ok_or_else(|| BuildError::Uninitialized("dim".to_string()))?,
dim_increment: self
.dim_increment
.ok_or_else(|| BuildError::Uninitialized("dim_increment".to_string()))?,
dim_index: self.dim_index.empty_to_none(),
dim_name: self.dim_name.empty_to_none(),
dim_array_index: self.dim_array_index,
};
de.validate(lvl)?;
Ok(de)
}
}
impl DimElement {
pub fn builder() -> DimElementBuilder {
DimElementBuilder::default()
}
pub fn parse_indexes(text: &str) -> Option<Vec<String>> {
(if text.contains('-') {
let (start, end) = text.split_once('-')?;
if let (Ok(start), Ok(end)) = (start.parse::<u32>(), end.parse::<u32>()) {
Some((start..=end).map(|i| i.to_string()).collect::<Vec<_>>())
} else {
let mut start = start.bytes();
let mut end = end.bytes();
match (start.next(), start.next(), end.next(), end.next()) {
(Some(start), None, Some(end), None)
if (start.is_ascii_lowercase() && end.is_ascii_lowercase())
|| (start.is_ascii_uppercase() && end.is_ascii_uppercase()) =>
{
Some((start..=end).map(|c| char::from(c).to_string()).collect())
}
_ => None,
}
}
} else {
Some(text.split(',').map(|s| s.to_string()).collect())
})
.filter(|v| !v.is_empty())
}
pub fn indexes_as_range(&self) -> Option<RangeInclusive<u32>> {
let mut integers = Vec::with_capacity(self.dim as usize);
for idx in self.indexes() {
let val = idx.parse::<u32>().ok()?;
if val.to_string() != idx {
return None;
}
integers.push(val);
}
let min = *integers.iter().min()?;
let max = *integers.iter().max()?;
if max - min + 1 != self.dim {
return None;
}
for (&i, r) in integers.iter().zip(min..=max) {
if i != r {
return None;
}
}
Some(min..=max)
}
pub fn modify_from(
&mut self,
builder: DimElementBuilder,
lvl: ValidateLevel,
) -> Result<(), SvdError> {
if let Some(dim) = builder.dim {
self.dim = dim;
}
if let Some(dim_increment) = builder.dim_increment {
self.dim_increment = dim_increment;
}
if builder.dim_index.is_some() {
self.dim_index = builder.dim_index.empty_to_none();
}
if builder.dim_name.is_some() {
self.dim_name = builder.dim_name.empty_to_none();
}
if builder.dim_array_index.is_some() {
self.dim_array_index = builder.dim_array_index;
}
self.validate(lvl)
}
pub fn validate(&self, _lvl: ValidateLevel) -> Result<(), SvdError> {
Ok(())
}
pub fn indexes(&self) -> Indexes {
Indexes {
i: 0,
dim: self.dim,
dim_index: &self.dim_index,
}
}
}
pub struct Indexes<'a> {
i: u32,
dim: u32,
dim_index: &'a Option<Vec<String>>,
}
impl<'a> Iterator for Indexes<'a> {
type Item = Cow<'a, str>;
fn next(&mut self) -> Option<Self::Item> {
if self.i == self.dim {
return None;
}
let i = self.i;
self.i += 1;
if let Some(index) = self.dim_index.as_ref() {
Some(index[i as usize].as_str().into())
} else {
Some(i.to_string().into())
}
}
}
#[cfg(feature = "serde")]
mod ser_de {
use super::*;
use serde::{de, Deserialize, Deserializer, Serializer};
#[derive(serde::Serialize, serde::Deserialize)]
#[serde(untagged)]
enum DimIndex {
Array(Vec<String>),
String(String),
}
pub fn deserialize_dim_index<'de, D>(deserializer: D) -> Result<Option<Vec<String>>, D::Error>
where
D: Deserializer<'de>,
{
Ok(match Option::<DimIndex>::deserialize(deserializer)? {
None => None,
Some(DimIndex::Array(a)) => Some(a),
Some(DimIndex::String(s)) => Some(
DimElement::parse_indexes(&s)
.ok_or_else(|| de::Error::custom("Failed to deserialize dimIndex"))?,
),
})
}
pub fn serialize_dim_index<S>(val: &Option<Vec<String>>, s: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
s.serialize_str(&val.as_ref().unwrap().join(","))
}
}