use std::{ffi::OsStr, path::Path};
use crate::{Context, PDBError, StrictnessLevel};
use super::general::ReadResult;
#[derive(Debug, Clone, Copy, Default)]
pub enum Format {
Pdb,
Mmcif,
#[default]
Auto,
}
impl From<&str> for Format {
fn from(s: &str) -> Self {
match s {
"pdb" => Self::Pdb,
"mmcif" => Self::Mmcif,
_ => panic!("Unknown format: {}", s),
}
}
}
#[derive(Debug, Default)]
pub struct ReadOptions {
pub(crate) format: Format,
pub(crate) level: StrictnessLevel,
pub(crate) capitalise_chains: bool,
#[cfg(feature = "compression")]
pub(crate) decompress: bool,
pub(crate) discard_hydrogens: bool,
pub(crate) only_first_model: bool,
pub(crate) only_atomic_coords: bool,
}
impl ReadOptions {
pub fn new() -> Self {
Self::default()
}
pub fn set_format(&mut self, format: Format) -> &mut Self {
self.format = format;
self
}
pub fn guess_format(&mut self, filename: &str) -> &mut Self {
if let Some((file_format, is_compressed)) = guess_format(filename) {
self.set_decompress(is_compressed).set_format(file_format)
} else {
self
}
}
pub fn set_level(&mut self, level: StrictnessLevel) -> &mut Self {
self.level = level;
self
}
pub fn set_capitalise_chains(&mut self, capitalise_chains: bool) -> &mut Self {
self.capitalise_chains = capitalise_chains;
self
}
#[cfg(feature = "compression")]
pub fn set_decompress(&mut self, decompress: bool) -> &mut Self {
self.decompress = decompress;
self
}
pub fn set_discard_hydrogens(&mut self, discard_hydrogens: bool) -> &mut Self {
self.discard_hydrogens = discard_hydrogens;
self
}
pub fn set_only_first_model(&mut self, only_first_model: bool) -> &mut Self {
self.only_first_model = only_first_model;
self
}
pub fn set_only_atomic_coords(&mut self, only_atomic_coords: bool) -> &mut Self {
self.only_atomic_coords = only_atomic_coords;
self
}
pub fn read(&self, path: impl AsRef<str>) -> ReadResult {
if self.decompress {
let filename = path.as_ref();
self.read_auto(filename)
} else {
match self.format {
Format::Pdb => super::pdb::open_pdb_with_options(path, self),
Format::Mmcif => super::mmcif::open_mmcif_with_options(path, self),
Format::Auto => self.read_auto(path),
}
}
}
fn read_auto(&self, path: impl AsRef<str>) -> ReadResult {
let filename = path.as_ref();
if let Some((file_format, is_compressed)) = guess_format(filename) {
if is_compressed {
let file = std::fs::File::open(filename).map_err(|_| {
vec![PDBError::new(
crate::ErrorLevel::BreakingError,
"Could not open file",
"Could not open the given file, make sure it exists and you have the correct permissions",
Context::show(filename),
)]
})?;
let decompressor = flate2::read::GzDecoder::new(file);
let reader = std::io::BufReader::new(decompressor);
match file_format {
Format::Pdb => {
super::pdb::open_pdb_raw_with_options(reader, Context::None, self)
}
Format::Mmcif => super::mmcif::open_mmcif_raw_with_options(reader, self),
Format::Auto => Err(vec![PDBError::new(
crate::ErrorLevel::BreakingError,
"Could not determine file type",
"Could not determine the type of the gzipped file, use .pdb.gz or .cif.gz",
Context::show(filename),
)]),
}
} else {
match file_format {
Format::Pdb => super::pdb::open_pdb_with_options(path, self),
Format::Mmcif => super::mmcif::open_mmcif_with_options(path, self),
_ => Err(vec![PDBError::new(
crate::ErrorLevel::BreakingError,
"Incorrect extension",
"Could not determine the type of the given file extension, make it .pdb or .cif",
Context::show(path.as_ref()),
)])
}
}
} else {
Err(vec![PDBError::new(
crate::ErrorLevel::BreakingError,
"Missing extension",
"The given file does not have an extension, make it .pdb or .cif",
Context::show(path.as_ref()),
)])
}
}
pub fn read_raw<T>(&self, input: std::io::BufReader<T>) -> ReadResult
where
T: std::io::Read,
{
match self.format {
Format::Pdb => super::pdb::open_pdb_raw_with_options(input, Context::None, self),
Format::Mmcif => super::mmcif::open_mmcif_raw_with_options(input, self),
Format::Auto => Err(vec![PDBError::new(
crate::ErrorLevel::BreakingError,
"Could not determine file type",
"Could not determine the type of the input stream, set self.format",
Context::None,
)]),
}
}
}
fn guess_format(filename: &str) -> Option<(Format, bool)> {
let path = Path::new(filename);
match path.extension().and_then(OsStr::to_str) {
Some("pdb") | Some("pdb1") => Some((Format::Pdb, false)),
Some("cif") | Some("mmcif") => Some((Format::Mmcif, false)),
Some("gz") => {
let path_ext = Path::new(path.file_stem().and_then(OsStr::to_str).unwrap_or(""));
match path_ext.extension().and_then(OsStr::to_str) {
Some("pdb") | Some("pdb1") => Some((Format::Pdb, true)),
Some("cif") | Some("mmcif") => Some((Format::Mmcif, true)),
_ => None,
}
}
_ => None,
}
}