use core::hash::Hash;
use crate::Catalog;
use crate::assets::Assets;
use crate::holds::Sealed;
use crate::mesh::Built;
pub trait Meshes: Sealed + Catalog + Hash + Eq + Clone + 'static {
#[doc(hidden)]
fn build(&self, assets: &Assets) -> Built;
}
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub enum NoMeshes {}
impl Sealed for NoMeshes {}
impl Catalog for NoMeshes {
fn catalog() -> Vec<Self> {
Vec::new()
}
}
impl Meshes for NoMeshes {
fn build(&self, _assets: &Assets) -> Built {
match *self {}
}
}
#[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::Sealed 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) -> $crate::mesh::Built {
match self {
$(
Self::$mesh(mesh) => $crate::Mesh::build(mesh, assets)
.erased(::core::stringify!($mesh))
),+
}
}
}
};
}
#[cfg(test)]
mod tests {
use super::*;
use crate::mesh::{Cube, Mesh, MeshData, Sphere};
meshes! {
#[derive(Debug)]
enum Shape { Cube, Sphere }
}
meshes! {
enum Shapes { Wedge }
}
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
struct Wedge;
impl Catalog for Wedge {
fn catalog() -> Vec<Self> {
vec![Self]
}
}
impl Mesh for Wedge {
fn build(&self, _assets: &Assets) -> MeshData {
MeshData::new(Vec::new(), vec![0])
}
}
#[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);
assert_eq!(cube.geometry.part_count(), 1);
assert!(
cube.unresolved.is_empty(),
"nothing is wrong with a cube: {:?}",
cube.unresolved
);
let wedge = Shapes::from(Wedge).build(&assets);
assert_eq!(
wedge
.unresolved
.error()
.expect("the index reaches past the corners")
.to_string(),
"the game's assets did not resolve: the mesh `Wedge` has the index 0 past its 0 \
vertices",
"and a value of the set names its own type in what it did not get"
);
}
#[test]
fn the_set_of_no_meshes_catalogs_nothing() {
assert!(NoMeshes::catalog().is_empty());
}
}