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

Add attribute operations API #326

Merged
merged 16 commits into from
Apr 30, 2023
Merged
7 changes: 7 additions & 0 deletions core/src/header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,13 @@ impl<I, P> DataElement<I, P> {
pub fn into_value(self) -> Value<I, P> {
self.value
}

/// Split the constituent parts of this element into a tuple.
/// If the value is a sequence,
/// its lifetime may still be bound to the original source.
pub fn into_parts(self) -> (DataElementHeader, Value<I, P>) {
(self.header, self.value)
}
}

impl<I, P> DataElement<I, P>
Expand Down
9 changes: 4 additions & 5 deletions core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,17 @@
//! - [`dictionary`] describes common behavior of DICOM data dictionaries,
//! which translate attribute names and/or tags to a dictionary entry
//! containing relevant information about the attribute.
//! - [`ops`] provides constructs for defining
//! operations on DICOM attributes,
//! to be applied on types resembling DICOM objects or data sets.
//! - [`value`] holds definitions for values in standard DICOM elements,
//! with the awareness of multiplicity, representation,
//! and the possible presence of sequences.
//! - [`error`] contains crate-level error and result types.
//!
//! [`dictionary`]: ./dictionary/index.html
//! [`error`]: ./error/index.html
//! [`header`]: ./header/index.html
//! [`value`]: ./value/index.html

pub mod dictionary;
pub mod header;
pub mod ops;
pub mod value;

pub use dictionary::DataDictionary;
Expand Down
137 changes: 137 additions & 0 deletions core/src/ops.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
//! Module for the attribute operations API.
//!
//! This allows consumers to specify and implement
//! operations on DICOM objects
//! as part of a larger process,
//! such as anonymization or transcoding.
//!
//! The most important type here is [`AttributeOp`],
//! which indicates which attribute is affected,
//! and the operation to apply ([`AttributeAction`]).
//! All DICOM object types supporting this API
//! implement the [`ApplyOp`] trait.
//!
//! # Example
//!
//! Given a DICOM object
//! (opened using [`dicom_object`](https://docs.rs/dicom-object)),
//! construct an [`AttributeOp`]
//! and apply it using [`apply`](ApplyOp::apply).
//!
//! ```no_run
//! use dicom_core::ops::*;
//! # /* do not really import this
//! use dicom_object::open_file;
//! # */
//!
//! # struct DicomObj;
//! # impl ApplyOp for DicomObj {
//! # type Err = snafu::Whatever;
//! # fn apply(&mut self, _: AttributeOp) -> Result<(), Self::Err> {
//! # panic!("this is just a stub");
//! # }
//! # }
//! # fn open_file(_: &str) -> Result<DicomObj, Box<dyn std::error::Error>> { Ok(DicomObj) }
//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
//! let mut obj = open_file("1/2/0003.dcm")?;
//! // hide patient name
//! obj.apply(AttributeOp {
//! tag: (0x0010, 0x0010).into(),
//! action: AttributeAction::ReplaceStr("Patient^Anonymous".into()),
//! })?;
//! # Ok(())
//! # }
//! ```
use std::borrow::Cow;

use crate::{Tag, PrimitiveValue, VR};

/// Descriptor for a single operation
/// to apply over a DICOM data set.
///
/// This type is purely descriptive.
/// It outlines a non-exhaustive set of possible changes around an attribute,
/// as well as set some expectations regarding the outcome of certain actions
/// against the attribute's previous state.
///
/// The operations themselves are provided
/// alongside DICOM objec or DICOM data set implementations.
///
/// Attribute operations can only select shallow attributes,
/// but the operation may be implemented when applied against nested data sets.
#[derive(Debug, Clone, PartialEq)]
pub struct AttributeOp {
/// the tag of the attribute to apply
pub tag: Tag,
/// the effective action to apply
pub action: AttributeAction,
}

/// Descriptor for the kind of action to apply over an attribute.
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq)]
pub enum AttributeAction {
/// Remove the attribute if it exists.
Remove,
/// If the attribute exists, clear its value to zero bytes.
Empty,
/// If the attribute exists,
/// set or provide a hint about the attribute's value representation.
///
/// The underlying value is not modified.
/// Implementations are free to ignore this request if
/// it cannot be done or it does not make sense
/// for the given implementation.
SetVr(VR),
/// Fully replace the value with the given DICOM value,
/// creating the attribute if it does not exist yet.
Replace(PrimitiveValue),
/// Fully replace a textual value with the given string,
/// creating the attribute if it does not exist yet.
ReplaceStr(Cow<'static, str>),
/// Append a string as an additional textual value,
/// creating the attribute if it does not exist yet.
///
/// New value items are recorded as separate text values,
/// meaning that they are delimited by a backslash (`\`) at encoding time,
/// regardless of the value representation.
PushStr(Cow<'static, str>),
/// Append a 32-bit signed integer as an additional numeric value,
/// creating the attribute if it does not exist yet.
PushI32(i32),
/// Append a 32-bit unsigned integer as an additional numeric value,
/// creating the attribute if it does not exist yet.
PushU32(u32),
/// Append a 16-bit signed integer as an additional numeric value,
/// creating the attribute if it does not exist yet.
PushI16(i16),
/// Append a 16-bit unsigned integer as an additional numeric value,
/// creating the attribute if it does not exist yet.
PushU16(u16),
/// Append a 32-bit floating point number as an additional numeric value,
/// creating the attribute if it does not exist yet.
PushF32(f32),
/// Append a 64-bit floating point number as an additional numeric value,
/// creating the attribute if it does not exist yet.
PushF64(f64),
}

/// Trait for applying DICOM attribute operations.
///
/// This is typically implemented by DICOM objects and other data set types
/// to serve as a common API for attribute manipulation.
pub trait ApplyOp {
/// The operation error type
type Err: std::error::Error + 'static;

/// Apply the given attribute operation on the receiving object.
///
/// Effects may slightly differ between implementations,
/// but should always be compliant with
/// the expectations defined in [`AttributeAction`] variants.
///
/// If the action to apply is unsupported,
/// or not possible for other reasons,
/// an error is returned and no changes to the receiver are made.
fn apply(&mut self, op: AttributeOp) -> Result<(), Self::Err>;
}
Loading