#[cfg(feature = "mmcif")]
use formats::{cif_write_findings, write_mmcif_to_with_options};
use molframe_core::ExecutionContext;
use molframe_core::diagnostic::{Code, Diagnostic, Findings};
use molframe_core::io::{Format, InputBuffer, OutputOptions, OutputSink, ReadOptions};
use std::io::Write;
use std::path::Path;
use crate::structure::Structure;
mod formats;
pub use read_in::{read_buffer_in, read_bytes_in, read_with_options_in};
#[cfg(feature = "geometry")]
pub use formats::transform;
#[cfg(feature = "chemistry")]
pub use formats::{open_component_dictionary, read_component_dictionary};
#[cfg(feature = "mmcif")]
pub use formats::{
read_document, write_mmcif, write_mmcif_with_findings, write_mmcif_with_options,
};
#[cfg(feature = "bcif")]
pub use formats::{write_bcif, write_bcif_with_findings, write_bcif_with_options};
#[cfg(feature = "pdb")]
pub use formats::{
write_pdb, write_pdb_with_findings, write_pdbqt_with_findings, write_pqr_with_findings,
};
#[cfg(feature = "mmcif")]
mod cif_family;
mod dispatch;
#[cfg(feature = "mmcif")]
mod extensions;
#[cfg(feature = "pdb")]
mod pdb_symmetry;
mod read_in;
#[cfg(feature = "chemistry")]
mod small_molecule;
#[cfg(any(
feature = "mmcif",
feature = "pdb",
feature = "bcif",
feature = "modelcif"
))]
mod structure_batches;
#[cfg(any(
feature = "mmcif",
feature = "pdb",
feature = "bcif",
feature = "modelcif"
))]
pub use structure_batches::{StructureBatchReader, open_structure_batches};
#[cfg(feature = "mmcif")]
use dispatch::unsupported;
use dispatch::{dispatch_read, unsupported_writer};
pub fn read(path: impl AsRef<Path>) -> Result<Structure, Findings> {
read_with_diagnostics(path).map(|(structure, _)| structure)
}
pub fn read_with_diagnostics(
path: impl AsRef<Path>,
) -> Result<(Structure, Vec<Diagnostic>), Findings> {
read_with_options(path, &ReadOptions::new())
}
pub fn read_with_options(
path: impl AsRef<Path>,
options: &ReadOptions,
) -> Result<(Structure, Vec<Diagnostic>), Findings> {
let path = path.as_ref();
let input = InputBuffer::open(path, options.limits).map_err(Findings::from)?;
let name = path.file_name().and_then(|name| name.to_str());
read_buffer(&input, name, options)
}
pub fn read_bytes(
bytes: Vec<u8>,
name: Option<&str>,
options: &ReadOptions,
) -> Result<(Structure, Vec<Diagnostic>), Findings> {
let input = InputBuffer::from_encoded_bytes(bytes, options.limits).map_err(Findings::from)?;
read_buffer(&input, name, options)
}
pub fn write(path: impl AsRef<Path>, structure: &Structure) -> Result<(), Findings> {
write_with_options(path, structure, &WriteOptions::canonical())
}
pub fn write_with_options(
path: impl AsRef<Path>,
structure: &Structure,
options: &WriteOptions,
) -> Result<(), Findings> {
let path = path.as_ref();
let name = path.file_name().and_then(|name| name.to_str());
let Some(format) = name.and_then(Format::from_name) else {
return Err(Findings::from(
Diagnostic::new(Code::E1001).with_context("name", path.display().to_string()),
));
};
write_as(path, structure, format, options)
}
pub fn write_as(
path: impl AsRef<Path>,
structure: &Structure,
format: Format,
options: &WriteOptions,
) -> Result<(), Findings> {
let path = path.as_ref();
let mut output = OutputSink::create(path, options.output).map_err(Findings::from)?;
if let Err(findings) = write_stream(&mut output, structure, format, options) {
drop(output);
let _ = std::fs::remove_file(path);
return Err(findings);
}
output.finish().map_err(Findings::from)
}
#[derive(Clone, Debug)]
pub struct WriteOptions {
output: OutputOptions,
#[cfg(feature = "mmcif")]
cif: molframe_cif::CifWriteOptions,
#[cfg(feature = "pdb")]
pdb: molframe_pdb::PdbOptions,
}
impl WriteOptions {
#[must_use]
pub fn canonical() -> Self {
Self {
output: OutputOptions::default(),
#[cfg(feature = "mmcif")]
cif: molframe_cif::CifWriteOptions::new(),
#[cfg(feature = "pdb")]
pdb: molframe_pdb::PdbOptions::new(),
}
}
#[must_use]
pub fn with_output(mut self, output: OutputOptions) -> Self {
self.output = output;
self
}
#[cfg(feature = "mmcif")]
#[must_use]
pub fn with_cif(mut self, cif: molframe_cif::CifWriteOptions) -> Self {
self.cif = cif;
self
}
#[cfg(feature = "pdb")]
#[must_use]
pub fn with_pdb(mut self, pdb: molframe_pdb::PdbOptions) -> Self {
self.pdb = pdb;
self
}
#[must_use]
pub const fn output(&self) -> &OutputOptions {
&self.output
}
#[cfg(feature = "mmcif")]
#[must_use]
pub const fn cif(&self) -> &molframe_cif::CifWriteOptions {
&self.cif
}
#[cfg(feature = "pdb")]
#[must_use]
pub const fn pdb(&self) -> &molframe_pdb::PdbOptions {
&self.pdb
}
}
fn write_stream<W: Write>(
output: &mut W,
structure: &Structure,
format: Format,
options: &WriteOptions,
) -> Result<(), Findings> {
#[cfg(not(any(feature = "mmcif", feature = "bcif", feature = "pdb")))]
let _ = options;
#[cfg(not(any(
feature = "mmcif",
feature = "bcif",
feature = "pdb",
feature = "chemistry"
)))]
let _ = (output, structure);
match format {
#[cfg(feature = "mmcif")]
Format::Mmcif => write_mmcif_to_with_options(structure, options.cif(), output)
.map_err(|error| cif_write_findings(&error)),
#[cfg(feature = "bcif")]
Format::BinaryCif => molframe_bcif::write_structure_to_with_memory_limit(
structure.engine(),
&formats::complete_cif_options(structure.engine(), options.cif()),
options.output().memory_limit_bytes,
output,
)
.map_err(Findings::from),
#[cfg(feature = "pdb")]
Format::Pdb => molframe_pdb::write_to(
structure.engine(),
&formats::complete_pdb_options(structure.engine(), options.pdb()),
output,
)
.map_err(Findings::from),
#[cfg(feature = "pdb")]
Format::Mmtf => {
molframe_pdb::write_mmtf_to(structure.engine(), output).map_err(Findings::from)
}
#[cfg(feature = "pdb")]
Format::Pqr => molframe_pdb::write_pqr_to(structure.engine(), options.pdb(), output)
.map_err(Findings::from),
#[cfg(feature = "pdb")]
Format::Pdbqt => molframe_pdb::write_pdbqt_to(structure.engine(), options.pdb(), output)
.map_err(Findings::from),
#[cfg(feature = "chemistry")]
Format::Sdf => small_molecule::write_sdf_to(structure.engine(), output),
other => Err(unsupported_writer(other).into()),
}
}
pub fn read_buffer(
input: &InputBuffer,
name: Option<&str>,
options: &ReadOptions,
) -> Result<(Structure, Vec<Diagnostic>), Findings> {
read_buffer_in(input, name, options, &ExecutionContext::default())
}
#[cfg(all(feature = "chemistry", feature = "spatial"))]
pub fn perceive(structure: &Structure) -> Result<(Structure, Vec<Diagnostic>), Findings> {
perceive_in(structure, &ExecutionContext::default())
}
#[cfg(all(feature = "chemistry", feature = "spatial"))]
pub fn perceive_in(
structure: &Structure,
context: &ExecutionContext,
) -> Result<(Structure, Vec<Diagnostic>), Findings> {
enrich_read(
Ok((structure.engine().clone(), Vec::new())),
&ReadOptions::new(),
context,
)
.map(|(structure, diagnostics)| (structure.into(), diagnostics))
}
#[cfg(all(feature = "chemistry", feature = "spatial"))]
use enrich::enrich_read;
#[cfg(not(all(feature = "chemistry", feature = "spatial")))]
type CoreRead = Result<(molframe_core::structure::Structure, Vec<Diagnostic>), Findings>;
#[cfg(not(all(feature = "chemistry", feature = "spatial")))]
fn enrich_read(result: CoreRead, _options: &ReadOptions, _context: &ExecutionContext) -> CoreRead {
result
}
#[cfg(all(feature = "chemistry", feature = "spatial"))]
mod enrich;
#[cfg(all(test, feature = "pdb"))]
#[path = "facade/input_digest_tests.rs"]
mod input_digest_tests;
#[cfg(all(test, feature = "pdb"))]
#[path = "facade/pdb_symmetry_tests.rs"]
mod pdb_symmetry_tests;
#[cfg(all(test, feature = "chemistry", feature = "mmcif", feature = "pdb"))]
#[path = "facade/small_molecule_tests.rs"]
mod small_molecule_tests;
#[cfg(test)]
#[path = "facade/facade_tests.rs"]
mod tests;
#[cfg(all(test, feature = "pdb"))]
#[path = "facade/write_as_tests.rs"]
mod write_as_tests;
#[cfg(all(test, feature = "pdb", feature = "gzip"))]
#[path = "facade/compressed_tests.rs"]
mod compressed_tests;