mirage-engine 0.1.1

Mirage, an immediate-mode 3D engine for simple games on desktop and the browser
Documentation
//! What a build needed from the assets and did not get, for the one
//! startup error.

use core::fmt;

use crate::mesh::MeshError;
use crate::skybox::SkyboxError;

/// The mark that separates a source's file name from an item name it
/// shares with another source.
pub(crate) const QUALIFIER: char = '#';

/// One thing a build needed from the assets and did not get.
#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub(crate) enum Unresolved {
    /// No source holds an item of that name and kind.
    Absent { name: String },
    /// More than one source holds an item of that name, which is read
    /// qualified.
    Ambiguous { name: String, sources: Vec<String> },
    /// No material of the asset resolves to the part.
    PartUnnamed { asset: String, part: String },
    /// Two materials of the asset resolve to the one part.
    PartTwice {
        asset: String,
        part: String,
        first: String,
        second: String,
    },
    /// No animation of the asset resolves to the clip.
    ClipUnnamed { asset: String, clip: String },
    /// Two animations of the asset resolve to the one clip.
    ClipTwice {
        asset: String,
        clip: String,
        first: String,
        second: String,
    },
    /// The animation the clip resolves to moves no joint of the asset.
    ClipStill {
        asset: String,
        clip: String,
        animation: String,
    },
    /// The animation the clip resolves to moves one node of the asset along
    /// two curves at once.
    ClipRepeated {
        asset: String,
        clip: String,
        animation: String,
        node: String,
    },
    /// A mesh was built with an error in it.
    Mesh {
        mesh: &'static str,
        error: MeshError,
    },
    /// A skybox was built over an image that is no sky at all.
    Skybox { skybox: String, error: SkyboxError },
}

impl Unresolved {
    /// A name nothing resolves to.
    pub(crate) fn absent(name: &str) -> Self {
        Self::Absent {
            name: name.to_owned(),
        }
    }

    /// What `name` reads as under the source `by`.
    pub(crate) fn qualified(by: &str, name: &str) -> String {
        format!("{by}{QUALIFIER}{name}")
    }
}

impl fmt::Display for Unresolved {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::Absent { name } => write!(f, "no asset is named `{name}`"),
            Self::Ambiguous { name, sources } => {
                let alternatives: Vec<String> = sources
                    .iter()
                    .map(|source| format!("`{}`", Self::qualified(source, name)))
                    .collect();
                write!(
                    f,
                    "several sources call something `{name}`; ask for {}",
                    alternatives.join(" or ")
                )
            }
            Self::PartUnnamed { asset, part } => write!(
                f,
                "the asset `{asset}` has no material that resolves to the part {part}"
            ),
            Self::PartTwice {
                asset,
                part,
                first,
                second,
            } => write!(
                f,
                "the asset `{asset}` has two materials that resolve to the part {part}: \
                 `{first}` and `{second}`"
            ),
            Self::ClipUnnamed { asset, clip } => write!(
                f,
                "the asset `{asset}` has no animation that resolves to the clip {clip}"
            ),
            Self::ClipTwice {
                asset,
                clip,
                first,
                second,
            } => write!(
                f,
                "the asset `{asset}` has two animations that resolve to the clip {clip}: \
                 `{first}` and `{second}`"
            ),
            Self::ClipStill {
                asset,
                clip,
                animation,
            } => write!(
                f,
                "the asset `{asset}` has the animation `{animation}` for the clip {clip}, which \
                 moves no joint of it"
            ),
            Self::ClipRepeated {
                asset,
                clip,
                animation,
                node,
            } => write!(
                f,
                "the asset `{asset}` has the animation `{animation}` for the clip {clip}, which \
                 moves the node `{node}` along two curves at once"
            ),
            Self::Mesh { mesh, error } => write!(f, "the mesh `{mesh}` {error}"),
            Self::Skybox { skybox, error } => write!(f, "the skybox `{skybox}` {error}"),
        }
    }
}