diff options
| -rw-r--r-- | crates/fparkan-path/src/lib.rs | 21 | ||||
| -rw-r--r-- | crates/fparkan-render/src/lib.rs | 46 | ||||
| -rw-r--r-- | crates/fparkan-resource/src/lib.rs | 47 | ||||
| -rw-r--r-- | crates/fparkan-world/src/lib.rs | 22 |
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( |
