#[cfg(feature = "std")]
use std::path::PathBuf;
use concinnity_core::result::CnResult;
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
#[non_exhaustive]
pub enum Error {
#[cfg(feature = "std")]
#[error("no compiled world data at {}", blob.display())]
MissingData {
blob: PathBuf,
},
#[cfg(feature = "std")]
#[error("compiled world data at {} failed to load: {cause}", blob.display())]
UnreadableData {
blob: PathBuf,
cause: CnResult,
},
#[cfg(feature = "std")]
#[error("{} is a single blob file, but this world spans {needed} more", blob.display())]
OverflowUnsupported {
blob: PathBuf,
needed: u32,
},
#[cfg(feature = "std")]
#[error("no state directory was installed, so there is nowhere to read world data from")]
NoStateRoot,
#[error(transparent)]
Runtime(#[from] CnResult),
}
#[cfg(feature = "std")]
impl Error {
fn io_kind(&self) -> std::io::ErrorKind {
match self {
Error::MissingData { .. } | Error::NoStateRoot => std::io::ErrorKind::NotFound,
Error::UnreadableData { .. } | Error::OverflowUnsupported { .. } => {
std::io::ErrorKind::InvalidData
}
Error::Runtime(_) => std::io::ErrorKind::Other,
}
}
}
#[cfg(feature = "std")]
impl From<Error> for std::io::Error {
fn from(error: Error) -> Self {
std::io::Error::new(error.io_kind(), error.to_string())
}
}
#[cfg(feature = "std")]
pub(crate) fn from_startup(error: concinnity_engine::StartupError) -> Error {
use concinnity_engine::StartupError as S;
match error {
S::MissingData { blob } => Error::MissingData { blob },
S::UnreadableData { blob, cause } => Error::UnreadableData { blob, cause },
S::OverflowUnsupported { blob, needed } => Error::OverflowUnsupported { blob, needed },
S::NoStateRoot => Error::NoStateRoot,
}
}
#[cfg(test)]
mod tests {
use super::Error;
use alloc::string::ToString;
use concinnity_core::result::CnResult;
#[test]
fn a_runtime_failure_carries_the_status_it_was_built_from() {
let error = Error::from(CnResult::InvalidState);
assert_eq!(error, Error::Runtime(CnResult::InvalidState));
assert_eq!(error.to_string(), CnResult::InvalidState.to_string());
}
#[cfg(feature = "std")]
mod std_tier {
use super::super::Error;
use concinnity_core::result::CnResult;
use std::io::ErrorKind;
use std::path::PathBuf;
fn blob() -> PathBuf {
PathBuf::from("/apps/MyGame/data/0")
}
#[test]
fn every_load_failure_names_the_blob_it_could_not_use() {
for error in [
Error::MissingData { blob: blob() },
Error::UnreadableData {
blob: blob(),
cause: CnResult::FileIo,
},
Error::OverflowUnsupported {
blob: blob(),
needed: 2,
},
] {
assert!(error.to_string().contains("/apps/MyGame/data/0"), "{error}");
}
}
#[test]
fn an_unreadable_blob_reports_what_the_read_said() {
let error = Error::UnreadableData {
blob: blob(),
cause: CnResult::FileIo,
};
assert!(
error.to_string().contains(&CnResult::FileIo.to_string()),
"{error}"
);
}
#[test]
fn an_overflowing_world_reports_how_far_it_spans() {
let error = Error::OverflowUnsupported {
blob: blob(),
needed: 3,
};
assert!(error.to_string().contains('3'), "{error}");
}
#[test]
fn the_io_conversion_keeps_the_kind() {
let cases = [
(Error::MissingData { blob: blob() }, ErrorKind::NotFound),
(Error::NoStateRoot, ErrorKind::NotFound),
(
Error::UnreadableData {
blob: blob(),
cause: CnResult::FileIo,
},
ErrorKind::InvalidData,
),
(
Error::OverflowUnsupported {
blob: blob(),
needed: 2,
},
ErrorKind::InvalidData,
),
(Error::Runtime(CnResult::InvalidState), ErrorKind::Other),
];
for (error, kind) in cases {
let message = error.to_string();
let io: std::io::Error = error.into();
assert_eq!(io.kind(), kind, "{message}");
assert_eq!(io.to_string(), message);
}
}
#[test]
fn an_engine_startup_failure_maps_variant_for_variant() {
use concinnity_engine::StartupError as S;
let cases = [
(
S::MissingData { blob: blob() },
Error::MissingData { blob: blob() },
),
(
S::UnreadableData {
blob: blob(),
cause: CnResult::FileIo,
},
Error::UnreadableData {
blob: blob(),
cause: CnResult::FileIo,
},
),
(
S::OverflowUnsupported {
blob: blob(),
needed: 4,
},
Error::OverflowUnsupported {
blob: blob(),
needed: 4,
},
),
(S::NoStateRoot, Error::NoStateRoot),
];
for (startup, expected) in cases {
assert_eq!(super::super::from_startup(startup), expected);
}
}
}
}