honeycomb_core/cmap/builder/structure.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
//! Module short description
//!
//! Should you interact with this module directly?
//!
//! Content description if needed
// ------ IMPORTS
#[cfg(feature = "utils")]
use super::GridDescriptor;
use crate::prelude::{AttributeBind, CMap2};
use crate::{attributes::AttrStorageManager, geometry::CoordsFloat};
use thiserror::Error;
#[cfg(feature = "io")]
use vtkio::Vtk;
// ------ CONTENT
// --- common error enum
/// Builder-level error enum
///
/// This enum is used to describe all non-panic errors that can occur when using a builder
/// structure.
#[derive(Error, Debug)]
pub enum BuilderError {
// grid-related variants
/// One or multiple of the specified grid characteristics are invalid.
#[error("invalid grid parameters - {0}")]
InvalidGridParameters(&'static str),
/// The builder is missing one or multiple parameters to generate the grid.
#[error("insufficient parameters - please specifiy at least 2")]
MissingGridParameters,
// vtk-related variants
/// Specified VTK file contains inconsistent data.
#[error("invalid/corrupted data in the vtk file - {0}")]
BadVtkData(&'static str),
/// Specified VTK file contains unsupported data.
#[error("unsupported data in the vtk file - {0}")]
UnsupportedVtkData(&'static str),
}
// --- main struct
/// Combinatorial map builder structure.
///
/// # Example
///
/// ```rust
/// # use honeycomb_core::prelude::BuilderError;
/// # fn main() -> Result<(), BuilderError> {
/// use honeycomb_core::prelude::{CMap2, CMapBuilder};
///
/// let builder = CMapBuilder::default().n_darts(10);
/// let map: CMap2<f64> = builder.build()?;
///
/// assert_eq!(map.n_darts(), 11); // 10 + null dart = 11
///
/// # Ok(())
/// # }
/// ```
#[derive(Default)]
pub struct CMapBuilder<T>
where
T: CoordsFloat,
{
#[cfg(feature = "io")]
pub(super) vtk_file: Option<Vtk>,
#[cfg(feature = "utils")]
pub(super) grid_descriptor: Option<GridDescriptor<T>>,
pub(super) attributes: AttrStorageManager,
pub(super) n_darts: usize,
pub(super) coordstype: std::marker::PhantomData<T>,
}
// --- setters
impl<T: CoordsFloat> CMapBuilder<T> {
/// Set the number of dart that the created map will contain.
#[must_use = "unused builder object, consider removing this method call"]
pub fn n_darts(mut self, n_darts: usize) -> Self {
self.n_darts = n_darts;
self
}
/// Add the specified attribute that the created map will contain.
///
/// Each attribute must be uniquely typed, i.e. a single type or struct cannot be added twice
/// to the builder / map. This includes type aliases as these are not distinct from the
/// compiler's perspective.
///
/// If you have multiple attributes that are represented using the same data type, you may want
/// to look into the **Newtype** pattern
/// [here](https://rust-unofficial.github.io/patterns/patterns/behavioural/newtype.html)
/// and [here](https://doc.rust-lang.org/rust-by-example/generics/new_types.html)
#[must_use = "unused builder object, consider removing this method call"]
pub fn add_attribute<A: AttributeBind + 'static>(mut self) -> Self {
self.attributes.add_storage::<A>(self.n_darts);
self
}
}
// --- build methods
impl<T: CoordsFloat> CMapBuilder<T> {
#[allow(clippy::missing_errors_doc)]
/// Consumes the builder and produce a [`CMap2`] object.
///
/// # Return / Errors
///
/// This method return a `Result` taking the following values:
/// - `Ok(map: CMap2)` -- Map generation was successful.
/// - `Err(BuilderError)` -- There was an error during construction. See [`BuilderError`] for
/// details.
///
/// # Panics
///
/// This method may panic if type casting goes wrong during parameters parsing.
///
/// # Example
///
/// See [`CMapBuilder`] example.
pub fn build(self) -> Result<CMap2<T>, BuilderError> {
#[cfg(feature = "io")]
if let Some(vfile) = self.vtk_file {
// build from vtk
// this routine should return a Result instead of the map directly
return super::io::build_2d_from_vtk(vfile, self.attributes);
}
#[cfg(feature = "utils")]
if let Some(gridb) = self.grid_descriptor {
// build from grid descriptor
let split = gridb.split_quads;
return gridb.parse_2d().map(|(origin, ns, lens)| {
if split {
super::grid::building_routines::build_2d_splitgrid(
origin,
ns,
lens,
self.attributes,
)
} else {
super::grid::building_routines::build_2d_grid(origin, ns, lens, self.attributes)
}
});
}
Ok(CMap2::new_with_undefined_attributes(
self.n_darts,
self.attributes,
))
}
}