conspire 0.7.0

The Rust interface to conspire.
Documentation
use crate::geometry::grid::Grid;
use std::{
    array::from_fn,
    fmt::Display,
    fs::read_to_string,
    io::{Error, ErrorKind, Result},
    path::Path,
    str::FromStr,
};

pub(super) fn read<const D: usize, T, P>(path: P, nel: Vec<usize>) -> Result<Grid<D, T>>
where
    T: FromStr,
    <T as FromStr>::Err: Display,
    P: AsRef<Path>,
{
    let nel: [usize; D] = nel.clone().try_into().map_err(|_| {
        Error::new(
            ErrorKind::InvalidInput,
            format!(
                "SPN nel has {} axes but Grid was asked for D={D}",
                nel.len()
            ),
        )
    })?;
    let text = read_to_string(path)?;
    let data = text
        .split_whitespace()
        .map(|token| {
            token
                .parse::<T>()
                .map_err(|error| Error::new(ErrorKind::InvalidData, error.to_string()))
        })
        .collect::<Result<Vec<T>>>()?;
    let total: usize = nel.iter().product();
    if data.len() != total {
        return Err(Error::new(
            ErrorKind::InvalidData,
            format!(
                "SPN file has {} values but nel {:?} expects {total}",
                data.len(),
                from_fn::<_, D, _>(|axis| nel[axis]),
            ),
        ));
    }
    Ok(Grid::new(data, nel))
}