use core::fmt;
use std::collections::BTreeSet;
use crate::Error;
use crate::mesh::MeshError;
use crate::skybox::SkyboxError;
pub(crate) const QUALIFIER: char = '#';
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub(crate) struct Unresolved(BTreeSet<Missing>);
impl Unresolved {
pub(crate) fn of(missing: Missing) -> Self {
Self(BTreeSet::from([missing]))
}
pub(crate) fn record(&mut self, other: Self) {
self.0.extend(other.0);
}
pub(crate) fn taken(&mut self) -> Self {
Self(core::mem::take(&mut self.0))
}
pub(crate) fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub(crate) fn logged(&self) {
for missing in &self.0 {
log::debug!("{missing}");
}
}
pub(crate) fn error(&self) -> Option<Error> {
(!self.is_empty()).then(|| {
let mut lines: Vec<String> = self.0.iter().map(Missing::to_string).collect();
lines.sort();
Error::msg(format!(
"the game's assets did not resolve: {}",
lines.join("; ")
))
})
}
}
#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub(crate) enum Missing {
Absent { name: String },
Ambiguous { name: String, sources: Vec<String> },
PartUnnamed { asset: String, part: String },
PartTwice {
asset: String,
part: String,
first: String,
second: String,
},
ClipUnnamed { asset: String, clip: String },
ClipTwice {
asset: String,
clip: String,
first: String,
second: String,
},
ClipStill {
asset: String,
clip: String,
animation: String,
},
ClipRepeated {
asset: String,
clip: String,
animation: String,
node: String,
},
Mesh {
mesh: &'static str,
error: MeshError,
},
Skybox { skybox: String, error: SkyboxError },
}
impl Missing {
pub(crate) fn absent(name: &str) -> Self {
Self::Absent {
name: name.to_owned(),
}
}
pub(crate) fn qualified(by: &str, name: &str) -> String {
format!("{by}{QUALIFIER}{name}")
}
}
impl fmt::Display for Missing {
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}"),
}
}
}