dicom_object/
file.rs

1use dicom_core::{DataDictionary, Tag};
2use dicom_dictionary_std::StandardDataDictionary;
3use dicom_encoding::transfer_syntax::TransferSyntaxIndex;
4use dicom_transfer_syntax_registry::TransferSyntaxRegistry;
5
6use crate::{DefaultDicomObject, ReadError};
7use std::io::Read;
8use std::path::Path;
9
10pub type Result<T, E = ReadError> = std::result::Result<T, E>;
11
12/// Create a DICOM object by reading from a byte source.
13///
14/// This function assumes the standard file encoding structure without the
15/// preamble: file meta group, followed by the rest of the data set.
16pub fn from_reader<F>(file: F) -> Result<DefaultDicomObject>
17where
18    F: Read,
19{
20    OpenFileOptions::new().from_reader(file)
21}
22
23/// Create a DICOM object by reading from a file.
24///
25/// This function assumes the standard file encoding structure: 128-byte
26/// preamble, file meta group, and the rest of the data set.
27pub fn open_file<P>(path: P) -> Result<DefaultDicomObject>
28where
29    P: AsRef<Path>,
30{
31    OpenFileOptions::new().open_file(path)
32}
33
34/// A builder type for opening a DICOM file with additional options.
35///
36/// This builder exposes additional properties
37/// to configure the reading of a DICOM file.
38///
39/// # Example
40///
41/// Create a `OpenFileOptions`,
42/// call adaptor methods in a chain,
43/// and finish the operation with
44/// either [`open_file()`](OpenFileOptions::open_file)
45/// or [`from_reader()`](OpenFileOptions::from_reader).
46///
47/// ```no_run
48/// # use dicom_object::OpenFileOptions;
49/// let file = OpenFileOptions::new()
50///     .read_until(dicom_dictionary_std::tags::PIXEL_DATA)
51///     .open_file("path/to/file.dcm")?;
52/// # Result::<(), Box<dyn std::error::Error>>::Ok(())
53/// ```
54#[derive(Debug, Default, Clone)]
55#[non_exhaustive]
56pub struct OpenFileOptions<D = StandardDataDictionary, T = TransferSyntaxRegistry> {
57    data_dictionary: D,
58    ts_index: T,
59    read_until: Option<Tag>,
60    read_preamble: ReadPreamble,
61}
62
63impl OpenFileOptions {
64    pub fn new() -> Self {
65        OpenFileOptions::default()
66    }
67}
68
69impl<D, T> OpenFileOptions<D, T> {
70    /// Set the operation to read only until the given tag is found.
71    ///
72    /// The reading process ends immediately after this tag,
73    /// or any other tag that is next in the standard DICOM tag ordering,
74    /// is found in the object's root data set.
75    /// An element with the exact tag will be excluded from the output.
76    pub fn read_until(mut self, tag: Tag) -> Self {
77        self.read_until = Some(tag);
78        self
79    }
80
81    /// Set the operation to read all elements of the data set to the end.
82    ///
83    /// This is the default behavior.
84    pub fn read_all(mut self) -> Self {
85        self.read_until = None;
86        self
87    }
88
89    /// Set whether to read the 128-byte DICOM file preamble.
90    pub fn read_preamble(mut self, option: ReadPreamble) -> Self {
91        self.read_preamble = option;
92        self
93    }
94
95    /// Set the transfer syntax index to use when reading the file.
96    pub fn tranfer_syntax_index<Tr>(self, ts_index: Tr) -> OpenFileOptions<D, Tr>
97    where
98        Tr: TransferSyntaxIndex,
99    {
100        OpenFileOptions {
101            data_dictionary: self.data_dictionary,
102            read_until: self.read_until,
103            read_preamble: self.read_preamble,
104            ts_index,
105        }
106    }
107
108    /// Set the data element dictionary to use when reading the file.
109    pub fn dictionary<Di>(self, dict: Di) -> OpenFileOptions<Di, T>
110    where
111        Di: DataDictionary,
112        Di: Clone,
113    {
114        OpenFileOptions {
115            data_dictionary: dict,
116            read_until: self.read_until,
117            read_preamble: self.read_preamble,
118            ts_index: self.ts_index,
119        }
120    }
121
122    /// Open the file at the given path.
123    pub fn open_file<P>(self, path: P) -> Result<DefaultDicomObject<D>>
124    where
125        P: AsRef<Path>,
126        D: DataDictionary,
127        D: Clone,
128        T: TransferSyntaxIndex,
129    {
130        DefaultDicomObject::open_file_with_all_options(
131            path,
132            self.data_dictionary,
133            self.ts_index,
134            self.read_until,
135            self.read_preamble,
136        )
137    }
138
139    /// Obtain a DICOM object by reading from a byte source.
140    ///
141    /// This method assumes
142    /// the standard file encoding structure without the preamble:
143    /// file meta group, followed by the rest of the data set.
144    pub fn from_reader<R>(self, from: R) -> Result<DefaultDicomObject<D>>
145    where
146        R: Read,
147        D: DataDictionary,
148        D: Clone,
149        T: TransferSyntaxIndex,
150    {
151        DefaultDicomObject::from_reader_with_all_options(
152            from,
153            self.data_dictionary,
154            self.ts_index,
155            self.read_until,
156            self.read_preamble,
157        )
158    }
159}
160
161/// An enumerate of supported options for
162/// whether to read the 128-byte DICOM file preamble.
163#[derive(Debug, Default, Copy, Clone, Eq, Hash, PartialEq)]
164pub enum ReadPreamble {
165    /// Try to detect the presence of the preamble automatically.
166    /// If detection fails, it will revert to always reading the preamble
167    /// when opening a file by path,
168    /// and not reading it when reading from a byte source.
169    #[default]
170    Auto,
171    /// Never read the preamble,
172    /// thus assuming that the original source does not have it.
173    Never,
174    /// Always read the preamble first,
175    /// thus assuming that the original source always has it.
176    Always,
177}