use crate::{error::ExtxyzError, Result};
use std::io::Write;
use extxyz_types::{escape, Frame, Value};
pub fn write_frames<W, I>(w: &mut W, frames: I) -> Result<usize>
where
W: Write,
I: IntoIterator<Item = Frame>,
{
let mut count = 0;
for frame in frames {
write_frame(w, &frame)?;
count += 1;
}
Ok(count)
}
pub fn write_frame<W>(w: &mut W, frame: &Frame) -> Result<()>
where
W: Write,
{
let natoms = frame.natoms();
writeln!(w, "{natoms}")?;
let info = frame.info_orderd();
let mut iter = info.iter().peekable();
while let Some((k, v)) = iter.next() {
if *k == "Properties" {
continue;
}
let s = escape(k);
write!(w, "{s}")?;
write!(w, "=")?;
if *k == "Lattice" {
match v {
Value::MatrixInteger(_, _) => {
write!(w, "{v}")?;
}
Value::MatrixFloat(_, _) => {
write!(w, "{v}")?;
}
_ => {
return Err(ExtxyzError::InvalidValue(
"Lattice must be a 3x3 int/float matrix",
));
}
}
} else {
write!(w, "{v}")?;
}
if iter.peek().is_some() {
write!(w, " ")?;
}
}
write!(w, "Properties=")?;
let mut s = String::new();
let arrs = frame.arrs_orderd();
let mut iter = arrs.iter().peekable();
while let Some((k, v)) = iter.next() {
s.push_str(k);
s.push(':');
match v {
Value::VecInteger(_, _) => s.push_str("I:1"),
Value::VecFloat(_, _) => s.push_str("R:1"),
Value::VecBool(_, _) => s.push_str("L:1"),
Value::VecText(_, _) => s.push_str("S:1"),
Value::MatrixInteger(_, shape) => s.push_str(format!("I:{}", shape.1).as_str()),
Value::MatrixFloat(_, shape) => s.push_str(format!("R:{}", shape.1).as_str()),
Value::MatrixBool(_, shape) => s.push_str(format!("L:{}", shape.1).as_str()),
Value::MatrixText(_, shape) => s.push_str(format!("S:{}", shape.1).as_str()),
_ => {
return Err(ExtxyzError::InvalidValue(
"arrs can only be vector or matrix",
));
}
}
if iter.peek().is_some() {
s.push(':');
}
}
write!(w, "{}", escape(&s))?;
writeln!(w)?;
for i in 0..natoms {
let mut iter = arrs.iter().peekable();
while let Some((_, v)) = iter.next() {
let i = i as usize;
match v {
Value::VecInteger(items, _) => write!(w, "{}", items[i])?,
Value::VecFloat(items, _) => write!(w, "{:>16.8}", items[i])?,
Value::VecBool(items, _) => write!(w, "{}", items[i])?,
Value::VecText(items, _) => {
let s = &items[i];
let sl = (*s).len();
if sl > 5 {
write!(w, "{1:<0$}", sl + 2, items[i])?
} else {
write!(w, "{:<5}", items[i])?
}
}
Value::MatrixInteger(items, _) => {
let s = &items[i];
let indent = " ";
let s = s
.iter()
.map(|i| format!("{i}"))
.collect::<Vec<_>>()
.join(indent);
write!(w, "{s}")?;
}
Value::MatrixFloat(items, _) => {
let s = &items[i];
let indent = " ";
let s = s
.iter()
.map(|i| {
let s = format!("{:>16.8}", i);
s
})
.collect::<Vec<_>>()
.join(indent);
write!(w, "{s}")?;
}
Value::MatrixBool(items, _) => {
let s = &items[i];
let indent = " ";
let s = s
.iter()
.map(|i| format!("{i}"))
.collect::<Vec<_>>()
.join(indent);
write!(w, "{s}")?;
}
Value::MatrixText(items, _) => {
let s = &items[i];
let indent = " ";
let s = s
.iter()
.map(|i| format!("{i}"))
.collect::<Vec<_>>()
.join(indent);
write!(w, "{s}")?;
}
_ => {
return Err(ExtxyzError::InvalidValue(
"arrs can only be vector or matrix",
));
}
}
if iter.peek().is_some() {
write!(w, " ")?; }
}
writeln!(w)?;
}
w.flush()?;
Ok(())
}
#[cfg(test)]
mod tests {
use std::io::{BufWriter, Cursor};
use crate::read_frame;
use super::*;
trait FrameNewExample {
fn new_example() -> Frame;
}
impl FrameNewExample for Frame {
fn new_example() -> Frame {
let inp = r#"4
key1=a key2=a/b key3=a@b key4="a@b"
Mg -4.25650 3.79180 -2.54123
C -1.15405 2.86652 -1.26699
C -5.53758 3.70936 0.63504
C -7.28250 4.71303 -3.82016
"#;
let mut rd = Cursor::new(inp.as_bytes());
read_frame(&mut rd).unwrap()
}
}
#[test]
fn test_write_frame() {
let frame = Frame::new_example();
let mut buf = Vec::new();
{
let mut w = BufWriter::new(&mut buf);
write_frame(&mut w, &frame).unwrap();
}
let s = String::from_utf8(buf).unwrap();
let expect = r#"4
key1=a key2=a/b key3=a@b key4=a@b Properties=species:S:1:pos:R:3
Mg -4.25650000 3.79180000 -2.54123000
C -1.15405000 2.86652000 -1.26699000
C -5.53758000 3.70936000 0.63504000
C -7.28250000 4.71303000 -3.82016000
"#;
assert_eq!(s, expect);
}
#[test]
fn test_write_frames_default() {
let inp = r#"4
key1=a key2=a/b key3=a@b key4="a@b"
Mg -4.25650 3.79180 -2.54123
C -1.15405 2.86652 -1.26699
C -5.53758 3.70936 0.63504
C -7.28250 4.71303 -3.82016
"#;
let rd = Cursor::new(inp.as_bytes());
let frame1 = read_frame(&mut rd.clone()).unwrap();
let frame2 = read_frame(&mut rd.clone()).unwrap();
let frames = vec![frame1, frame2];
let mut buf = Vec::new();
{
let mut w = BufWriter::new(&mut buf);
write_frames(&mut w, frames).unwrap();
}
let s = String::from_utf8(buf).unwrap();
let expect = r#"4
key1=a key2=a/b key3=a@b key4=a@b Properties=species:S:1:pos:R:3
Mg -4.25650000 3.79180000 -2.54123000
C -1.15405000 2.86652000 -1.26699000
C -5.53758000 3.70936000 0.63504000
C -7.28250000 4.71303000 -3.82016000
4
key1=a key2=a/b key3=a@b key4=a@b Properties=species:S:1:pos:R:3
Mg -4.25650000 3.79180000 -2.54123000
C -1.15405000 2.86652000 -1.26699000
C -5.53758000 3.70936000 0.63504000
C -7.28250000 4.71303000 -3.82016000
"#;
assert_eq!(s, expect);
}
}