use std::{borrow::Cow, fmt::Write};
use smallvec::{smallvec, SmallVec};
use crate::{PrimitiveValue, Tag, VR};
#[derive(Debug, Clone, PartialEq)]
pub struct AttributeOp {
pub selector: AttributeSelector,
pub action: AttributeAction,
}
impl AttributeOp {
pub fn new(selector: impl Into<AttributeSelector>, action: AttributeAction) -> Self {
AttributeOp {
selector: selector.into(),
action,
}
}
}
#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq)]
pub enum AttributeSelectorStep {
Tag(Tag),
Nested { tag: Tag, item: u32 },
}
impl From<Tag> for AttributeSelectorStep {
fn from(value: Tag) -> Self {
AttributeSelectorStep::Tag(value)
}
}
impl From<(Tag, u32)> for AttributeSelectorStep {
fn from((tag, item): (Tag, u32)) -> Self {
AttributeSelectorStep::Nested { tag, item }
}
}
impl std::fmt::Display for AttributeSelectorStep {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
AttributeSelectorStep::Tag(tag) => std::fmt::Display::fmt(tag, f),
AttributeSelectorStep::Nested { tag, item } => write!(f, "{tag}[{item}]"),
}
}
}
#[derive(Debug, Clone, Eq, Hash, PartialEq)]
pub struct AttributeSelector(SmallVec<[AttributeSelectorStep; 2]>);
impl AttributeSelector {
pub fn new(steps: impl IntoIterator<Item = AttributeSelectorStep>) -> Option<Self> {
let mut steps: SmallVec<_> = steps.into_iter().collect();
debug_assert!(steps.len() < 256);
let (last, rest) = steps.split_last_mut()?;
if matches!(last, AttributeSelectorStep::Nested { .. }) {
return None;
}
for step in rest {
if let AttributeSelectorStep::Tag(tag) = step {
*step = AttributeSelectorStep::Nested { tag: *tag, item: 0 };
}
}
Some(AttributeSelector(steps))
}
pub fn split_first(&self) -> (AttributeSelectorStep, Option<AttributeSelector>) {
match self.0.split_first() {
Some((first, rest)) => {
let rest = if rest.is_empty() {
None
} else {
Some(AttributeSelector(rest.into()))
};
(*first, rest)
}
None => {
unreachable!("invariant broken: attribute selector should have at least one step")
}
}
}
pub fn iter(&self) -> impl Iterator<Item = &AttributeSelectorStep> {
self.into_iter()
}
pub fn first_step(&self) -> &AttributeSelectorStep {
self.0
.first()
.expect("invariant broken: attribute selector should have at least one step")
}
pub fn last_step(&self) -> &AttributeSelectorStep {
self.0
.last()
.expect("invariant broken: attribute selector should have at least one step")
}
pub fn last_tag(&self) -> Tag {
match self.last_step() {
AttributeSelectorStep::Tag(tag) => *tag,
_ => unreachable!("invariant broken: last attribute selector step should be Tag"),
}
}
pub fn num_steps(&self) -> u32 {
self.0.len() as u32
}
}
impl IntoIterator for AttributeSelector {
type Item = AttributeSelectorStep;
type IntoIter = <SmallVec<[AttributeSelectorStep; 2]> as IntoIterator>::IntoIter;
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}
impl<'a> IntoIterator for &'a AttributeSelector {
type Item = &'a AttributeSelectorStep;
type IntoIter = <&'a SmallVec<[AttributeSelectorStep; 2]> as IntoIterator>::IntoIter;
fn into_iter(self) -> Self::IntoIter {
self.0.iter()
}
}
impl From<Tag> for AttributeSelector {
fn from(tag: Tag) -> Self {
AttributeSelector(smallvec![tag.into()])
}
}
impl From<(Tag, u32, Tag)> for AttributeSelector {
fn from((tag0, item, tag1): (Tag, u32, Tag)) -> Self {
AttributeSelector(smallvec![(tag0, item).into(), tag1.into()])
}
}
impl From<(Tag, Tag)> for AttributeSelector {
#[inline]
fn from((tag0, tag1): (Tag, Tag)) -> Self {
AttributeSelector(smallvec![(tag0, 0).into(), tag1.into()])
}
}
impl From<(Tag, u32, Tag, u32, Tag)> for AttributeSelector {
fn from((tag0, item0, tag1, item1, tag2): (Tag, u32, Tag, u32, Tag)) -> Self {
AttributeSelector(smallvec![
(tag0, item0).into(),
(tag1, item1).into(),
tag2.into()
])
}
}
impl From<(Tag, Tag, u32, Tag)> for AttributeSelector {
fn from((tag0, tag1, item1, tag2): (Tag, Tag, u32, Tag)) -> Self {
AttributeSelector(smallvec![
(tag0, 0).into(),
(tag1, item1).into(),
tag2.into()
])
}
}
impl From<(Tag, u32, Tag, Tag)> for AttributeSelector {
fn from((tag0, item0, tag1, tag2): (Tag, u32, Tag, Tag)) -> Self {
AttributeSelector(smallvec![
(tag0, item0).into(),
(tag1, 0).into(),
tag2.into()
])
}
}
impl From<(Tag, Tag, Tag)> for AttributeSelector {
fn from((tag0, tag1, tag2): (Tag, Tag, Tag)) -> Self {
AttributeSelector(smallvec![(tag0, 0).into(), (tag1, 0).into(), tag2.into()])
}
}
impl From<(Tag, u32, Tag, u32, Tag, u32, Tag)> for AttributeSelector {
fn from(
(tag0, item0, tag1, item1, tag2, item2, tag3): (Tag, u32, Tag, u32, Tag, u32, Tag),
) -> Self {
AttributeSelector(smallvec![
(tag0, item0).into(),
(tag1, item1).into(),
(tag2, item2).into(),
tag3.into()
])
}
}
impl std::fmt::Display for AttributeSelector {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut started = false;
for step in &self.0 {
if !started {
started = true;
} else {
f.write_char('.')?;
}
std::fmt::Display::fmt(step, f)?;
}
Ok(())
}
}
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq)]
pub enum AttributeAction {
Remove,
Empty,
SetVr(VR),
Set(PrimitiveValue),
SetStr(Cow<'static, str>),
SetIfMissing(PrimitiveValue),
SetStrIfMissing(Cow<'static, str>),
Replace(PrimitiveValue),
ReplaceStr(Cow<'static, str>),
PushStr(Cow<'static, str>),
PushI32(i32),
PushU32(u32),
PushI16(i16),
PushU16(u16),
PushF32(f32),
PushF64(f64),
Truncate(usize),
}
impl AttributeAction {
pub fn is_constructive(&self) -> bool {
matches!(
self,
AttributeAction::Set(_)
| AttributeAction::SetStr(_)
| AttributeAction::SetIfMissing(_)
| AttributeAction::SetStrIfMissing(_)
| AttributeAction::PushF32(_)
| AttributeAction::PushF64(_)
| AttributeAction::PushI16(_)
| AttributeAction::PushI32(_)
| AttributeAction::PushStr(_)
| AttributeAction::PushU16(_)
| AttributeAction::PushU32(_)
)
}
}
pub trait ApplyOp {
type Err: std::error::Error + 'static;
fn apply(&mut self, op: AttributeOp) -> Result<(), Self::Err>;
}
#[cfg(test)]
mod tests {
use crate::{
ops::{AttributeSelector, AttributeSelectorStep},
Tag,
};
#[test]
fn display_selectors() {
let selector: AttributeSelector = Tag(0x0014, 0x5100).into();
assert_eq!(selector.to_string(), "(0014,5100)");
let selector: AttributeSelector = (Tag(0x0018, 0x6011), 2, Tag(0x0018, 0x6012)).into();
assert_eq!(selector.to_string(), "(0018,6011)[2].(0018,6012)");
let selector = AttributeSelector::from((Tag(0x0040, 0xA730), 1, Tag(0x0040, 0xA730)));
assert_eq!(selector.to_string(), "(0040,A730)[1].(0040,A730)");
}
#[test]
fn split_selectors() {
let selector: AttributeSelector = Tag(0x0014, 0x5100).into();
assert_eq!(
selector.split_first(),
(AttributeSelectorStep::Tag(Tag(0x0014, 0x5100)), None)
);
let selector: AttributeSelector = (Tag(0x0018, 0x6011), 2, Tag(0x0018, 0x6012)).into();
assert_eq!(
selector.split_first(),
(
AttributeSelectorStep::Nested {
tag: Tag(0x0018, 0x6011),
item: 2
},
Some(Tag(0x0018, 0x6012).into())
)
);
let selector = AttributeSelector::from((Tag(0x0040, 0xA730), Tag(0x0040, 0xA730)));
assert_eq!(
selector.split_first(),
(
AttributeSelectorStep::Nested {
tag: Tag(0x0040, 0xA730),
item: 0
},
Some(Tag(0x0040, 0xA730).into())
)
);
}
}