use core::hash::Hash;
use crate::Catalog;
use crate::assets::Assets;
use crate::mesh::{Geometry, MeshError};
pub trait Meshes: Catalog + Hash + Eq + Clone + 'static {
#[doc(hidden)]
fn build(&self, assets: &Assets) -> Result<Geometry, Vec<MeshError>>;
#[doc(hidden)]
fn name(&self) -> &'static str;
}
impl Catalog for () {
fn catalog() -> Vec<Self> {
Vec::new()
}
}
impl Meshes for () {
fn build(&self, _assets: &Assets) -> Result<Geometry, Vec<MeshError>> {
Ok(Geometry::empty())
}
fn name(&self) -> &'static str {
"()"
}
}
#[macro_export]
macro_rules! meshes {
($(#[$attribute:meta])* $vis:vis enum $set:ident { $($mesh:ident),+ $(,)? }) => {
$(#[$attribute])*
#[derive(Clone, Eq, Hash, PartialEq)]
$vis enum $set {
$($mesh($mesh)),+
}
$(
impl ::core::convert::From<$mesh> for $set {
fn from(mesh: $mesh) -> Self {
Self::$mesh(mesh)
}
}
impl $crate::Holds<$mesh> for $set {}
)+
impl $crate::Holds<$set> for $set {}
impl $crate::Catalog for $set {
fn catalog() -> ::std::vec::Vec<Self> {
let mut all = ::std::vec::Vec::new();
$(all.extend(<$mesh as $crate::Catalog>::catalog().into_iter().map(Self::$mesh));)+
all
}
}
impl $crate::Meshes for $set {
fn build(
&self,
assets: &$crate::Assets,
) -> ::core::result::Result<$crate::mesh::Geometry, ::std::vec::Vec<$crate::mesh::MeshError>> {
match self {
$(Self::$mesh(mesh) => $crate::Mesh::build(mesh, assets).erased()),+
}
}
fn name(&self) -> &'static str {
match self {
$(Self::$mesh(_) => ::core::stringify!($mesh)),+
}
}
}
};
}
#[cfg(test)]
mod tests {
use super::*;
use crate::mesh::{Cube, Sphere};
meshes! {
#[derive(Debug)]
enum Shape { Cube, Sphere }
}
#[test]
fn a_set_catalogs_every_value_of_every_type_it_names() {
let catalog = Shape::catalog();
assert_eq!(catalog.len(), 1 + Sphere::catalog().len());
assert_eq!(catalog[0], Shape::Cube(Cube));
assert!(matches!(catalog[1], Shape::Sphere(_)));
}
#[test]
fn a_set_value_builds_the_mesh_it_wraps_under_its_types_name() {
let assets = Assets::default();
let cube = Shape::from(Cube)
.build(&assets)
.expect("nothing is wrong with a cube");
assert_eq!(cube.part_count(), 1);
assert_eq!(Shape::from(Cube).name(), "Cube");
assert_eq!(Shape::from(Sphere { subdivisions: 1 }).name(), "Sphere");
}
#[test]
fn the_set_of_no_meshes_builds_nothing() {
assert!(<()>::catalog().is_empty());
assert_eq!(
().build(&Assets::default())
.expect("nothing is wrong with nothing")
.part_count(),
0
);
}
}