use crate::error::NiftiError;
use crate::error::Result;
use crate::extension::{Extender, ExtensionSequence};
use crate::header::NiftiHeader;
use crate::header::MAGIC_CODE_NI1;
use crate::util::{into_img_file_gz, is_gz_file, open_file_maybe_gz};
use crate::volume::inmem::InMemNiftiVolume;
use crate::volume::streamed::StreamedNiftiVolume;
use crate::volume::{FromSource, FromSourceOptions, NiftiVolume};
use byteordered::ByteOrdered;
use flate2::bufread::GzDecoder;
use std::fs::File;
use std::io::{self, BufReader, Read};
use std::path::Path;
pub use crate::util::{GzDecodedFile, MaybeGzDecodedFile};
#[derive(Debug, Clone, PartialEq)]
pub struct ReaderOptions {
fix_header: bool,
}
impl ReaderOptions {
pub fn new() -> ReaderOptions {
ReaderOptions { fix_header: false }
}
pub fn fix_header(&mut self, fix_header: bool) -> &mut Self {
self.fix_header = fix_header;
self
}
pub fn read_file<P>(&self, path: P) -> Result<InMemNiftiObject>
where
P: AsRef<Path>,
{
let file = BufReader::new(File::open(&path)?);
let mut obj = if is_gz_file(&path) {
InMemNiftiObject::from_file_impl(path, GzDecoder::new(file), Default::default())
} else {
InMemNiftiObject::from_file_impl(path, file, Default::default())
}?;
if self.fix_header {
obj.header.fix();
}
Ok(obj)
}
pub fn read_file_pair<P, Q>(&self, hdr_path: P, vol_path: Q) -> Result<InMemNiftiObject>
where
P: AsRef<Path>,
Q: AsRef<Path>,
{
let file = BufReader::new(File::open(&hdr_path)?);
let mut obj = if is_gz_file(&hdr_path) {
InMemNiftiObject::from_file_pair_impl(
GzDecoder::new(file),
vol_path,
Default::default(),
)
} else {
InMemNiftiObject::from_file_pair_impl(file, vol_path, Default::default())
}?;
if self.fix_header {
obj.header.fix();
}
Ok(obj)
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct ReaderStreamedOptions {
fix_header: bool,
}
impl ReaderStreamedOptions {
pub fn new() -> ReaderStreamedOptions {
ReaderStreamedOptions { fix_header: false }
}
pub fn fix_header(&mut self, fix_header: bool) -> &mut Self {
self.fix_header = fix_header;
self
}
pub fn read_file<P>(&self, path: P) -> Result<StreamedNiftiObject<MaybeGzDecodedFile>>
where
P: AsRef<Path>,
{
let reader = open_file_maybe_gz(&path)?;
let mut obj = StreamedNiftiObject::from_file_impl(path, reader, None)?;
if self.fix_header {
obj.header.fix();
}
Ok(obj)
}
pub fn read_file_rank<P>(
&self,
path: P,
slice_rank: u16,
) -> Result<StreamedNiftiObject<MaybeGzDecodedFile>>
where
P: AsRef<Path>,
{
let reader = open_file_maybe_gz(&path)?;
let mut obj = StreamedNiftiObject::from_file_impl(path, reader, Some(slice_rank))?;
if self.fix_header {
obj.header.fix();
}
Ok(obj)
}
pub fn read_file_pair<P, Q>(
&self,
hdr_path: P,
vol_path: Q,
) -> Result<StreamedNiftiObject<MaybeGzDecodedFile>>
where
P: AsRef<Path>,
Q: AsRef<Path>,
{
let reader = open_file_maybe_gz(hdr_path)?;
let mut obj =
StreamedNiftiObject::from_file_pair_impl(reader, vol_path, Default::default())?;
if self.fix_header {
obj.header.fix();
}
Ok(obj)
}
pub fn read_file_pair_rank<P, Q>(
&self,
hdr_path: P,
vol_path: Q,
slice_rank: u16,
) -> Result<StreamedNiftiObject<MaybeGzDecodedFile>>
where
P: AsRef<Path>,
Q: AsRef<Path>,
{
let reader = open_file_maybe_gz(hdr_path)?;
let mut obj = StreamedNiftiObject::from_file_pair_impl(reader, vol_path, Some(slice_rank))?;
if self.fix_header {
obj.header.fix();
}
Ok(obj)
}
}
pub trait NiftiObject {
type Volume: NiftiVolume;
fn header(&self) -> &NiftiHeader;
fn header_mut(&mut self) -> &mut NiftiHeader;
fn extensions(&self) -> &ExtensionSequence;
fn volume(&self) -> &Self::Volume;
fn into_volume(self) -> Self::Volume;
}
#[derive(Debug, PartialEq, Clone)]
pub struct GenericNiftiObject<V> {
header: NiftiHeader,
extensions: ExtensionSequence,
volume: V,
}
impl<V> NiftiObject for GenericNiftiObject<V>
where
V: NiftiVolume,
{
type Volume = V;
fn header(&self) -> &NiftiHeader {
&self.header
}
fn header_mut(&mut self) -> &mut NiftiHeader {
&mut self.header
}
fn extensions(&self) -> &ExtensionSequence {
&self.extensions
}
fn volume(&self) -> &Self::Volume {
&self.volume
}
fn into_volume(self) -> Self::Volume {
self.volume
}
}
pub type InMemNiftiObject = GenericNiftiObject<InMemNiftiVolume>;
impl InMemNiftiObject {
#[deprecated(
since = "0.10.0",
note = "use `read_file` from `ReaderOptions` instead"
)]
pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Self> {
let gz = is_gz_file(&path);
let file = BufReader::new(File::open(&path)?);
if gz {
Self::from_file_impl(path, GzDecoder::new(file), Default::default())
} else {
Self::from_file_impl(path, file, Default::default())
}
}
#[deprecated(
since = "0.10.0",
note = "use `read_file_pair` from `ReaderOptions` instead"
)]
pub fn from_file_pair<P, Q>(hdr_path: P, vol_path: Q) -> Result<Self>
where
P: AsRef<Path>,
Q: AsRef<Path>,
{
let gz = is_gz_file(&hdr_path);
let file = BufReader::new(File::open(&hdr_path)?);
if gz {
Self::from_file_pair_impl(GzDecoder::new(file), vol_path, Default::default())
} else {
Self::from_file_pair_impl(file, vol_path, Default::default())
}
}
}
pub type StreamedNiftiObject<R> = GenericNiftiObject<StreamedNiftiVolume<R>>;
impl StreamedNiftiObject<MaybeGzDecodedFile> {
#[deprecated(
since = "0.10.0",
note = "use `read_file` from `ReaderStreamedOptions` instead"
)]
pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Self> {
let reader = open_file_maybe_gz(&path)?;
Self::from_file_impl(path, reader, None)
}
#[deprecated(
since = "0.10.0",
note = "use `read_file_rank` from `ReaderStreamedOptions` instead"
)]
pub fn from_file_rank<P: AsRef<Path>>(path: P, slice_rank: u16) -> Result<Self> {
let reader = open_file_maybe_gz(&path)?;
Self::from_file_impl(path, reader, Some(slice_rank))
}
#[deprecated(
since = "0.10.0",
note = "use `read_file_pair` from `ReaderStreamedOptions` instead"
)]
pub fn from_file_pair<P, Q>(hdr_path: P, vol_path: Q) -> Result<Self>
where
P: AsRef<Path>,
Q: AsRef<Path>,
{
let reader = open_file_maybe_gz(hdr_path)?;
Self::from_file_pair_impl(reader, vol_path, Default::default())
}
#[deprecated(
since = "0.10.0",
note = "use `read_file_pair_rank` from `ReaderStreamedOptions` instead"
)]
pub fn from_file_pair_rank<P, Q>(hdr_path: P, vol_path: Q, slice_rank: u16) -> Result<Self>
where
P: AsRef<Path>,
Q: AsRef<Path>,
{
let reader = open_file_maybe_gz(hdr_path)?;
Self::from_file_pair_impl(reader, vol_path, Some(slice_rank))
}
}
impl<V> GenericNiftiObject<V> {
pub fn from_reader<R>(mut source: R) -> Result<Self>
where
R: Read,
V: FromSource<R>,
{
let header = NiftiHeader::from_reader(&mut source)?;
if &header.magic == MAGIC_CODE_NI1 {
return Err(NiftiError::NoVolumeData);
}
let extender = Extender::from_reader(&mut source)?;
let (volume, extensions) = GenericNiftiObject::from_reader_with_extensions(
source,
&header,
extender,
Default::default(),
)?;
Ok(GenericNiftiObject {
header,
extensions,
volume,
})
}
fn from_reader_with_extensions<R>(
mut source: R,
header: &NiftiHeader,
extender: Extender,
options: <V as FromSourceOptions>::Options,
) -> Result<(V, ExtensionSequence)>
where
R: Read,
V: FromSource<R>,
{
let len = header.vox_offset as usize;
let len = if len < 352 { 0 } else { len - 352 };
let ext = {
let source = ByteOrdered::runtime(&mut source, header.endianness);
ExtensionSequence::from_reader(extender, source, len)?
};
Ok((V::from_reader(source, &header, options)?, ext))
}
fn from_file_impl<P, R>(
path: P,
mut stream: R,
options: <V as FromSourceOptions>::Options,
) -> Result<Self>
where
P: AsRef<Path>,
R: Read,
V: FromSource<R>,
V: FromSource<MaybeGzDecodedFile>,
{
let header = NiftiHeader::from_reader(&mut stream)?;
let (volume, ext) = if &header.magic == MAGIC_CODE_NI1 {
let extender = Extender::from_reader_optional(&mut stream)?.unwrap_or_default();
let img_path = path.as_ref().to_path_buf();
let mut img_path_gz = into_img_file_gz(img_path);
Self::from_file_with_extensions(&img_path_gz, &header, extender, options.clone())
.or_else(|e| {
match e {
NiftiError::Io(ref io_e) if io_e.kind() == io::ErrorKind::NotFound => {
let has_ext = img_path_gz.set_extension("");
debug_assert!(has_ext);
Self::from_file_with_extensions(img_path_gz, &header, extender, options)
}
e => Err(e),
}
})
.map_err(|e| {
if let NiftiError::Io(io_e) = e {
NiftiError::MissingVolumeFile(io_e)
} else {
e
}
})?
} else {
let extender = Extender::from_reader(&mut stream)?;
let len = header.vox_offset as usize;
let len = if len < 352 { 0 } else { len - 352 };
let ext = {
let stream = ByteOrdered::runtime(&mut stream, header.endianness);
ExtensionSequence::from_reader(extender, stream, len)?
};
let volume = FromSource::from_reader(stream, &header, options)?;
(volume, ext)
};
Ok(GenericNiftiObject {
header,
extensions: ext,
volume,
})
}
fn from_file_pair_impl<S, Q>(
mut hdr_stream: S,
vol_path: Q,
options: <V as FromSourceOptions>::Options,
) -> Result<Self>
where
S: Read,
Q: AsRef<Path>,
V: FromSource<MaybeGzDecodedFile>,
{
let header = NiftiHeader::from_reader(&mut hdr_stream)?;
let extender = Extender::from_reader_optional(hdr_stream)?.unwrap_or_default();
let (volume, extensions) =
Self::from_file_with_extensions(vol_path, &header, extender, options)?;
Ok(GenericNiftiObject {
header,
extensions,
volume,
})
}
fn from_file_with_extensions<P>(
path: P,
header: &NiftiHeader,
extender: Extender,
options: <V as FromSourceOptions>::Options,
) -> Result<(V, ExtensionSequence)>
where
P: AsRef<Path>,
V: FromSource<MaybeGzDecodedFile>,
{
let reader = open_file_maybe_gz(path)?;
Self::from_reader_with_extensions(reader, &header, extender, options)
}
}