style: format workspace with Rust 1.97
Docs Deploy / Build and Deploy MkDocs (push) Successful in 36s
Test / Lint (push) Failing after 2m31s
Test / Test (push) Skipped
Test / Render parity (push) Skipped

This commit is contained in:
2026-07-18 05:28:07 +04:00
parent 02b4da2e3d
commit 2da28d0495
8 changed files with 147 additions and 96 deletions
@@ -32,7 +32,14 @@ fn take_runtime_owners_in_dependency_order<Instance, Validation, Surface, Device
instance.take(); instance.take();
} }
fn take_runtime_children_with_validation_snapshot<Surface, Device, Swapchain, Validation, Snapshot, Capture>( fn take_runtime_children_with_validation_snapshot<
Surface,
Device,
Swapchain,
Validation,
Snapshot,
Capture,
>(
surface: &mut Option<Surface>, surface: &mut Option<Surface>,
device: &mut Option<Device>, device: &mut Option<Device>,
swapchain: &mut Option<Swapchain>, swapchain: &mut Option<Swapchain>,
+7 -9
View File
@@ -634,15 +634,13 @@ pub fn prepare_mission_assets_with_repository<R: ResourceRepository>(
root_prototype_spans: &[std::ops::Range<usize>], root_prototype_spans: &[std::ops::Range<usize>],
prototypes: &[EffectivePrototype], prototypes: &[EffectivePrototype],
) -> Result<MissionAssets, AssetError> { ) -> Result<MissionAssets, AssetError> {
Ok( Ok(prepare_mission_assets_profiled_with_repository(
prepare_mission_assets_profiled_with_repository( repository,
repository, root_prototype_spans,
root_prototype_spans, prototypes,
prototypes, AssetPreparationLimits::default(),
AssetPreparationLimits::default(), )?
)? .0)
.0,
)
} }
/// Builds mission assets while enforcing explicit preparation limits. /// Builds mission assets while enforcing explicit preparation limits.
+10 -2
View File
@@ -894,7 +894,11 @@ mod tests {
fs::write(root.join("WORLD/MAP/land.map"), build_nres(&[])).expect("land map"); fs::write(root.join("WORLD/MAP/land.map"), build_nres(&[])).expect("land map");
let manifest = CorpusManifest { let manifest = CorpusManifest {
kind: CorpusKind::Unknown, kind: CorpusKind::Unknown,
files: vec![manifest_entry("WORLD/MAP/land.map", 16, sha256(b"land.map"))], files: vec![manifest_entry(
"WORLD/MAP/land.map",
16,
sha256(b"land.map"),
)],
casefold_collisions: Vec::new(), casefold_collisions: Vec::new(),
}; };
@@ -913,7 +917,11 @@ mod tests {
fs::write(root.join("WORLD/MAP/land.msh"), build_nres(&[])).expect("land msh"); fs::write(root.join("WORLD/MAP/land.msh"), build_nres(&[])).expect("land msh");
let manifest = CorpusManifest { let manifest = CorpusManifest {
kind: CorpusKind::Unknown, kind: CorpusKind::Unknown,
files: vec![manifest_entry("WORLD/MAP/land.msh", 16, sha256(b"land.msh"))], files: vec![manifest_entry(
"WORLD/MAP/land.msh",
16,
sha256(b"land.msh"),
)],
casefold_collisions: Vec::new(), casefold_collisions: Vec::new(),
}; };
+42 -41
View File
@@ -137,7 +137,8 @@ pub enum LandFileKind {
/// ///
/// Returns a string error when the archive cannot be read or decoded. /// Returns a string error when the archive cannot be read or decoded.
pub fn inspect_archive_file(path: &Path, sample_limit: usize) -> Result<ArchiveInspection, String> { pub fn inspect_archive_file(path: &Path, sample_limit: usize) -> Result<ArchiveInspection, String> {
inspect_archive_file_diagnostic(path, sample_limit).map_err(|diagnostic| render_human(&diagnostic)) inspect_archive_file_diagnostic(path, sample_limit)
.map_err(|diagnostic| render_human(&diagnostic))
} }
/// Inspects a format archive and returns a structured diagnostic on failure. /// Inspects a format archive and returns a structured diagnostic on failure.
@@ -215,7 +216,9 @@ fn inspect_archive_bytes(
Arc::from(bytes.to_vec().into_boxed_slice()), Arc::from(bytes.to_vec().into_boxed_slice()),
ReadProfile::Compatible, ReadProfile::Compatible,
) )
.map_err(|err| archive_parse_diagnostic("S1.NRES.DECODE", source, bytes, err.to_string()))?; .map_err(|err| {
archive_parse_diagnostic("S1.NRES.DECODE", source, bytes, err.to_string())
})?;
let mut sample = Vec::new(); let mut sample = Vec::new();
for entry in document.entries().iter().take(sample_limit) { for entry in document.entries().iter().take(sample_limit) {
sample.push(NresEntrySummary { sample.push(NresEntrySummary {
@@ -234,7 +237,9 @@ fn inspect_archive_bytes(
Arc::from(bytes.to_vec().into_boxed_slice()), Arc::from(bytes.to_vec().into_boxed_slice()),
fparkan_rsli::ReadProfile::Compatible, fparkan_rsli::ReadProfile::Compatible,
) )
.map_err(|err| archive_parse_diagnostic("S1.RSLI.DECODE", source, bytes, err.to_string()))?; .map_err(|err| {
archive_parse_diagnostic("S1.RSLI.DECODE", source, bytes, err.to_string())
})?;
Ok(ArchiveInspection::Rsli { Ok(ArchiveInspection::Rsli {
entries: document.entries().len(), entries: document.entries().len(),
}) })
@@ -259,8 +264,8 @@ pub fn inspect_model_from_root(
archive: &str, archive: &str,
resource: &str, resource: &str,
) -> Result<ModelInspection, String> { ) -> Result<ModelInspection, String> {
let bytes = let bytes = read_resource_bytes_diagnostic(root, archive, resource)
read_resource_bytes_diagnostic(root, archive, resource).map_err(|err| render_human(&err))?; .map_err(|err| render_human(&err))?;
let document = decode_nres(bytes.clone(), ReadProfile::Compatible).map_err(|err| { let document = decode_nres(bytes.clone(), ReadProfile::Compatible).map_err(|err| {
render_human(&resource_parse_diagnostic( render_human(&resource_parse_diagnostic(
"S1.NRES.DECODE", "S1.NRES.DECODE",
@@ -293,10 +298,8 @@ pub fn load_model_from_root(
archive: &str, archive: &str,
resource: &str, resource: &str,
) -> Result<ModelAsset, String> { ) -> Result<ModelAsset, String> {
let document = let document = load_model_document_from_root_diagnostic(root, archive, resource)
load_model_document_from_root_diagnostic(root, archive, resource).map_err(|err| { .map_err(|err| render_human(&err))?;
render_human(&err)
})?;
let msh = decode_msh(&document).map_err(|err| err.to_string())?; let msh = decode_msh(&document).map_err(|err| err.to_string())?;
validate_msh(&msh).map_err(|err| err.to_string()) validate_msh(&msh).map_err(|err| err.to_string())
} }
@@ -312,8 +315,8 @@ pub fn inspect_texture_from_root(
archive: &str, archive: &str,
resource: &str, resource: &str,
) -> Result<TextureInspection, String> { ) -> Result<TextureInspection, String> {
let bytes = let bytes = read_resource_bytes_diagnostic(root, archive, resource)
read_resource_bytes_diagnostic(root, archive, resource).map_err(|err| render_human(&err))?; .map_err(|err| render_human(&err))?;
let document = decode_texm(bytes).map_err(|err| err.to_string())?; let document = decode_texm(bytes).map_err(|err| err.to_string())?;
Ok(TextureInspection { Ok(TextureInspection {
width: document.width(), width: document.width(),
@@ -385,18 +388,16 @@ fn read_resource_bytes_diagnostic(
) )
})?; })?;
let resource_name = resource_name(name.as_bytes()); let resource_name = resource_name(name.as_bytes());
let archive_handle = repository let archive_handle = repository.open_archive(&archive_path).map_err(|err| {
.open_archive(&archive_path) diagnostic(DiagnosticCode("S1.RESOURCE.OPEN_ARCHIVE"), err.to_string()).with_context(
.map_err(|err| { DiagnosticContext {
diagnostic(DiagnosticCode("S1.RESOURCE.OPEN_ARCHIVE"), err.to_string()).with_context( phase: Some(Phase::Read),
DiagnosticContext { path: Some(archive.to_string()),
phase: Some(Phase::Read), archive_entry: Some(name.to_string()),
path: Some(archive.to_string()), ..DiagnosticContext::default()
archive_entry: Some(name.to_string()), },
..DiagnosticContext::default() )
}, })?;
)
})?;
let Some(handle) = repository let Some(handle) = repository
.find(archive_handle, &resource_name) .find(archive_handle, &resource_name)
.map_err(|err| { .map_err(|err| {
@@ -410,21 +411,19 @@ fn read_resource_bytes_diagnostic(
) )
})? })?
else { else {
return Err( return Err(diagnostic(
diagnostic( DiagnosticCode("S1.RESOURCE.MISSING_ENTRY"),
DiagnosticCode("S1.RESOURCE.MISSING_ENTRY"), format!(
format!( "resource not found: {archive}/{}",
"resource not found: {archive}/{}", String::from_utf8_lossy(name.as_bytes())
String::from_utf8_lossy(name.as_bytes()) ),
), )
) .with_context(DiagnosticContext {
.with_context(DiagnosticContext { phase: Some(Phase::Resolve),
phase: Some(Phase::Resolve), path: Some(archive.to_string()),
path: Some(archive.to_string()), archive_entry: Some(name.to_string()),
archive_entry: Some(name.to_string()), ..DiagnosticContext::default()
..DiagnosticContext::default() }));
}),
);
}; };
let bytes = repository.read(handle).map_err(|err| { let bytes = repository.read(handle).map_err(|err| {
diagnostic(DiagnosticCode("S1.RESOURCE.READ"), err.to_string()).with_context( diagnostic(DiagnosticCode("S1.RESOURCE.READ"), err.to_string()).with_context(
@@ -514,8 +513,7 @@ mod tests {
let path = dir.join("broken.nres"); let path = dir.join("broken.nres");
fs::write(&path, b"NRes").expect("broken nres"); fs::write(&path, b"NRes").expect("broken nres");
let diagnostic = let diagnostic = inspect_archive_file_diagnostic(&path, 0).expect_err("diagnostic failure");
inspect_archive_file_diagnostic(&path, 0).expect_err("diagnostic failure");
assert_eq!(diagnostic.code.0, "S1.NRES.DECODE"); assert_eq!(diagnostic.code.0, "S1.NRES.DECODE");
let expected_path = path.display().to_string(); let expected_path = path.display().to_string();
@@ -555,7 +553,10 @@ mod tests {
assert_eq!(diagnostic.code.0, "S1.NRES.DECODE"); assert_eq!(diagnostic.code.0, "S1.NRES.DECODE");
assert_eq!(diagnostic.context.phase, Some(Phase::Parse)); assert_eq!(diagnostic.context.phase, Some(Phase::Parse));
assert_eq!(diagnostic.context.path.as_deref(), Some("models.rlb")); assert_eq!(diagnostic.context.path.as_deref(), Some("models.rlb"));
assert_eq!(diagnostic.context.archive_entry.as_deref(), Some("BROKEN.MSH")); assert_eq!(
diagnostic.context.archive_entry.as_deref(),
Some("BROKEN.MSH")
);
assert_eq!( assert_eq!(
diagnostic.context.span, diagnostic.context.span,
Some(SourceSpan { Some(SourceSpan {
+6 -5
View File
@@ -802,10 +802,8 @@ fn parse_entries(
header: &NresHeader, header: &NresHeader,
limits: DecodeLimits, limits: DecodeLimits,
) -> Result<Vec<NresEntry>, NresError> { ) -> Result<Vec<NresEntry>, NresError> {
let capacity = checked_allocation_len( let capacity =
u64::from(header.entry_count), checked_allocation_len(u64::from(header.entry_count), u64::from(limits.max_entries))?;
u64::from(limits.max_entries),
)?;
let mut entries = Vec::with_capacity(capacity); let mut entries = Vec::with_capacity(capacity);
let directory_offset = let directory_offset =
usize::try_from(header.directory_offset).map_err(|_| DecodeError::IntegerOverflow)?; usize::try_from(header.directory_offset).map_err(|_| DecodeError::IntegerOverflow)?;
@@ -1629,7 +1627,10 @@ mod tests {
..DecodeLimits::default() ..DecodeLimits::default()
} }
), ),
Err(NresError::Binary(DecodeError::LimitExceeded { count: 2, limit: 1 })) Err(NresError::Binary(DecodeError::LimitExceeded {
count: 2,
limit: 1
}))
)); ));
} }
+23 -10
View File
@@ -209,7 +209,11 @@ pub struct PrototypeGraphNode {
impl PrototypeGraphNode { impl PrototypeGraphNode {
/// Creates a typed resource node. /// Creates a typed resource node.
#[must_use] #[must_use]
pub fn resource(kind: PrototypeGraphNodeKind, resource: ResourceKey, id: PrototypeGraphNodeId) -> Self { pub fn resource(
kind: PrototypeGraphNodeKind,
resource: ResourceKey,
id: PrototypeGraphNodeId,
) -> Self {
Self { Self {
id, id,
kind, kind,
@@ -1656,15 +1660,24 @@ mod tests {
fn malformed_unit_dat_failure_is_classified_on_unit_edge() { fn malformed_unit_dat_failure_is_classified_on_unit_edge() {
let mut vfs = MemoryVfs::default(); let mut vfs = MemoryVfs::default();
let dat_path = resource_archive_path(b"UNITS/AUTO/bad.dat").expect("dat path"); let dat_path = resource_archive_path(b"UNITS/AUTO/bad.dat").expect("dat path");
vfs.insert(dat_path, Arc::from([1_u8, 2, 3].to_vec().into_boxed_slice())); vfs.insert(
dat_path,
Arc::from([1_u8, 2, 3].to_vec().into_boxed_slice()),
);
let vfs = Arc::new(vfs); let vfs = Arc::new(vfs);
let repo = CachedResourceRepository::new(vfs.clone()); let repo = CachedResourceRepository::new(vfs.clone());
let (_graph, _resolved, report) = let (_graph, _resolved, report) = build_prototype_graph_report(
build_prototype_graph_report(&repo, vfs.as_ref(), &[resource_name(b"UNITS/AUTO/bad.dat")]); &repo,
vfs.as_ref(),
&[resource_name(b"UNITS/AUTO/bad.dat")],
);
assert_eq!(report.failures.len(), 1); assert_eq!(report.failures.len(), 1);
assert_eq!(report.failures[0].edge, PrototypeGraphEdge::MissionToUnitDat); assert_eq!(
report.failures[0].edge,
PrototypeGraphEdge::MissionToUnitDat
);
} }
#[test] #[test]
@@ -1690,7 +1703,10 @@ mod tests {
); );
assert_eq!(report.failures.len(), 1); assert_eq!(report.failures.len(), 1);
assert_eq!(report.failures[0].edge, PrototypeGraphEdge::UnitDatToComponent); assert_eq!(
report.failures[0].edge,
PrototypeGraphEdge::UnitDatToComponent
);
assert!(report.failures[0].message.contains("missing_component")); assert!(report.failures[0].message.contains("missing_component"));
} }
@@ -2007,10 +2023,7 @@ mod tests {
assert_eq!(report.failures.len(), 1); assert_eq!(report.failures.len(), 1);
assert_eq!(report.failures[0].resource_raw, b"broken"); assert_eq!(report.failures[0].resource_raw, b"broken");
assert_eq!( assert_eq!(report.failures[0].edge, PrototypeGraphEdge::PrototypeToMesh);
report.failures[0].edge,
PrototypeGraphEdge::PrototypeToMesh
);
assert!(report.failures[0].message.contains("broken")); assert!(report.failures[0].message.contains("broken"));
assert!(report.failures[0] assert!(report.failures[0]
.message .message
+37 -10
View File
@@ -636,7 +636,11 @@ impl RsliDocument {
/// ///
/// Returns [`RsliError`] when the packed payload exceeds configured /// Returns [`RsliError`] when the packed payload exceeds configured
/// limits, `id` is invalid, or the payload cannot be decoded. /// limits, `id` is invalid, or the payload cannot be decoded.
pub fn load_with_limits(&self, id: EntryId, limits: DecodeLimits) -> Result<Vec<u8>, RsliError> { pub fn load_with_limits(
&self,
id: EntryId,
limits: DecodeLimits,
) -> Result<Vec<u8>, RsliError> {
let record = self.record_by_id(id)?; let record = self.record_by_id(id)?;
let packed = self.packed_slice(id, record)?; let packed = self.packed_slice(id, record)?;
decode_payload( decode_payload(
@@ -1208,12 +1212,18 @@ fn validate_lookup_order(records: &[EntryRecord]) -> Result<(), RsliError> {
.map_err(|_| RsliError::IntegerOverflow)?; .map_err(|_| RsliError::IntegerOverflow)?;
let left = records let left = records
.get(left_original) .get(left_original)
.ok_or(RsliError::CorruptEntryTable("sort_to_original is not a permutation"))?; .ok_or(RsliError::CorruptEntryTable(
"sort_to_original is not a permutation",
))?;
let right = records let right = records
.get(right_original) .get(right_original)
.ok_or(RsliError::CorruptEntryTable("sort_to_original is not a permutation"))?; .ok_or(RsliError::CorruptEntryTable(
if cmp_c_string(c_name_bytes(&left.meta.name_raw), c_name_bytes(&right.meta.name_raw)) "sort_to_original is not a permutation",
== std::cmp::Ordering::Greater ))?;
if cmp_c_string(
c_name_bytes(&left.meta.name_raw),
c_name_bytes(&right.meta.name_raw),
) == std::cmp::Ordering::Greater
{ {
return Err(RsliError::CorruptEntryTable( return Err(RsliError::CorruptEntryTable(
"presorted lookup names are not sorted", "presorted lookup names are not sorted",
@@ -1906,7 +1916,9 @@ mod tests {
assert!(matches!( assert!(matches!(
decode(arc(bytes.clone()), ReadProfile::Strict), decode(arc(bytes.clone()), ReadProfile::Strict),
Err(RsliError::CorruptEntryTable("presorted lookup names are not sorted")) Err(RsliError::CorruptEntryTable(
"presorted lookup names are not sorted"
))
)); ));
let doc = decode(arc(bytes), ReadProfile::Compatible).expect("compatible fallback"); let doc = decode(arc(bytes), ReadProfile::Compatible).expect("compatible fallback");
@@ -2418,14 +2430,23 @@ mod tests {
..DecodeLimits::default() ..DecodeLimits::default()
} }
), ),
Err(RsliError::Binary(DecodeError::LimitExceeded { count: 2, limit: 1 })) Err(RsliError::Binary(DecodeError::LimitExceeded {
count: 2,
limit: 1
}))
)); ));
} }
#[test] #[test]
fn stored_entries_require_exact_packed_size() { fn stored_entries_require_exact_packed_size() {
let bytes = synthetic_rsli( let bytes = synthetic_rsli(
&[SyntheticEntry::with_payload(b"A", 0x000, 0, b"ok", b"ok!".to_vec())], &[SyntheticEntry::with_payload(
b"A",
0x000,
0,
b"ok",
b"ok!".to_vec(),
)],
true, true,
0x7782, 0x7782,
None, None,
@@ -2434,7 +2455,10 @@ mod tests {
assert!(matches!( assert!(matches!(
doc.load(EntryId(0)), doc.load(EntryId(0)),
Err(RsliError::OutputSizeMismatch { expected: 2, got: 3 }) Err(RsliError::OutputSizeMismatch {
expected: 2,
got: 3
})
)); ));
} }
@@ -2457,7 +2481,10 @@ mod tests {
..DecodeLimits::default() ..DecodeLimits::default()
} }
), ),
Err(RsliError::Binary(DecodeError::LimitExceeded { count: 5, limit: 4 })) Err(RsliError::Binary(DecodeError::LimitExceeded {
count: 5,
limit: 4
}))
)); ));
} }
+14 -18
View File
@@ -257,11 +257,7 @@ fn select_casefolded_match(
} }
} }
fn list_recursive( fn list_recursive(root: &Path, dir: &Path, out: &mut Vec<VfsEntry>) -> Result<(), VfsError> {
root: &Path,
dir: &Path,
out: &mut Vec<VfsEntry>,
) -> Result<(), VfsError> {
let read_dir = fs::read_dir(dir).map_err(VfsError::Io)?; let read_dir = fs::read_dir(dir).map_err(VfsError::Io)?;
let mut children = Vec::new(); let mut children = Vec::new();
for entry in read_dir { for entry in read_dir {
@@ -286,11 +282,9 @@ fn list_recursive(
let rel_bytes = rel.as_os_str().as_bytes(); let rel_bytes = rel.as_os_str().as_bytes();
#[cfg(not(unix))] #[cfg(not(unix))]
let rel_bytes = rel.to_str().ok_or(VfsError::Path)?.as_bytes(); let rel_bytes = rel.to_str().ok_or(VfsError::Path)?.as_bytes();
let path = fparkan_path::normalize_relative( let path =
rel_bytes, fparkan_path::normalize_relative(rel_bytes, fparkan_path::PathPolicy::HostCompatible)
fparkan_path::PathPolicy::HostCompatible, .map_err(|_| VfsError::Path)?;
)
.map_err(|_| VfsError::Path)?;
out.push(VfsEntry { out.push(VfsEntry {
path, path,
metadata: metadata_from_host_file(&child, &metadata)?, metadata: metadata_from_host_file(&child, &metadata)?,
@@ -299,10 +293,7 @@ fn list_recursive(
Ok(()) Ok(())
} }
fn metadata_from_host_file( fn metadata_from_host_file(path: &Path, metadata: &fs::Metadata) -> Result<VfsMetadata, VfsError> {
path: &Path,
metadata: &fs::Metadata,
) -> Result<VfsMetadata, VfsError> {
if !metadata.is_file() { if !metadata.is_file() {
return Err(VfsError::Path); return Err(VfsError::Path);
} }
@@ -659,8 +650,7 @@ mod tests {
} }
let vfs = DirectoryVfs::new(&root); let vfs = DirectoryVfs::new(&root);
let path = let path = normalize_relative(b"data/\xFF.bin", PathPolicy::HostCompatible).expect("path");
normalize_relative(b"data/\xFF.bin", PathPolicy::HostCompatible).expect("path");
assert_eq!(vfs.read(&path).expect("read raw path").as_ref(), b"raw"); assert_eq!(vfs.read(&path).expect("read raw path").as_ref(), b"raw");
let entries = vfs let entries = vfs
@@ -770,8 +760,14 @@ mod tests {
let entries = overlay.list(&prefix).expect("list"); let entries = overlay.list(&prefix).expect("list");
assert_eq!(entries.len(), 2); assert_eq!(entries.len(), 2);
assert_eq!(entries[0].path.display_lossy(), entries[1].path.display_lossy()); assert_eq!(
assert_ne!(entries[0].path.identity_bytes(), entries[1].path.identity_bytes()); entries[0].path.display_lossy(),
entries[1].path.display_lossy()
);
assert_ne!(
entries[0].path.identity_bytes(),
entries[1].path.identity_bytes()
);
} }
fn unique_test_dir(name: &str) -> PathBuf { fn unique_test_dir(name: &str) -> PathBuf {