Make the CI checks say what they mean, and format the workspace
The Android job's "Verify minimum API level" step has never verified the minimum API level. It took the first `*.so` anywhere under the target directory, which is a host proc-macro from debug/deps — an x86-64 object built by the runner's gcc, whose .comment section cannot mention Android and so can never contradict the expected value. It now reads the artifact under the target triple, compares against MIN_API parsed from the Dockerfile rather than a second copy of the number, and fails on a mismatch. Both sides are checked non-empty first: two failed parses would otherwise compare equal and pass, which is the same silent success in a new costume. The Android image installs one SDK package per layer and keeps the output. sdkmanager is a JVM program that aborts when it cannot get memory, and the single `> /dev/null` step reported that as a bare "exit code 134" while a retry re-downloaded everything that had already succeeded. tools/ci-local.sh runs all four jobs — desktop, android, layering, traceability — against the host toolchain, which is pinned to the same 1.92.0 CI installs. Its matrix check compares regeneration against the working tree rather than against HEAD: CI starts from a clean checkout, so git's answer is the right one there and reports every local run stale here. The rest is rustfmt across the workspace, and the clippy findings that surfaced once it did: manual_contains in dr-thumbs and collections_ui, a map iterated as pairs for its keys, an index loop over a slice, and two runtime assertions on a constant now made at compile time. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -219,7 +219,11 @@ pub fn apply_press(
|
||||
selection.clear();
|
||||
}
|
||||
|
||||
let (lo, hi) = if from <= row { (from, row) } else { (row, from) };
|
||||
let (lo, hi) = if from <= row {
|
||||
(from, row)
|
||||
} else {
|
||||
(row, from)
|
||||
};
|
||||
let hi = hi.min(ids.len().saturating_sub(1));
|
||||
for id in &ids[lo..=hi] {
|
||||
selection.insert(*id);
|
||||
@@ -290,7 +294,8 @@ pub fn refresh_tree(window: &AppWindow, ctl: &Rc<CollectionsController>, catalog
|
||||
|
||||
// Deep counts are one query per row. That is fine at sidebar scale and
|
||||
// wrong at grid scale, which is why the grid does not do this.
|
||||
let deep = coll::deep_count(catalog.connection(), id).unwrap_or(row.collection.direct_count);
|
||||
let deep =
|
||||
coll::deep_count(catalog.connection(), id).unwrap_or(row.collection.direct_count);
|
||||
|
||||
ids.push(id);
|
||||
smart.push(row.collection.kind == CollectionKind::Smart);
|
||||
@@ -348,8 +353,7 @@ fn compose_drag_image(thumbs: &[slint::Image]) -> slint::Image {
|
||||
let tw = ((size.width as f32 * scale) as u32).max(1);
|
||||
let th = ((size.height as f32 * scale) as u32).max(1);
|
||||
|
||||
let mut canvas =
|
||||
slint::SharedPixelBuffer::<slint::Rgba8Pixel>::new(tw + offset, th + offset);
|
||||
let mut canvas = slint::SharedPixelBuffer::<slint::Rgba8Pixel>::new(tw + offset, th + offset);
|
||||
let cw = canvas.width();
|
||||
let stride = cw as usize;
|
||||
let pixels = canvas.make_mut_slice();
|
||||
@@ -670,7 +674,12 @@ fn start_trash(
|
||||
window: &AppWindow,
|
||||
ctl: &Rc<CollectionsController>,
|
||||
catalog: &Rc<RefCell<Option<Catalog>>>,
|
||||
session: &Rc<dyn Fn() -> Option<(dr_sync_nextcloud::AppCredentials, dr_sync_nextcloud::Session)>>,
|
||||
session: &Rc<
|
||||
dyn Fn() -> Option<(
|
||||
dr_sync_nextcloud::AppCredentials,
|
||||
dr_sync_nextcloud::Session,
|
||||
)>,
|
||||
>,
|
||||
images: &[ImageId],
|
||||
reload: &Rc<dyn Fn()>,
|
||||
) {
|
||||
@@ -735,7 +744,12 @@ fn start_restore(
|
||||
window: &AppWindow,
|
||||
ctl: &Rc<CollectionsController>,
|
||||
catalog: &Rc<RefCell<Option<Catalog>>>,
|
||||
session: &Rc<dyn Fn() -> Option<(dr_sync_nextcloud::AppCredentials, dr_sync_nextcloud::Session)>>,
|
||||
session: &Rc<
|
||||
dyn Fn() -> Option<(
|
||||
dr_sync_nextcloud::AppCredentials,
|
||||
dr_sync_nextcloud::Session,
|
||||
)>,
|
||||
>,
|
||||
images: &[ImageId],
|
||||
reload: &Rc<dyn Fn()>,
|
||||
) {
|
||||
@@ -823,7 +837,11 @@ fn drain_trash(
|
||||
};
|
||||
|
||||
match msg {
|
||||
TrashMessage::Progress { done, total, failed } => {
|
||||
TrashMessage::Progress {
|
||||
done,
|
||||
total,
|
||||
failed,
|
||||
} => {
|
||||
let status = if failed > 0 {
|
||||
format!("{done} / {total} · {failed} failed")
|
||||
} else {
|
||||
@@ -925,7 +943,10 @@ pub fn wire<S, R, C>(
|
||||
) where
|
||||
S: Fn() + 'static,
|
||||
R: Fn() -> Vec<ImageId> + 'static,
|
||||
C: Fn() -> Option<(dr_sync_nextcloud::AppCredentials, dr_sync_nextcloud::Session)> + 'static,
|
||||
C: Fn() -> Option<(
|
||||
dr_sync_nextcloud::AppCredentials,
|
||||
dr_sync_nextcloud::Session,
|
||||
)> + 'static,
|
||||
{
|
||||
// Coerced to trait objects here rather than at each use: `start_trash` and
|
||||
// `drain_trash` are shared by three callbacks, and a generic parameter would
|
||||
@@ -933,7 +954,10 @@ pub fn wire<S, R, C>(
|
||||
let on_scope_changed: Rc<dyn Fn()> = Rc::new(on_scope_changed);
|
||||
let visible_ids = Rc::new(visible_ids);
|
||||
let session: Rc<
|
||||
dyn Fn() -> Option<(dr_sync_nextcloud::AppCredentials, dr_sync_nextcloud::Session)>,
|
||||
dyn Fn() -> Option<(
|
||||
dr_sync_nextcloud::AppCredentials,
|
||||
dr_sync_nextcloud::Session,
|
||||
)>,
|
||||
> = Rc::new(session);
|
||||
|
||||
// --- selection ---------------------------------------------------------
|
||||
@@ -1310,7 +1334,13 @@ pub fn wire<S, R, C>(
|
||||
crate::library::thumbs_dir(&sess.server, &sess.user_id),
|
||||
);
|
||||
|
||||
drain_trash(w.as_weak(), ctl.clone(), catalog.clone(), rx, reload.clone());
|
||||
drain_trash(
|
||||
w.as_weak(),
|
||||
ctl.clone(),
|
||||
catalog.clone(),
|
||||
rx,
|
||||
reload.clone(),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1702,7 +1732,7 @@ fn unique_name(conn: &rusqlite::Connection, parent: Option<CollectionId>) -> Str
|
||||
}
|
||||
for n in 2..1000 {
|
||||
let candidate = format!("{base} {n}");
|
||||
if !taken.iter().any(|t| *t == candidate) {
|
||||
if !taken.contains(&candidate) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
@@ -1948,7 +1978,11 @@ mod tests {
|
||||
}
|
||||
|
||||
/// The spring's inputs: rows, which have children, and which are collapsed.
|
||||
fn spring_fixture() -> (Vec<CollectionId>, Vec<bool>, std::collections::HashSet<CollectionId>) {
|
||||
fn spring_fixture() -> (
|
||||
Vec<CollectionId>,
|
||||
Vec<bool>,
|
||||
std::collections::HashSet<CollectionId>,
|
||||
) {
|
||||
let ids = vec![CollectionId(1), CollectionId(2), CollectionId(3)];
|
||||
// 1 is a collapsed parent, 2 an expanded parent, 3 a leaf.
|
||||
let has_children = vec![true, true, false];
|
||||
@@ -2000,11 +2034,16 @@ mod tests {
|
||||
// A tree that flaps open under every passing pointer is worse than one
|
||||
// that never opens. This pins the intent rather than the number: a
|
||||
// reflex-speed value here would be a regression, not a tuning choice.
|
||||
assert!(
|
||||
// Asserted in a const item rather than at runtime: the condition is
|
||||
// constant either way, and clippy is right that a runtime assert on it
|
||||
// is theatre. This form fails the build instead of a test run, which is
|
||||
// strictly earlier, and keeps the bound where a reader of the constant
|
||||
// will look for it.
|
||||
const _: () = assert!(
|
||||
SPRING_DELAY_MS >= 300,
|
||||
"a pointer crossing a parent must not open it"
|
||||
);
|
||||
assert!(
|
||||
const _: () = assert!(
|
||||
SPRING_DELAY_MS <= 900,
|
||||
"and a deliberate dwell must not feel like a hang"
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user