use std::fs::File;
use std::io::Read;
use thiserror::Error;
use vtkio::Vtk;
use crate::attributes::{AttrStorageManager, AttributeBind};
use crate::cmap::{CMap2, CMap3};
use crate::geometry::CoordsFloat;
use super::io::CMapFile;
#[derive(Error, Debug, PartialEq, Eq)]
pub enum BuilderError {
#[error("error parsing a value in one of the cmap file section - {0}")]
BadValue(&'static str),
#[error("error parsing the cmap file meta section - {0}")]
BadMetaData(&'static str),
#[error("duplicated section in cmap file - {0}")]
DuplicatedSection(String),
#[error("inconsistent data - {0}")]
InconsistentData(&'static str),
#[error("required section missing in cmap file - {0}")]
MissingSection(&'static str),
#[error("unknown header in cmap file - {0}")]
UnknownHeader(String),
#[error("invalid/corrupted data in the vtk file - {0}")]
BadVtkData(&'static str),
#[error("unsupported data in the vtk file - {0}")]
UnsupportedVtkData(&'static str),
}
pub struct CMapBuilder<const D: usize> {
builder_kind: BuilderType,
attributes: AttrStorageManager,
}
enum BuilderType {
CMap(CMapFile),
FreeDarts(usize),
Vtk(Vtk),
}
#[doc(hidden)]
pub trait Builder<T: CoordsFloat> {
type MapType;
fn build(self) -> Result<Self::MapType, BuilderError>;
}
impl<T: CoordsFloat> Builder<T> for CMapBuilder<2> {
type MapType = CMap2<T>;
fn build(self) -> Result<Self::MapType, BuilderError> {
match self.builder_kind {
BuilderType::CMap(cfile) => super::io::build_2d_from_cmap_file(cfile, self.attributes),
BuilderType::FreeDarts(n_darts) => Ok(CMap2::new_with_undefined_attributes(
n_darts,
self.attributes,
)),
BuilderType::Vtk(vfile) => super::io::build_2d_from_vtk(vfile, self.attributes),
}
}
}
impl<T: CoordsFloat> Builder<T> for CMapBuilder<3> {
type MapType = CMap3<T>;
fn build(self) -> Result<Self::MapType, BuilderError> {
match self.builder_kind {
BuilderType::CMap(cfile) => super::io::build_3d_from_cmap_file(cfile, self.attributes),
BuilderType::FreeDarts(n_darts) => Ok(CMap3::new_with_undefined_attributes(
n_darts,
self.attributes,
)),
BuilderType::Vtk(_vfile) => unimplemented!(),
}
}
}
impl<const D: usize> CMapBuilder<D> {
#[must_use = "unused builder object"]
pub fn from_n_darts_and_attributes(n_darts: usize, other: Self) -> Self {
Self {
builder_kind: BuilderType::FreeDarts(n_darts),
attributes: other.attributes,
}
}
#[must_use = "unused builder object"]
pub fn from_n_darts(n_darts: usize) -> Self {
Self {
builder_kind: BuilderType::FreeDarts(n_darts),
attributes: AttrStorageManager::default(),
}
}
#[must_use = "unused builder object"]
pub fn from_cmap_file(file_path: impl AsRef<std::path::Path> + std::fmt::Debug) -> Self {
let mut f = File::open(file_path).expect("E: could not open specified file");
let mut buf = String::new();
f.read_to_string(&mut buf)
.expect("E: could not read content from file");
let cmap_file = CMapFile::try_from(buf).unwrap();
Self {
builder_kind: BuilderType::CMap(cmap_file),
attributes: AttrStorageManager::default(),
}
}
#[must_use = "unused builder object"]
pub fn from_vtk_file(file_path: impl AsRef<std::path::Path> + std::fmt::Debug) -> Self {
let vtk_file =
Vtk::import(file_path).unwrap_or_else(|e| panic!("E: failed to load file: {e:?}"));
Self {
builder_kind: BuilderType::Vtk(vtk_file),
attributes: AttrStorageManager::default(),
}
}
#[must_use = "unused builder object"]
pub fn add_attribute<A: AttributeBind + 'static>(mut self) -> Self {
self.attributes.add_storage::<A>(1);
self
}
#[allow(clippy::missing_errors_doc)]
#[allow(private_interfaces, private_bounds)]
pub fn build<T: CoordsFloat>(self) -> Result<<Self as Builder<T>>::MapType, BuilderError>
where
Self: Builder<T>,
{
Builder::build(self)
}
}