#![allow(clippy::match_bool)]
use std::{borrow::Cow, collections::HashMap, ops::Deref};
#[must_use]
pub fn escape(s: &str) -> Cow<'_, str> {
let needs_quoting = s.chars().any(|c| {
matches!(
c,
'"' | '\\' | '\n' | ' ' | '=' | ',' | '[' | ']' | '{' | '}'
)
});
if !needs_quoting {
return Cow::Borrowed(s);
}
let mut out = String::with_capacity(s.len() + 4);
out.push('"');
for c in s.chars() {
match c {
'\n' => out.push_str("\\n"),
'\\' => out.push_str("\\\\"),
'"' => out.push_str("\\\""),
_ => out.push(c),
}
}
out.push('"');
Cow::Owned(out)
}
#[derive(Debug, Default, Copy, Clone)]
pub struct Integer(i32);
#[derive(Debug, Default, Copy, Clone)]
pub struct FloatNum(f64);
#[derive(Debug, Default, Copy, Clone)]
pub struct Boolean(bool);
#[derive(Debug, Default, Clone)]
pub struct Text(String);
impl Deref for Integer {
type Target = i32;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl Deref for FloatNum {
type Target = f64;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl Deref for Boolean {
type Target = bool;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl Deref for Text {
type Target = str;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl From<i32> for Integer {
fn from(value: i32) -> Self {
Self(value)
}
}
impl From<f64> for FloatNum {
fn from(value: f64) -> Self {
Self(value)
}
}
impl From<bool> for Boolean {
fn from(value: bool) -> Self {
Self(value)
}
}
impl From<String> for Text {
fn from(value: String) -> Self {
Self(value)
}
}
impl From<&str> for Text {
fn from(value: &str) -> Self {
Self(value.to_string())
}
}
impl std::fmt::Display for Integer {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
impl std::fmt::Display for FloatNum {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if f.precision().is_some() {
std::fmt::Display::fmt(&self.0, f)
} else {
write!(f, "{:.8}", self.0)
}
}
}
impl std::fmt::Display for Boolean {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self.0 {
true => write!(f, "T"),
false => write!(f, "F"),
}
}
}
impl std::fmt::Display for Text {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let escaped = escape(&self.0);
f.pad(&escaped)
}
}
#[derive(Debug, Clone, Default)]
pub enum Value {
Integer(Integer),
Float(FloatNum),
Bool(Boolean),
Str(Text),
VecInteger(Vec<Integer>, u32),
VecFloat(Vec<FloatNum>, u32),
VecBool(Vec<Boolean>, u32),
VecText(Vec<Text>, u32),
MatrixInteger(Vec<Vec<Integer>>, (u32, u32)),
MatrixFloat(Vec<Vec<FloatNum>>, (u32, u32)),
MatrixBool(Vec<Vec<Boolean>>, (u32, u32)),
MatrixText(Vec<Vec<Text>>, (u32, u32)),
#[default]
Unsupported,
}
impl Value {
pub fn as_integer(self) -> Option<Integer> {
match self {
Value::Integer(i) => Some(i),
_ => None,
}
}
pub fn as_float(self) -> Option<FloatNum> {
match self {
Value::Float(i) => Some(i),
_ => None,
}
}
pub fn as_bool(self) -> Option<Boolean> {
match self {
Value::Bool(i) => Some(i),
_ => None,
}
}
pub fn as_text(self) -> Option<Text> {
match self {
Value::Str(i) => Some(i),
_ => None,
}
}
}
impl std::fmt::Display for Value {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
fn fmt_array<T: std::fmt::Display>(arr: &[T]) -> String {
arr.iter()
.map(std::string::ToString::to_string)
.collect::<Vec<_>>()
.join(", ")
}
fn fmt_matrix<T: std::fmt::Display>(matrix: &[Vec<T>]) -> String {
matrix
.iter()
.map(|row| format!("[{}]", fmt_array(row)))
.collect::<Vec<_>>()
.join(", ")
}
match self {
Value::Integer(v) => write!(f, "{v}"),
Value::Float(v) => write!(f, "{v}"),
Value::Bool(v) => write!(f, "{v}"),
Value::Str(v) => write!(f, "{v}"),
Value::VecInteger(arr, _) => write!(f, "[{}]", fmt_array(arr)),
Value::VecFloat(arr, _) => write!(f, "[{}]", fmt_array(arr)),
Value::VecBool(arr, _) => write!(f, "[{}]", fmt_array(arr)),
Value::VecText(arr, _) => write!(f, "[{}]", fmt_array(arr)),
Value::MatrixInteger(matrix, _) => write!(f, "[{}]", fmt_matrix(matrix)),
Value::MatrixFloat(matrix, _) => write!(f, "[{}]", fmt_matrix(matrix)),
Value::MatrixBool(matrix, _) => write!(f, "[{}]", fmt_matrix(matrix)),
Value::MatrixText(matrix, _) => write!(f, "[{}]", fmt_matrix(matrix)),
Value::Unsupported => write!(f, "<unsupported>"),
}
}
}
#[derive(Debug)]
pub struct DictHandler(pub Vec<(String, Value)>);
impl DictHandler {
#[must_use]
pub fn get(&self, key: &str) -> Option<&Value> {
for (k, v) in &self.0 {
if k.as_str() == key {
return Some(v);
}
}
None
}
}
impl<'a> DictHandler {
pub fn iter(&'a self) -> std::slice::Iter<'a, (String, Value)> {
self.into_iter()
}
}
impl<'a> IntoIterator for &'a DictHandler {
type Item = &'a (String, Value);
type IntoIter = std::slice::Iter<'a, (String, Value)>;
fn into_iter(self) -> Self::IntoIter {
self.0.iter()
}
}
#[derive(Debug)]
pub struct Frame {
natoms: u32,
info: DictHandler,
arrs: DictHandler,
}
impl Frame {
#[must_use]
pub fn new(natoms: u32, info: Vec<(String, Value)>, arrs: Vec<(String, Value)>) -> Self {
Self {
natoms,
info: DictHandler(info),
arrs: DictHandler(arrs),
}
}
#[must_use]
pub fn natoms(&self) -> u32 {
self.natoms
}
pub fn set_comment(&mut self, comment: &str) {
let newv = Value::Str(Text::from(comment));
if let Some((_, value)) = self.info.0.iter_mut().find(|(k, _)| k == "comment") {
*value = newv;
} else {
self.info.0.push(("comment".to_string(), newv));
};
}
#[must_use]
pub fn arrs(&self) -> HashMap<&str, &Value> {
let arrs = self.arrs.iter().map(|(k, v)| (k.as_str(), v));
arrs.collect::<HashMap<_, _>>()
}
#[must_use]
pub fn info(&self) -> HashMap<&str, &Value> {
self.info
.iter()
.map(|(k, v)| (k.as_str(), v))
.collect::<HashMap<_, _>>()
}
pub fn info_orderd(&self) -> Vec<(&str, &Value)> {
self.info
.iter()
.map(|(k, v)| (k.as_str(), v))
.collect::<Vec<(_, _)>>()
}
pub fn arrs_orderd(&self) -> Vec<(&str, &Value)> {
self.arrs
.iter()
.map(|(k, v)| (k.as_str(), v))
.collect::<Vec<(_, _)>>()
}
}