fix: make core error displays actionable
This commit is contained in:
@@ -97,7 +97,14 @@ pub enum PathError {
|
|||||||
|
|
||||||
impl fmt::Display for PathError {
|
impl fmt::Display for PathError {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
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"),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -229,6 +236,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]
|
#[test]
|
||||||
fn strict_legacy_rejects_host_only_segments() {
|
fn strict_legacy_rejects_host_only_segments() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
|||||||
@@ -172,7 +172,28 @@ pub enum RenderError {
|
|||||||
|
|
||||||
impl std::fmt::Display for RenderError {
|
impl std::fmt::Display for RenderError {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
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
|
||||||
|
),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -534,6 +555,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]
|
#[test]
|
||||||
fn ui_phase_is_excluded_until_requested() -> Result<(), RenderError> {
|
fn ui_phase_is_excluded_until_requested() -> Result<(), RenderError> {
|
||||||
let snapshot = RenderSnapshot {
|
let snapshot = RenderSnapshot {
|
||||||
|
|||||||
@@ -128,7 +128,30 @@ pub enum ResourceError {
|
|||||||
|
|
||||||
impl std::fmt::Display for ResourceError {
|
impl std::fmt::Display for ResourceError {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
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"),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -884,6 +907,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]
|
#[test]
|
||||||
#[ignore = "requires licensed corpus"]
|
#[ignore = "requires licensed corpus"]
|
||||||
fn licensed_corpora_repository_reads_nres_and_rsli() {
|
fn licensed_corpora_repository_reads_nres_and_rsli() {
|
||||||
|
|||||||
@@ -169,7 +169,15 @@ pub enum WorldError {
|
|||||||
|
|
||||||
impl std::fmt::Display for WorldError {
|
impl std::fmt::Display for WorldError {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
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"),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -632,6 +640,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]
|
#[test]
|
||||||
fn identity_metadata_keeps_original_mirror_and_owner_distinct() {
|
fn identity_metadata_keeps_original_mirror_and_owner_distinct() {
|
||||||
let mut world = new(WorldConfig);
|
let mut world = new(WorldConfig);
|
||||||
|
|||||||
Reference in New Issue
Block a user