Review pass: fix bugs, silence clippy, correct docs
Bugs: - extract: pick the OUTERMOST archive suffix (sfx.first(), not last()) so x.tar.gz uses the gz->tar path - gwshop: truncate the error snippet by chars, not bytes (UTF-8 panic) - sorter: py_stem_split now matches pathlib (0 < i < len-1) so a trailing dot is not treated as a suffix - sorter::apply_plan: a per-file link/copy/mkdir failure is logged and skipped instead of aborting the whole apply (never-abort contract) - orchestrate/state: add State::mark_logged and use it for every per-URL record so a state-save error cannot abort a run Cleanups: - remove dead is_archive_name and unused SourceInfo.manifest field - mark kept-but-unused capabilities with allow(dead_code) + rationale - let-chains / is_ok_and / is_multiple_of / replace([..]) / sort_by_key and other idiomatic simplifications across modules - difflib: SequenceMatcher owns its char vecs (bit-identical parity kept) - --only now enforces gofile|0807 via value_parser Docs: - SORTING.md: drop phantom sorter_plan.jsonl, os.link -> std::fs::hard_link, zipfile -> the zip crate - README: add --since to the dlp synopsis, clarify --subs/--assume-done - .gitignore: ignore catalogue/sorter/_logs/subscriptions run artifacts Validation: cargo fmt --check clean, cargo clippy --all-targets 0 warnings, cargo test 87 passed / 0 failed.
This commit is contained in:
+30
-19
@@ -16,27 +16,32 @@ pub struct Match {
|
||||
size: usize,
|
||||
}
|
||||
|
||||
pub struct SequenceMatcher<'a> {
|
||||
a: &'a str,
|
||||
b: &'a str,
|
||||
pub struct SequenceMatcher {
|
||||
/// `a`/`b` as chars, collected once (python works on code points; the
|
||||
/// matching routines index them repeatedly, so collecting here avoids
|
||||
/// re-allocating on every `find_longest_match` call).
|
||||
a_chars: Vec<char>,
|
||||
b_chars: Vec<char>,
|
||||
/// b[j] -> indices (purged of junk and popular elements, like `b2j`).
|
||||
b2j: std::collections::HashMap<char, Vec<usize>>,
|
||||
bjunk: std::collections::HashSet<char>,
|
||||
}
|
||||
|
||||
impl<'a> SequenceMatcher<'a> {
|
||||
pub fn new(a: &'a str, b: &'a str) -> Self {
|
||||
impl SequenceMatcher {
|
||||
pub fn new(a: &str, b: &str) -> Self {
|
||||
// __chain_b with isjunk=None: no junk, but autojunk purges
|
||||
// "popular" characters from long sequences (len(b) >= 200).
|
||||
let a_chars: Vec<char> = a.chars().collect();
|
||||
let b_chars: Vec<char> = b.chars().collect();
|
||||
let mut b2j: std::collections::HashMap<char, Vec<usize>> = std::collections::HashMap::new();
|
||||
for (idx, elt) in b.chars().enumerate() {
|
||||
for (idx, &elt) in b_chars.iter().enumerate() {
|
||||
b2j.entry(elt).or_default().push(idx);
|
||||
}
|
||||
let bjunk = std::collections::HashSet::new(); // isjunk is None
|
||||
let mut bpopular = std::collections::HashSet::new();
|
||||
let n = b.chars().count();
|
||||
let n = b_chars.len();
|
||||
if n >= 200 {
|
||||
let ntest = n / 100 + 1;
|
||||
let mut bpopular = std::collections::HashSet::new();
|
||||
for (&elt, idxs) in &b2j {
|
||||
if idxs.len() > ntest {
|
||||
bpopular.insert(elt);
|
||||
@@ -46,7 +51,12 @@ impl<'a> SequenceMatcher<'a> {
|
||||
b2j.remove(&elt);
|
||||
}
|
||||
}
|
||||
SequenceMatcher { a, b, b2j, bjunk }
|
||||
SequenceMatcher {
|
||||
a_chars,
|
||||
b_chars,
|
||||
b2j,
|
||||
bjunk,
|
||||
}
|
||||
}
|
||||
|
||||
fn isbjunk(&self, elt: char) -> bool {
|
||||
@@ -55,9 +65,10 @@ impl<'a> SequenceMatcher<'a> {
|
||||
|
||||
/// CPython SequenceMatcher.find_longest_match(alo, ahi, blo, bhi) over
|
||||
/// char indices.
|
||||
#[allow(clippy::needless_range_loop)] // `i` feeds the besti/bestj arithmetic
|
||||
fn find_longest_match(&self, alo: usize, ahi: usize, blo: usize, bhi: usize) -> Match {
|
||||
let a_chars: Vec<char> = self.a.chars().collect();
|
||||
let b_chars: Vec<char> = self.b.chars().collect();
|
||||
let a_chars = &self.a_chars;
|
||||
let b_chars = &self.b_chars;
|
||||
let nothing: Vec<usize> = Vec::new();
|
||||
|
||||
let mut besti = alo;
|
||||
@@ -129,8 +140,8 @@ impl<'a> SequenceMatcher<'a> {
|
||||
|
||||
/// All matching blocks, in order (recursive queue algorithm).
|
||||
pub fn get_matching_blocks(&self) -> Vec<Match> {
|
||||
let la = self.a.chars().count();
|
||||
let lb = self.b.chars().count();
|
||||
let la = self.a_chars.len();
|
||||
let lb = self.b_chars.len();
|
||||
let mut blocks: Vec<Match> = Vec::new();
|
||||
let mut queue: Vec<(usize, usize, usize, usize)> = vec![(0, la, 0, lb)];
|
||||
while let Some((alo, ahi, blo, bhi)) = queue.pop() {
|
||||
@@ -163,12 +174,12 @@ impl<'a> SequenceMatcher<'a> {
|
||||
pub fn quick_ratio(&self) -> f64 {
|
||||
let mut fullbcount: std::collections::HashMap<char, usize> =
|
||||
std::collections::HashMap::new();
|
||||
for elt in self.b.chars() {
|
||||
for &elt in &self.b_chars {
|
||||
*fullbcount.entry(elt).or_default() += 1;
|
||||
}
|
||||
let mut avail: std::collections::HashMap<char, usize> = std::collections::HashMap::new();
|
||||
let mut matches = 0usize;
|
||||
for elt in self.a.chars() {
|
||||
for &elt in &self.a_chars {
|
||||
let numb = avail
|
||||
.get(&elt)
|
||||
.copied()
|
||||
@@ -178,13 +189,13 @@ impl<'a> SequenceMatcher<'a> {
|
||||
matches += 1;
|
||||
}
|
||||
}
|
||||
Self::calculate_ratio(matches, self.a.chars().count(), self.b.chars().count())
|
||||
Self::calculate_ratio(matches, self.a_chars.len(), self.b_chars.len())
|
||||
}
|
||||
|
||||
/// Trivial upper bound (python `real_quick_ratio`).
|
||||
pub fn real_quick_ratio(&self) -> f64 {
|
||||
let la = self.a.chars().count();
|
||||
let lb = self.b.chars().count();
|
||||
let la = self.a_chars.len();
|
||||
let lb = self.b_chars.len();
|
||||
Self::calculate_ratio(la.min(lb), la, lb)
|
||||
}
|
||||
|
||||
@@ -192,7 +203,7 @@ impl<'a> SequenceMatcher<'a> {
|
||||
/// `SequenceMatcher(None, a, b).ratio()`.
|
||||
pub fn ratio(&self) -> f64 {
|
||||
let matches: usize = self.get_matching_blocks().iter().map(|m| m.size).sum();
|
||||
Self::calculate_ratio(matches, self.a.chars().count(), self.b.chars().count())
|
||||
Self::calculate_ratio(matches, self.a_chars.len(), self.b_chars.len())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user