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}