1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
// Chemfiles, a modern library for chemistry file reading and writing
// Copyright (C) 2015-2020 Guillaume Fraux -- BSD licensed
use std::convert::TryInto;
use std::ffi::CStr;
use std::path::Path;
use chemfiles_sys::{chfl_format_metadata, chfl_formats_list, chfl_free, chfl_guess_format};
use crate::errors::check_success;
use crate::{errors::check, Error};
/// `FormatMetadata` contains metadata associated with one format.
#[allow(clippy::struct_excessive_bools)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct FormatMetadata {
/// Name of the format.
pub name: &'static str,
/// Extension associated with the format.
pub extension: Option<&'static str>,
/// Extended, user-facing description of the format.
pub description: &'static str,
/// URL pointing to the format definition/reference.
pub reference: &'static str,
/// Is reading files in this format implemented?
pub read: bool,
/// Is writing files in this format implemented?
pub write: bool,
/// Does this format support in-memory IO?
pub memory: bool,
/// Does this format support storing atomic positions?
pub positions: bool,
/// Does this format support storing atomic velocities?
pub velocities: bool,
/// Does this format support storing unit cell information?
pub unit_cell: bool,
/// Does this format support storing atom names or types?
pub atoms: bool,
/// Does this format support storing bonds between atoms?
pub bonds: bool,
/// Does this format support storing residues?
pub residues: bool,
}
impl FormatMetadata {
pub(crate) fn from_raw(raw: &chfl_format_metadata) -> Self {
let str_from_ptr = |ptr| unsafe { CStr::from_ptr(ptr).to_str().expect("Invalid Rust str from C") };
let extension = if raw.extension.is_null() {
None
} else {
Some(str_from_ptr(raw.extension))
};
Self {
name: str_from_ptr(raw.name),
extension,
description: str_from_ptr(raw.description),
reference: str_from_ptr(raw.reference),
read: raw.read,
write: raw.write,
memory: raw.memory,
positions: raw.positions,
velocities: raw.velocities,
unit_cell: raw.unit_cell,
atoms: raw.atoms,
bonds: raw.bonds,
residues: raw.residues,
}
}
}
/// Get the list of formats known by chemfiles, as well as all associated metadata.
///
/// # Example
/// ```
/// let formats = chemfiles::formats_list();
/// println!("chemfiles supports {} formats:", formats.len());
/// for format in &formats {
/// println!(
/// " {:<15} {}",
/// format.name,
/// format.extension.as_deref().unwrap_or("")
/// );
/// }
/// ```
#[must_use]
pub fn formats_list() -> Vec<FormatMetadata> {
let mut formats = std::ptr::null_mut();
let mut count: u64 = 0;
let formats_slice = unsafe {
check_success(chfl_formats_list(&mut formats, &mut count));
std::slice::from_raw_parts(formats, count.try_into().expect("failed to convert u64 to usize"))
};
let formats_vec = formats_slice.iter().map(FormatMetadata::from_raw).collect();
unsafe {
let _ = chfl_free(formats as *const _);
}
return formats_vec;
}
#[allow(clippy::doc_markdown)]
/// Get the format that chemfiles would use to read a file at the given
/// ``path``.
///
/// The format is mostly guessed from the path extension, chemfiles only tries
/// to read the file to distinguish between CIF and mmCIF files. Opening the
/// file using the returned format string might still fail. For example, it will
/// fail if the file is not actually formatted according to the guessed format;
/// or the format/compression combination is not supported (e.g. `XTC / GZ` will
/// not work since the XTC reader does not support compressed files).
///
/// The returned format is represented in a way compatible with the various
/// `Trajectory` constructors, i.e. `"<format name> [/ <compression>]"`, where
/// compression is optional.
///
/// # Errors
///
/// This function returns an error if the file format couldn't be guessed.
///
/// # Panics
///
/// This function panics if the path can't be converted to a Unicode string.
///
/// # Examples
/// ```
/// let format = chemfiles::guess_format("trajectory.xyz.xz").unwrap();
/// assert_eq!(format, "XYZ / XZ");
///
/// let format = chemfiles::guess_format("trajectory.nc").unwrap();
/// assert_eq!(format, "Amber NetCDF");
///
/// let format = chemfiles::guess_format("trajectory.unknown.format");
/// assert!(format.is_err());
/// ```
pub fn guess_format<P>(path: P) -> Result<String, Error>
where
P: AsRef<Path>,
{
let path = path.as_ref().to_str().expect("couldn't convert path to Unicode");
let path = crate::strings::to_c(path);
let mut buffer = vec![0; 128];
unsafe {
check(chfl_guess_format(
path.as_ptr(),
buffer.as_mut_ptr(),
buffer.len() as u64,
))?;
}
Ok(crate::strings::from_c(buffer.as_ptr()))
}