use std::{
fs::File,
io,
io::{ErrorKind, Read, Seek, SeekFrom, Write},
path::Path,
};
use crate::reflection::{MapStatus, Reflection, ReflectionsData};
const HEADER_BLOCK: usize = 80;
const INITIAL_BYTES: [u8; 4] = *b"MTZ ";
#[macro_export]
macro_rules! parse_le {
($bytes:expr, $t:ty, $range:expr) => {{ <$t>::from_le_bytes($bytes[$range].try_into().unwrap()) }};
}
#[macro_export]
macro_rules! copy_le {
($dest:expr, $src:expr, $range:expr) => {{ $dest[$range].copy_from_slice(&$src.to_le_bytes()) }};
}
fn io_err(text: &str) -> io::Error {
io::Error::new(ErrorKind::InvalidData, text)
}
pub fn read_main_headers(buf: &[u8], save_headers: Option<&mut Vec<String>>) -> io::Result<()> {
let mut header_offset = 0;
const HEADER_SIZE: usize = 80;
let mut buf2 = [0u8; HEADER_SIZE];
let mut ncol = 0;
let mut i = 0;
loop {
let line = String::from_utf8_lossy(&buf[i..i + 80]).to_string();
if &line[..3] == "END" {
break;
}
println!("HEADER Type: {:?}", &line[..4]);
i += HEADER_SIZE;
}
Ok(())
}
impl ReflectionsData {
pub fn from_mtz(buf: &[u8]) -> io::Result<ReflectionsData> {
let mut pos = 0;
if buf[0..4] != INITIAL_BYTES {
return Err(io_err("Invalid MTZ start bytes; should be b'MTZ '."));
}
let header_addr = parse_le!(buf, u32, 4..8) as usize;
println!("Header addr: {:?}", header_addr);
let machine_stamp = parse_le!(buf, u32, 8..12);
let real_fmt = buf[8] >> 4;
let cplx_fmt = buf[8] & 0xf;
let int_fmt = buf[9] >> 4;
let char_fmt = buf[9] & 0xf;
let refl_data = &buf[21..header_addr];
let header_data = &buf[header_addr..];
read_main_headers(header_data, None)?;
println!("HEader data: {:x?}", &header_data[..100]);
let points = Vec::new();
Ok(Self {
space_group: "P 1".to_string(), cell_len_a: 0.,
cell_len_b: 0.,
cell_len_c: 0.,
cell_angle_alpha: 0.,
cell_angle_beta: 0.,
cell_angle_gamma: 0.,
points,
})
}
pub fn to_mtz(&self) -> Vec<u8> {
let mut result = Vec::new();
result.extend_from_slice(format!("{:<80}\n", "MTZ:V1.1").as_bytes());
result.extend(format!("TITLE {:<70}\n", "written by Rust").as_bytes());
result.extend(
format!(
"CELL {:8.3} {:8.3} {:8.3} {:7.3} {:7.3} {:7.3}{:10}\n",
self.cell_len_a,
self.cell_len_b,
self.cell_len_c,
self.cell_angle_alpha,
self.cell_angle_beta,
self.cell_angle_gamma,
""
)
.as_bytes(),
);
let cols = ["H", "K", "L", "F", "SIGF", "FREE"];
for (i, &c) in cols.iter().enumerate() {
result.extend(format!("COLUMN{:5}{:<8}{:>5}{:>5}\n", "", c, 1, i + 1).as_bytes());
}
result.extend("END\n".as_bytes());
while result.len() % HEADER_BLOCK != 0 {
result.push(b' ');
}
let mut bin = Vec::new();
for p in &self.points {
copy_le!(bin, p.h as f32, 0..4);
copy_le!(bin, p.k as f32, 4..8);
copy_le!(bin, p.l as f32, 8..12);
copy_le!(bin, p.amp as f32, 12..16);
copy_le!(bin, p.amp_uncertainty as f32, 20..24);
copy_le!(
bin,
if matches!(p.status, MapStatus::FreeSet) {
1.0
} else {
0.0
} as f32,
24..28
);
}
result.extend(bin);
result
}
}
pub fn load_mtz(path: &Path) -> io::Result<ReflectionsData> {
let mut file = File::open(path)?;
let mut buf = Vec::new();
file.read_to_end(&mut buf)?;
ReflectionsData::from_mtz(&buf)
}
pub fn save_mtz(data: &ReflectionsData, path: &Path) -> io::Result<()> {
let buf = data.to_mtz();
let mut file = File::open(path)?;
file.write_all(&buf)?;
Ok(())
}