aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/fparkan-path/src/lib.rs21
-rw-r--r--crates/fparkan-render/src/lib.rs46
-rw-r--r--crates/fparkan-resource/src/lib.rs47
-rw-r--r--crates/fparkan-world/src/lib.rs22
4 files changed, 132 insertions, 4 deletions
diff --git a/crates/fparkan-path/src/lib.rs b/crates/fparkan-path/src/lib.rs
index f59fda0..330b03a 100644
--- a/crates/fparkan-path/src/lib.rs
+++ b/crates/fparkan-path/src/lib.rs
@@ -97,7 +97,14 @@ pub enum PathError {
impl fmt::Display for PathError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- write!(f, "{self:?}")
+ match self {
+ Self::Empty => write!(f, "path is empty"),
+ Self::EmbeddedNul => write!(f, "path contains an embedded NUL byte"),
+ Self::Absolute => write!(f, "path must be relative and cannot be absolute"),
+ Self::ParentTraversal => write!(f, "path attempts to traverse outside its root"),
+ Self::EscapesRoot => write!(f, "normalized path escapes the configured root"),
+ Self::InvalidUtf8 => write!(f, "path is not valid UTF-8 after normalization"),
+ }
}
}
@@ -230,6 +237,18 @@ mod tests {
}
#[test]
+ fn path_error_display_is_actionable() {
+ assert_eq!(
+ PathError::ParentTraversal.to_string(),
+ "path attempts to traverse outside its root"
+ );
+ assert_eq!(
+ PathError::EmbeddedNul.to_string(),
+ "path contains an embedded NUL byte"
+ );
+ }
+
+ #[test]
fn strict_legacy_rejects_host_only_segments() {
assert_eq!(
normalize_relative(b"./DATA/MAPS", PathPolicy::StrictLegacy),
diff --git a/crates/fparkan-render/src/lib.rs b/crates/fparkan-render/src/lib.rs
index a2f18d6..5f6d1da 100644
--- a/crates/fparkan-render/src/lib.rs
+++ b/crates/fparkan-render/src/lib.rs
@@ -172,7 +172,28 @@ pub enum RenderError {
impl std::fmt::Display for RenderError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- write!(f, "{self:?}")
+ match self {
+ Self::InvalidRange => write!(f, "render command contains an empty index range"),
+ Self::InvalidDrawRange {
+ draw_id,
+ stable_order,
+ start,
+ count,
+ } => write!(
+ f,
+ "draw {} has invalid index range start={} count={} at stable order {}",
+ draw_id.0, start, count, stable_order
+ ),
+ Self::MaterialIndexOutOfBounds {
+ draw_id,
+ material_index,
+ material_count,
+ } => write!(
+ f,
+ "draw {} references material index {} but only {} material slots are available",
+ draw_id.0, material_index, material_count
+ ),
+ }
}
}
@@ -535,6 +556,29 @@ mod tests {
}
#[test]
+ fn render_error_display_is_actionable() {
+ assert_eq!(
+ RenderError::InvalidDrawRange {
+ draw_id: DrawId(9),
+ stable_order: 10,
+ start: 4,
+ count: 0
+ }
+ .to_string(),
+ "draw 9 has invalid index range start=4 count=0 at stable order 10"
+ );
+ assert_eq!(
+ RenderError::MaterialIndexOutOfBounds {
+ draw_id: DrawId(7),
+ material_index: 3,
+ material_count: 2
+ }
+ .to_string(),
+ "draw 7 references material index 3 but only 2 material slots are available"
+ );
+ }
+
+ #[test]
fn ui_phase_is_excluded_until_requested() -> Result<(), RenderError> {
let snapshot = RenderSnapshot {
camera: CameraSnapshot::default(),
diff --git a/crates/fparkan-resource/src/lib.rs b/crates/fparkan-resource/src/lib.rs
index b09c946..b84f6f9 100644
--- a/crates/fparkan-resource/src/lib.rs
+++ b/crates/fparkan-resource/src/lib.rs
@@ -128,7 +128,30 @@ pub enum ResourceError {
impl std::fmt::Display for ResourceError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- write!(f, "{self:?}")
+ match self {
+ Self::MissingArchive => write!(f, "archive was not found"),
+ Self::MissingEntry => write!(f, "resource entry was not found in the archive"),
+ Self::InvalidHandle => write!(
+ f,
+ "resource handle does not reference an open archive entry"
+ ),
+ Self::StaleHandle => {
+ write!(f, "resource handle belongs to an older archive generation")
+ }
+ Self::Format(message) => write!(f, "resource archive format error: {message}"),
+ Self::EntryRead { key, source } => {
+ write!(
+ f,
+ "failed to read resource {}:{} from {}: {}",
+ key.type_id
+ .map_or_else(|| "-".to_string(), |type_id| type_id.to_string()),
+ String::from_utf8_lossy(&key.name.0),
+ key.archive.as_str(),
+ source
+ )
+ }
+ Self::Poisoned => write!(f, "resource repository state lock was poisoned"),
+ }
}
}
@@ -885,6 +908,28 @@ mod tests {
}
#[test]
+ fn resource_error_display_is_actionable() {
+ let path = archive_path(b"bad/rsli.lib").expect("path");
+ let err = ResourceError::EntryRead {
+ key: ResourceKey {
+ archive: path,
+ name: resource_name(b"BROKEN.TEX"),
+ type_id: None,
+ },
+ source: "unsupported packing method 0x1e0".to_string(),
+ };
+
+ assert_eq!(
+ err.to_string(),
+ "failed to read resource -:BROKEN.TEX from bad/rsli.lib: unsupported packing method 0x1e0"
+ );
+ assert_eq!(
+ ResourceError::StaleHandle.to_string(),
+ "resource handle belongs to an older archive generation"
+ );
+ }
+
+ #[test]
#[ignore = "requires licensed corpus"]
fn licensed_corpora_repository_reads_nres_and_rsli() {
licensed_repository_gate("IS").expect("part 1 repository gate");
diff --git a/crates/fparkan-world/src/lib.rs b/crates/fparkan-world/src/lib.rs
index a253586..ec988ff 100644
--- a/crates/fparkan-world/src/lib.rs
+++ b/crates/fparkan-world/src/lib.rs
@@ -169,7 +169,15 @@ pub enum WorldError {
impl std::fmt::Display for WorldError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- write!(f, "{self:?}")
+ match self {
+ Self::InvalidHandle => write!(f, "object handle does not reference a known slot"),
+ Self::StaleHandle => write!(f, "object handle belongs to an older slot generation"),
+ Self::Deleted => write!(f, "object has already been deleted"),
+ Self::DuplicateOriginalObjectId(id) => {
+ write!(f, "original object id {} is already registered", id.0)
+ }
+ Self::InvalidFixedStep => write!(f, "fixed-step configuration must be non-zero"),
+ }
}
}
@@ -633,6 +641,18 @@ mod tests {
}
#[test]
+ fn world_error_display_is_actionable() {
+ assert_eq!(
+ WorldError::StaleHandle.to_string(),
+ "object handle belongs to an older slot generation"
+ );
+ assert_eq!(
+ WorldError::DuplicateOriginalObjectId(OriginalObjectId(8)).to_string(),
+ "original object id 8 is already registered"
+ );
+ }
+
+ #[test]
fn identity_metadata_keeps_original_mirror_and_owner_distinct() {
let mut world = new(WorldConfig);
let handle = construct_object(