Skip to content

Commit 461005d

Browse files
committed
[parser] extend dataset module
- add more impls to DataToken - add IntoTokens and various implementations
1 parent 40cf311 commit 461005d

File tree

1 file changed

+278
-2
lines changed

1 file changed

+278
-2
lines changed

parser/src/dataset/mod.rs

Lines changed: 278 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! Interpretation of DICOM data sets as streams of tokens.
2-
use dicom_core::header::{DataElementHeader, Length};
2+
use dicom_core::header::{DataElementHeader, Length, VR};
33
use dicom_core::value::{DicomValueType, PrimitiveValue};
4-
use dicom_core::Tag;
4+
use dicom_core::{value::Value, DataElement, Tag};
55
use std::fmt;
66

77
pub mod read;
@@ -73,9 +73,285 @@ impl PartialEq<Self> for DataToken {
7373
}
7474
}
7575

76+
impl From<DataElementHeader> for DataToken {
77+
fn from(header: DataElementHeader) -> Self {
78+
match header.vr() {
79+
VR::SQ => DataToken::SequenceStart {
80+
tag: header.tag,
81+
len: header.len,
82+
},
83+
_ => DataToken::ElementHeader(header),
84+
}
85+
}
86+
}
87+
88+
impl DataToken {
89+
pub fn is_sequence_start(&self) -> bool {
90+
match self {
91+
DataToken::SequenceStart { .. } => true,
92+
_ => false,
93+
}
94+
}
95+
}
96+
7697
/// The type of delimiter: sequence or item.
7798
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
7899
pub enum SeqTokenType {
79100
Sequence,
80101
Item,
81102
}
103+
104+
/// A trait for converting structured DICOM data into a stream of data tokens.
105+
pub trait IntoTokens {
106+
/// The iterator type through which tokens are obtained.
107+
type Iter: Iterator<Item = DataToken>;
108+
109+
/// Convert the value into tokens.
110+
fn into_tokens(self) -> Self::Iter;
111+
}
112+
113+
/// Token generator from a DICOM data element.
114+
pub enum DataElementTokens<I>
115+
where
116+
I: IntoTokens,
117+
{
118+
/// initial state, at the beginning of the element
119+
Start(
120+
// Option is used for easy taking from a &mut,
121+
// should always be Some in practice
122+
Option<DataElement<I>>,
123+
),
124+
/// header was read
125+
Header(
126+
// Option is used for easy taking from a &mut,
127+
// should always be Some in practice
128+
Option<DataElement<I>>,
129+
),
130+
/// reading tokens from items
131+
Items(
132+
FlattenTokens<
133+
<dicom_core::value::C<AsItem<I>> as IntoIterator>::IntoIter,
134+
ItemTokens<I::Iter>,
135+
>,
136+
),
137+
/// no more elements
138+
End,
139+
}
140+
141+
impl<I> Iterator for DataElementTokens<I>
142+
where
143+
I: IntoTokens,
144+
{
145+
type Item = DataToken;
146+
147+
fn next(&mut self) -> Option<Self::Item> {
148+
use DataElementTokens::*;
149+
let (out, next_state) = match self {
150+
Start(elem) => {
151+
let elem = elem.take().unwrap();
152+
// data element header token
153+
154+
let token = DataToken::from(*elem.header());
155+
if token.is_sequence_start() {
156+
// retrieve sequence value, begin item sequence
157+
match elem.into_value() {
158+
Value::Primitive(_) => unreachable!(),
159+
Value::Sequence { items, size: _ } => {
160+
let items: dicom_core::value::C<_> = items
161+
.into_iter()
162+
.map(|o| AsItem(Length::UNDEFINED, o))
163+
.collect();
164+
(Some(token), DataElementTokens::Items(items.into_tokens()))
165+
}
166+
}
167+
} else {
168+
(
169+
Some(DataToken::ElementHeader(*elem.header())),
170+
Header(Some(elem)),
171+
)
172+
}
173+
}
174+
Header(elem) => {
175+
let elem = elem.take().unwrap();
176+
match elem.into_value() {
177+
Value::Sequence { .. } => unreachable!(),
178+
Value::Primitive(value) => {
179+
// return primitive value, done
180+
let token = DataToken::PrimitiveValue(value);
181+
(Some(token), End)
182+
}
183+
}
184+
}
185+
Items(tokens) => {
186+
if let Some(token) = tokens.next() {
187+
// bypass manual state transition
188+
return Some(token);
189+
} else {
190+
// sequence end token, end
191+
(Some(DataToken::SequenceEnd), End)
192+
}
193+
}
194+
End => (None, End),
195+
};
196+
*self = next_state;
197+
198+
out
199+
}
200+
}
201+
202+
impl<I> IntoTokens for DataElement<I>
203+
where
204+
I: IntoTokens,
205+
{
206+
type Iter = DataElementTokens<I>;
207+
208+
fn into_tokens(self) -> Self::Iter {
209+
DataElementTokens::Start(Some(self))
210+
}
211+
}
212+
213+
/// Flatten a sequence of elements into their respective
214+
/// token sequence in order.
215+
#[derive(Debug, PartialEq)]
216+
pub struct FlattenTokens<O, K> {
217+
seq: O,
218+
tokens: Option<K>,
219+
}
220+
221+
impl<O, K> Iterator for FlattenTokens<O, K>
222+
where
223+
O: Iterator,
224+
O::Item: IntoTokens<Iter = K>,
225+
K: Iterator<Item = DataToken>,
226+
{
227+
type Item = DataToken;
228+
229+
fn next(&mut self) -> Option<Self::Item> {
230+
// ensure a token sequence
231+
if self.tokens.is_none() {
232+
match self.seq.next() {
233+
Some(entries) => {
234+
self.tokens = Some(entries.into_tokens());
235+
}
236+
None => return None,
237+
}
238+
}
239+
240+
// retrieve the next token
241+
match self.tokens.as_mut().map(|s| s.next()) {
242+
Some(Some(token)) => Some(token),
243+
Some(None) => {
244+
self.tokens = None;
245+
self.next()
246+
}
247+
None => unreachable!(),
248+
}
249+
}
250+
}
251+
252+
impl<T> IntoTokens for Vec<T>
253+
where
254+
T: IntoTokens,
255+
{
256+
type Iter = FlattenTokens<<Vec<T> as IntoIterator>::IntoIter, <T as IntoTokens>::Iter>;
257+
258+
fn into_tokens(self) -> Self::Iter {
259+
FlattenTokens {
260+
seq: self.into_iter(),
261+
tokens: None,
262+
}
263+
}
264+
}
265+
266+
impl<T> IntoTokens for dicom_core::value::C<T>
267+
where
268+
T: IntoTokens,
269+
{
270+
type Iter =
271+
FlattenTokens<<dicom_core::value::C<T> as IntoIterator>::IntoIter, <T as IntoTokens>::Iter>;
272+
273+
fn into_tokens(self) -> Self::Iter {
274+
FlattenTokens {
275+
seq: self.into_iter(),
276+
tokens: None,
277+
}
278+
}
279+
}
280+
281+
// A stream of tokens from a DICOM item.
282+
#[derive(Debug)]
283+
pub enum ItemTokens<T> {
284+
/// Just started, an item header token will come next
285+
Start {
286+
len: Length,
287+
object_tokens: Option<T>,
288+
},
289+
/// Will return tokens from the inner object, then an end of item token
290+
/// when it ends
291+
Object { object_tokens: T },
292+
/// Just ended, no more tokens
293+
End,
294+
}
295+
296+
impl<T> ItemTokens<T>
297+
where
298+
T: Iterator<Item = DataToken>,
299+
{
300+
pub fn new<O>(len: Length, object: O) -> Self
301+
where
302+
O: IntoTokens<Iter = T>,
303+
{
304+
ItemTokens::Start {
305+
len,
306+
object_tokens: Some(object.into_tokens()),
307+
}
308+
}
309+
}
310+
311+
impl<T> Iterator for ItemTokens<T>
312+
where
313+
T: Iterator<Item = DataToken>,
314+
{
315+
type Item = DataToken;
316+
317+
fn next(&mut self) -> Option<Self::Item> {
318+
let (next_state, out) = match self {
319+
ItemTokens::Start { len, object_tokens } => (
320+
ItemTokens::Object {
321+
object_tokens: object_tokens.take().unwrap(),
322+
},
323+
Some(DataToken::ItemStart { len: *len }),
324+
),
325+
ItemTokens::Object { object_tokens } => {
326+
if let Some(token) = object_tokens.next() {
327+
return Some(token);
328+
} else {
329+
(ItemTokens::End, Some(DataToken::ItemEnd))
330+
}
331+
}
332+
ItemTokens::End => {
333+
return None;
334+
}
335+
};
336+
337+
*self = next_state;
338+
out
339+
}
340+
}
341+
342+
/// A newtype for interpreting the given data as an item.
343+
/// When converting a value of this type into tokens, the inner value's tokens
344+
/// will be surrounded by an item start and an item delimiter.
345+
#[derive(Debug, Clone, PartialEq)]
346+
pub struct AsItem<I>(Length, I);
347+
348+
impl<I> IntoTokens for AsItem<I>
349+
where
350+
I: IntoTokens,
351+
{
352+
type Iter = ItemTokens<I::Iter>;
353+
354+
fn into_tokens(self) -> Self::Iter {
355+
ItemTokens::new(self.0, self.1)
356+
}
357+
}

0 commit comments

Comments
 (0)