use std::{collections::HashMap, num::ParseIntError, path::Path};
use itertools::Itertools;
use thiserror::Error;
use crate::prelude::*;
pub struct NdxFile {
groups: HashMap<String, SVec>,
}
impl NdxFile {
pub fn new(path: impl AsRef<Path>) -> Result<Self, SelectionError> {
let path = path.as_ref();
let ndx_str = std::fs::read_to_string(path)
.map_err(|e| NdxError::NdxIo(path.to_owned(), e))?;
let mut groups = HashMap::new();
let mut current_group = None;
let mut current_numbers = Vec::new();
for line in ndx_str.lines() {
let line = line.trim();
if line.is_empty() {
continue;
}
if line.starts_with('[') && line.ends_with(']') {
if let Some(group_name) = current_group.take() {
if !current_numbers.is_empty() {
groups.insert(group_name, current_numbers.into());
}
}
let group_name = line[1..line.len()-1].trim().to_string();
current_group = Some(group_name);
current_numbers = Vec::new();
} else if let Some(group_name) = ¤t_group {
current_numbers.extend(
line.split_whitespace()
.map(|s| s.parse::<usize>())
.map_ok(|i| i - 1) .collect::<Result<Vec<_>, _>>()
.map_err(|e| NdxError::Parse(group_name.clone(), e))?,
);
} else {
return Err(NdxError::MalformedNdxFile(path.to_owned()))?;
}
}
if let Some(group_name) = current_group {
if !current_numbers.is_empty() {
groups.insert(group_name, current_numbers.into());
}
}
Ok(Self { groups })
}
pub fn get_group(&self, name: impl AsRef<str>) -> Result<&SVec, SelectionError> {
let gr = name.as_ref();
Ok(self.groups.get(gr).ok_or_else(|| NdxError::NoGroup(gr.to_owned()))?)
}
pub fn get_group_as_sel(&self, name: impl AsRef<str>, src: &System) -> Result<Sel, SelectionError> {
let gr = name.as_ref();
let ind = self.groups.get(gr).ok_or_else(|| NdxError::NoGroup(gr.to_owned()))?;
Ok(src.select(ind)?)
}
}
#[derive(Debug, Error)]
pub enum NdxError {
#[error("group {0} not found")]
NoGroup(String),
#[error("group {0} is empty")]
EmptyGroup(String),
#[error("index parse error in group {0}")]
Parse(String, #[source] ParseIntError),
#[error("error reading ndx file {0}")]
NdxIo(std::path::PathBuf, #[source] std::io::Error),
#[error("malformed ndx file {0}")]
MalformedNdxFile(std::path::PathBuf),
}