This branch predates CI on `main`. `cargo fmt --all` only; no behaviour. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
118 lines
4.6 KiB
Rust
118 lines
4.6 KiB
Rust
//! Which screens does `forced_backdrop` DECIDE, and which does it merely agree with?
|
|
//!
|
|
//! Every check this corpus has run on the rule measured its **stability** — that
|
|
//! no verdict moved when something else changed. That is a different property
|
|
//! from **necessity**: an element whose position is already fixed by a read or an
|
|
//! implied key is confirmed by the rule, not decided by it.
|
|
//!
|
|
//! So: compute `derived_paint_order` with the rule, and again with the
|
|
//! `forced_backdrop` fallback removed, and report every entry whose order moves.
|
|
//! Where nothing moves, the rule is decorative on that screen; where it moves,
|
|
//! the rule is the only thing holding the order up.
|
|
//!
|
|
//! Raised by the port agent 2026-08-30. Reach note in
|
|
//! `docs/re/structures/ui-forced-backdrop.md`.
|
|
//!
|
|
//! cargo run -p sylpheed-formats --example forced_backdrop_necessity -- [pak...]
|
|
//!
|
|
//! 🔴 With no argument this used to default to `GP_TITLE` alone, so a bare run
|
|
//! reported **6 instances, not 80** — a thirteenth of the census, printed in the
|
|
//! same format and reading like the whole thing. The port agent hit it and nearly
|
|
//! filed the discrepancy back at me. It now walks every `dat/*.pak` by default and
|
|
//! says on stderr how many archives it opened, because "I ran your instrument" has
|
|
//! to mean the same thing to both of us.
|
|
|
|
use std::path::PathBuf;
|
|
|
|
use sylpheed_formats::{pak::PakArchive, ui_layout};
|
|
|
|
fn order_without_rule(build: &ui_layout::UiBuild, bundle: &[u8]) -> Vec<usize> {
|
|
let mut idx: Vec<usize> = (0..build.elements.len()).collect();
|
|
idx.sort_by_key(|&i| {
|
|
let el = &build.elements[i];
|
|
(
|
|
ui_layout::sprite_layer_key(build, bundle, el)
|
|
.or_else(|| ui_layout::implied_layer_key(&el.name))
|
|
.unwrap_or(u32::MAX),
|
|
i,
|
|
)
|
|
});
|
|
idx
|
|
}
|
|
|
|
fn main() {
|
|
let root = PathBuf::from(std::env::var("SYLPHEED_DISC").expect("SYLPHEED_DISC"));
|
|
let (mut total_decides, mut total_agrees) = (0usize, 0usize);
|
|
let mut paks: Vec<PathBuf> = std::env::args().skip(1).map(PathBuf::from).collect();
|
|
if paks.is_empty() {
|
|
let mut all: Vec<PathBuf> = std::fs::read_dir(root.join("dat"))
|
|
.expect("dat/")
|
|
.filter_map(|e| e.ok().map(|e| e.path()))
|
|
.filter(|p| p.extension().is_some_and(|x| x == "pak"))
|
|
.collect();
|
|
all.sort();
|
|
paks = all;
|
|
}
|
|
eprintln!("# scanning {} archive(s)", paks.len());
|
|
for path in &paks {
|
|
let ar = PakArchive::open(path).expect("pak");
|
|
println!("# {}", path.display());
|
|
println!("# entry forced decides elements note");
|
|
|
|
let mut decides = Vec::new();
|
|
let mut agrees = Vec::new();
|
|
#[allow(unused)]
|
|
let _ = (&decides, &agrees);
|
|
for (i, e) in ar.entries().iter().enumerate() {
|
|
let Ok(by) = ar.read(e) else { continue };
|
|
let Some(b) = ui_layout::parse_build(&by) else {
|
|
continue;
|
|
};
|
|
let with = ui_layout::derived_paint_order(&b, &by);
|
|
let without = order_without_rule(&b, &by);
|
|
|
|
// Which elements does the rule fire on, and of those, which have no key
|
|
// of their own to fall back on?
|
|
let mut forced = Vec::new();
|
|
let mut keyless = Vec::new();
|
|
for el in &b.elements {
|
|
if !ui_layout::forced_backdrop(&b, el) {
|
|
continue;
|
|
}
|
|
forced.push(el.name.clone());
|
|
let own = ui_layout::sprite_layer_key(&b, &by, el)
|
|
.or_else(|| ui_layout::implied_layer_key(&el.name));
|
|
if own.is_none() {
|
|
keyless.push(el.name.clone());
|
|
}
|
|
}
|
|
if forced.is_empty() {
|
|
continue;
|
|
}
|
|
let moved = with != without;
|
|
if moved {
|
|
decides.push(i);
|
|
} else {
|
|
agrees.push(i);
|
|
}
|
|
println!(
|
|
" {i:>5} {:>6} {:>7} {:>8} forced=[{}] keyless=[{}]",
|
|
forced.len(),
|
|
if moved { "YES" } else { "no" },
|
|
b.elements.len(),
|
|
forced.join(","),
|
|
keyless.join(","),
|
|
);
|
|
}
|
|
println!("# rule DECIDES the order on entries {decides:?}");
|
|
println!("# rule merely AGREES on entries {agrees:?}\n");
|
|
total_decides += decides.len();
|
|
total_agrees += agrees.len();
|
|
}
|
|
println!(
|
|
"# TOTAL over {} archive(s): {total_decides} deciding entries, \
|
|
{total_agrees} agreeing",
|
|
paks.len()
|
|
);
|
|
}
|