pub mod file;
pub mod loader;
pub mod mem;
pub mod meta;
pub mod pixeldata;
pub mod tokens;
mod util;
pub use crate::file::{from_reader, open_file};
pub use crate::mem::InMemDicomObject;
pub use crate::meta::{FileMetaTable, FileMetaTableBuilder};
pub use dicom_core::Tag;
pub use dicom_dictionary_std::StandardDataDictionary;
pub type DefaultDicomObject = FileDicomObject<mem::InMemDicomObject<StandardDataDictionary>>;
use dicom_core::header::Header;
use dicom_encoding::{text::SpecificCharacterSet, transfer_syntax::TransferSyntaxIndex};
use dicom_parser::dataset::{DataSetWriter, IntoTokens};
use dicom_transfer_syntax_registry::TransferSyntaxRegistry;
use snafu::{Backtrace, OptionExt, ResultExt, Snafu};
use std::fs::File;
use std::io::{BufWriter, Write};
use std::path::Path;
pub const IMPLEMENTATION_CLASS_UID: &str = "2.25.137038125948464847900039011591283709926";
pub const IMPLEMENTATION_VERSION_NAME: &str = "DICOM-rs 0.3";
pub trait DicomObject {
type Element: Header;
fn element(&self, tag: Tag) -> Result<Self::Element>;
fn element_by_name(&self, name: &str) -> Result<Self::Element>;
fn meta(&self) -> Option<&FileMetaTable> {
None
}
}
#[derive(Debug, Snafu)]
#[non_exhaustive]
pub enum Error {
#[snafu(display("Could not open file '{}'", filename.display()))]
OpenFile {
filename: std::path::PathBuf,
backtrace: Backtrace,
source: std::io::Error,
},
#[snafu(display("Could not read from file '{}'", filename.display()))]
ReadFile {
filename: std::path::PathBuf,
backtrace: Backtrace,
source: std::io::Error,
},
#[snafu(display("Could not parse meta group data set"))]
ParseMetaDataSet {
#[snafu(backtrace)]
source: crate::meta::Error,
},
#[snafu(display("Could not create data set parser"))]
CreateParser {
#[snafu(backtrace)]
source: dicom_parser::dataset::read::Error,
},
#[snafu(display("Could not read data set token"))]
ReadToken {
#[snafu(backtrace)]
source: dicom_parser::dataset::read::Error,
},
#[snafu(display("Could not write to file '{}'", filename.display()))]
WriteFile {
filename: std::path::PathBuf,
backtrace: Backtrace,
source: std::io::Error,
},
#[snafu(display("Could not write object preamble"))]
WritePreamble {
backtrace: Backtrace,
source: std::io::Error,
},
#[snafu(display("Could not write magic code"))]
WriteMagicCode {
backtrace: Backtrace,
source: std::io::Error,
},
#[snafu(display("Could not create data set printer"))]
CreatePrinter {
#[snafu(backtrace)]
source: dicom_parser::dataset::write::Error,
},
#[snafu(display("Could not print meta group data set"))]
PrintMetaDataSet {
#[snafu(backtrace)]
source: crate::meta::Error,
},
#[snafu(display("Could not print data set"))]
PrintDataSet {
#[snafu(backtrace)]
source: dicom_parser::dataset::write::Error,
},
#[snafu(display("Unsupported transfer syntax `{}`", uid))]
UnsupportedTransferSyntax { uid: String, backtrace: Backtrace },
#[snafu(display("No such data element with tag {}", tag))]
NoSuchDataElementTag { tag: Tag, backtrace: Backtrace },
#[snafu(display("No such data element {} (with tag {})", alias, tag))]
NoSuchDataElementAlias {
tag: Tag,
alias: String,
backtrace: Backtrace,
},
#[snafu(display("Unknown data attribute named `{}`", name))]
NoSuchAttributeName { name: String, backtrace: Backtrace },
#[snafu(display("Missing element value"))]
MissingElementValue { backtrace: Backtrace },
#[snafu(display("Unexpected token {:?}", token))]
UnexpectedToken {
token: dicom_parser::dataset::DataToken,
backtrace: Backtrace,
},
#[snafu(display("Premature data set end"))]
PrematureEnd { backtrace: Backtrace },
BuildMetaTable {
#[snafu(backtrace)]
source: crate::meta::Error,
},
PrepareMetaTable {
source: dicom_core::value::CastValueError,
backtrace: Backtrace,
},
}
pub type Result<T, E = Error> = std::result::Result<T, E>;
#[deprecated(since = "0.4.0", note = "use `FileDicomObject` instead")]
pub type RootDicomObject<O> = FileDicomObject<O>;
#[derive(Debug, Clone, PartialEq)]
pub struct FileDicomObject<O> {
meta: FileMetaTable,
obj: O,
}
impl<O> FileDicomObject<O> {
pub fn meta(&self) -> &FileMetaTable {
&self.meta
}
pub fn into_inner(self) -> O {
self.obj
}
}
impl<O> FileDicomObject<O>
where
for<'a> &'a O: IntoTokens,
{
pub fn write_to_file<P: AsRef<Path>>(&self, path: P) -> Result<()> {
let path = path.as_ref();
let file = File::create(path).context(WriteFile { filename: path })?;
let mut to = BufWriter::new(file);
to.write_all(&[0_u8; 128][..])
.context(WriteFile { filename: path })?;
to.write_all(b"DICM")
.context(WriteFile { filename: path })?;
self.meta.write(&mut to).context(PrintMetaDataSet)?;
let registry = TransferSyntaxRegistry::default();
let ts = registry.get(&self.meta.transfer_syntax).with_context(|| {
UnsupportedTransferSyntax {
uid: self.meta.transfer_syntax.clone(),
}
})?;
let cs = SpecificCharacterSet::Default;
let mut dset_writer = DataSetWriter::with_ts_cs(to, ts, cs).context(CreatePrinter)?;
dset_writer
.write_sequence((&self.obj).into_tokens())
.context(PrintDataSet)?;
Ok(())
}
pub fn write_all<W: Write>(&self, to: W) -> Result<()> {
let mut to = BufWriter::new(to);
to.write_all(&[0_u8; 128][..]).context(WritePreamble)?;
to.write_all(b"DICM").context(WriteMagicCode)?;
self.meta.write(&mut to).context(PrintMetaDataSet)?;
let registry = TransferSyntaxRegistry::default();
let ts = registry.get(&self.meta.transfer_syntax).with_context(|| {
UnsupportedTransferSyntax {
uid: self.meta.transfer_syntax.clone(),
}
})?;
let cs = SpecificCharacterSet::Default;
let mut dset_writer = DataSetWriter::with_ts_cs(to, ts, cs).context(CreatePrinter)?;
dset_writer
.write_sequence((&self.obj).into_tokens())
.context(PrintDataSet)?;
Ok(())
}
pub fn write_meta<W: Write>(&self, to: W) -> Result<()> {
self.meta.write(to).context(PrintMetaDataSet)
}
pub fn write_dataset<W: Write>(&self, to: W) -> Result<()> {
let to = BufWriter::new(to);
let registry = TransferSyntaxRegistry::default();
let ts = registry.get(&self.meta.transfer_syntax).with_context(|| {
UnsupportedTransferSyntax {
uid: self.meta.transfer_syntax.clone(),
}
})?;
let cs = SpecificCharacterSet::Default;
let mut dset_writer = DataSetWriter::with_ts_cs(to, ts, cs).context(CreatePrinter)?;
dset_writer
.write_sequence((&self.obj).into_tokens())
.context(PrintDataSet)?;
Ok(())
}
}
impl<O> ::std::ops::Deref for FileDicomObject<O> {
type Target = O;
fn deref(&self) -> &Self::Target {
&self.obj
}
}
impl<O> ::std::ops::DerefMut for FileDicomObject<O> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.obj
}
}
impl<O> DicomObject for FileDicomObject<O>
where
O: DicomObject,
{
type Element = <O as DicomObject>::Element;
fn element(&self, tag: Tag) -> Result<Self::Element> {
self.obj.element(tag)
}
fn element_by_name(&self, name: &str) -> Result<Self::Element> {
self.obj.element_by_name(name)
}
fn meta(&self) -> Option<&FileMetaTable> {
Some(&self.meta)
}
}
impl<'a, O: 'a> DicomObject for &'a FileDicomObject<O>
where
O: DicomObject,
{
type Element = <O as DicomObject>::Element;
fn element(&self, tag: Tag) -> Result<Self::Element> {
self.obj.element(tag)
}
fn element_by_name(&self, name: &str) -> Result<Self::Element> {
self.obj.element_by_name(name)
}
}
impl<O> IntoIterator for FileDicomObject<O>
where
O: IntoIterator,
{
type Item = <O as IntoIterator>::Item;
type IntoIter = <O as IntoIterator>::IntoIter;
fn into_iter(self) -> Self::IntoIter {
self.obj.into_iter()
}
}
#[cfg(test)]
mod tests {
use crate::meta::FileMetaTableBuilder;
use crate::FileDicomObject;
#[test]
fn smoke_test() {
const FILE_NAME: &str = ".smoke-test.dcm";
let meta = FileMetaTableBuilder::new()
.transfer_syntax(
dicom_transfer_syntax_registry::entries::EXPLICIT_VR_LITTLE_ENDIAN.uid(),
)
.media_storage_sop_class_uid("1.2.840.10008.5.1.4.1.1.1")
.media_storage_sop_instance_uid("1.2.3.456")
.implementation_class_uid("1.2.345.6.7890.1.234")
.build()
.unwrap();
let obj = FileDicomObject::new_empty_with_meta(meta);
obj.write_to_file(FILE_NAME).unwrap();
let obj2 = FileDicomObject::open_file(FILE_NAME).unwrap();
assert_eq!(obj, obj2);
let _ = std::fs::remove_file(FILE_NAME);
}
}