diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8ece70dba..9c6d956f9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -3,9 +3,10 @@ name: build permissions: contents: read -# Six jobs, five of which gate a merge: +# Seven jobs, six of which gate a merge: # Windows .exe — the real release binary, uploaded as an artifact. # Tests — `cargo test --workspace`. Blocking: a red suite is a red PR. +# Clippy — `cargo clippy --workspace --all-targets --locked -- -D warnings`. Blocking. # Audit — cargo-deny over the committed lockfile. Blocking. # Tour — deterministic interactive and AI documentation. Blocking. # Fmt — `cargo fmt --all -- --check`. Blocking. @@ -15,7 +16,7 @@ permissions: # .dmg ships, unevenly: arm64 is compiled, the Intel slice only # type-checked. Read its log; it never blocks. # -# THE LOCKFILE CONTRACT, which the three compiling jobs implement in the same three steps. +# THE LOCKFILE CONTRACT, which the four compiling jobs implement in the same three steps. # `Cargo.lock` is committed, so third-party versions move only in a deliberate commit. Each job # then, in this order and no other: # 1. verifies the committed lock still agrees with the manifests (`cargo fetch --locked`), @@ -55,9 +56,9 @@ on: workflow_dispatch: # A freeze makes exactly one thing worse: an advisory can be published against a pinned version # long after the last PR, and nothing would notice. This weekly run exists for the audit job - # alone — the three compiling jobs skip it, so it costs a couple of runner-minutes and writes + # alone — the four compiling jobs skip it, so it costs a couple of runner-minutes and writes # no cache. Side effect worth knowing when you read a commit's checks: each cron run attaches - # three `Skipped` entries to main's HEAD, so a commit whose gates really did pass can later + # four `Skipped` entries to main's HEAD, so a commit whose gates really did pass can later # display them as skipped. schedule: - cron: "0 6 * * 1" @@ -77,7 +78,7 @@ env: # Every job restores the cargo cache but only `main` writes it (`save-if` below). # A cache saved from a branch is readable only by that branch, so on any run whose -# manifest hash is new it is written once and never read again. Three jobs at ~1 GiB +# manifest hash is new it is written once and never read again. Four jobs at ~1 GiB # a piece fill the repository's 10 GB Actions quota fast, and once over it GitHub # evicts by least-recent-use — which takes the live caches, not the dead ones. jobs: @@ -143,6 +144,32 @@ jobs: - name: Run test suite run: cargo test --workspace --no-fail-fast --target x86_64-pc-windows-msvc + clippy: + name: Clippy (x86_64-msvc) + runs-on: windows-latest + # Compiles the workspace, so the weekly audit cron skips it like the other compiling jobs. + if: github.event_name != 'schedule' + steps: + - uses: actions/checkout@v7 + - uses: dtolnay/rust-toolchain@1.97.1 + with: + targets: x86_64-pc-windows-msvc + components: clippy + - uses: Swatinem/rust-cache@v2 + with: + save-if: ${{ github.ref == 'refs/heads/main' }} + # The lockfile contract — see the header. `--locked` on the clippy step then refuses any + # further rewrite after the MoonUI refresh has been checked. + - name: Verify the committed lockfile agrees with the manifests + run: cargo fetch --locked + - name: Refresh MoonUI only + run: cargo update -p moon-gpui -p moon-gpui-platform -p moon-ui + - name: Assert the refresh moved MoonUI and nothing else + shell: bash + run: bash .github/scripts/assert-only-moonui-moved.sh + - name: Lint the workspace + run: cargo clippy --workspace --all-targets --locked -- -D warnings + macos-probe: name: macOS probe (arm64 build, x86_64 check) — diagnostic runs-on: macos-14 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 098820b5c..75782b720 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -105,14 +105,15 @@ Three kinds of test, three homes. The toolchain dictates this, not taste: she posts the mark together with a comment. A listed author turns a wrong or stuck mark green by writing `/override` in the pull request. CI still only reports: nothing but you reading it stops a red merge. Branch from fresh `main`, open a PR, squash-merge — history stays linear. -- **CI runs `fmt`, not `clippy`.** Run `cargo clippy` yourself before pushing. The tree **is** +- **CI runs `fmt` and `clippy`.** The tree **is** rustfmt-clean: `cargo fmt --all` is the correct command, `rustfmt.toml` (`style_edition = "2024"`) is the authority, and CI enforces it via the `Fmt` job — which does not need the optional `private/uidoc` overlay to pass; `cargo fmt` must (and does) work without it. Blame history across the tree-wide reformat is preserved by `.git-blame-ignore-revs` — enable it locally with `git config blame.ignoreRevsFile .git-blame-ignore-revs`. -- Five CI gates, all on every PR and all meant to be green before you merge: the Windows +- Six CI gates, all on every PR and all meant to be green before you merge: the Windows `.exe` job (~15 min), `Tests (x86_64-msvc)` running `cargo test --workspace`, + `Clippy (x86_64-msvc)` running `cargo clippy --workspace --all-targets --locked -- -D warnings`, `Dependency audit (cargo-deny)`, `Tour` building the knowledge site, and `Fmt` running `cargo fmt --all -- --check`. They run in parallel. The macOS job is diagnostic (`continue-on-error`) — read its log, but it does not block. "Gate" is a convention here, not @@ -185,6 +186,8 @@ unnoticed. make build | run | release | check | fmt ``` +- `cargo clippy --workspace --all-targets -- -D warnings` must pass before a PR. + - Windows needs **VS 2022 Build Tools** (`vcvars64`). Resolve it with `vswhere -latest -find '**\vcvars64.bat'` — machines here carry both BuildTools and Community. - **Never wrap a build in `2>&1`.** `vcvars64.bat` writes to stderr on a healthy run, and diff --git a/Cargo.toml b/Cargo.toml index 9cde5226d..7959190fb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,3 +43,14 @@ opt-level = 2 [patch.crates-io] async-process = { git = "https://github.com/zed-industries/async-process.git", rev = "0b6d6713570af61806e1e5cb40e0f757cb93fd9d" } async-task = { git = "https://github.com/smol-rs/async-task.git", rev = "b4486cd71e4e94fbda54ce6302444de14f4d190e" } + +# Clippy is a merge gate (`cargo clippy --workspace --all-targets -- -D warnings`). +# These three stay allowed: fixing them is a refactor or would rewrite deliberate +# numeric comparisons, not a bugfix. Every crate opts in with `[lints] workspace = true`. +[workspace.lints.clippy] +# Constructors and draw helpers take one argument per field on purpose. +too_many_arguments = "allow" +# Nested results and iterators name the data; a typedef would only rename them. +type_complexity = "allow" +# `!(a < b)` is deliberate NaN handling. Never rewrite it to `a >= b`. +neg_cmp_op_on_partial_ord = "allow" diff --git a/crates/moon-chart/Cargo.toml b/crates/moon-chart/Cargo.toml index 9e51feadb..28d89ddcb 100644 --- a/crates/moon-chart/Cargo.toml +++ b/crates/moon-chart/Cargo.toml @@ -15,3 +15,6 @@ moon-core = { path = "../moon-core" } # Only for #[derive(Pod/Zeroable)] on instance types and ChartUniform. wgpu is no longer used: # the wgpu chart engine was removed with the egui binary; own-pass DX11 (chartdx) does the rendering. bytemuck = { version = "1", features = ["derive"] } + +[lints] +workspace = true diff --git a/crates/moon-chart/src/container.rs b/crates/moon-chart/src/container.rs index 9ae490549..3a47c1d4d 100644 --- a/crates/moon-chart/src/container.rs +++ b/crates/moon-chart/src/container.rs @@ -39,7 +39,7 @@ impl PaneSource { PaneSource::Manual => None, // Lazy on purpose: the eager form still computes the infinity this keeps out. PaneSource::AddToChart { born_ms, ttl_ms } => { - ttl_ms.is_finite().then(|| born_ms + ttl_ms) + ttl_ms.is_finite().then_some(born_ms + ttl_ms) } } } diff --git a/crates/moon-chart/src/order_geometry.rs b/crates/moon-chart/src/order_geometry.rs index 71003f915..b72da59fb 100644 --- a/crates/moon-chart/src/order_geometry.rs +++ b/crates/moon-chart/src/order_geometry.rs @@ -729,9 +729,9 @@ pub fn build_order_geometry( // the per-tab flag would leave a row of dots marking steps of a staircase that is no // longer there. if st.knots && !has_server_trace && show_move_history { - for i in 1..n { + for point in points.iter().skip(1) { markers.push(MarkerInstance::at_price( - to_rel(points[i].0), + to_rel(point.0), cur_p, st.knot_size * highlight_marker_mul, st.marker_thickness * highlight_thickness_mul, diff --git a/crates/moon-chart/src/trade_marks/tests.rs b/crates/moon-chart/src/trade_marks/tests.rs index 6d1e99d39..a0cb69855 100644 --- a/crates/moon-chart/src/trade_marks/tests.rs +++ b/crates/moon-chart/src/trade_marks/tests.rs @@ -141,9 +141,11 @@ fn normalize_folds_the_band_fields_onto_the_one_switch() { use moon_core::market::candles::{ VOLUME_STYLE_HILLS, VOLUME_STYLE_LEGACY_BARS, VOLUME_STYLE_OFF, }; - let mut cfg = ChartGraphicsCfg::default(); - cfg.candle_volume_style = VOLUME_STYLE_LEGACY_BARS; - cfg.candle_volume_sides = false; + let mut cfg = ChartGraphicsCfg { + candle_volume_style: VOLUME_STYLE_LEGACY_BARS, + candle_volume_sides: false, + ..ChartGraphicsCfg::default() + }; let out = normalize_chart_graphics(cfg); assert_eq!(out.candle_volume_style, VOLUME_STYLE_HILLS); assert!(out.candle_volume_sides); diff --git a/crates/moon-chart/src/view.rs b/crates/moon-chart/src/view.rs index 9c9f0672a..6d311beff 100644 --- a/crates/moon-chart/src/view.rs +++ b/crates/moon-chart/src/view.rs @@ -552,7 +552,7 @@ impl ChartView { let area_delta = area_w - self.last_phase_area_w; // Every shrink matters for the six-hour lower bound; sub-pixel growth can safely retain // the previous scale because it only increases the visible history. - let area_changed = area_delta < 0.0 || area_delta >= 0.5; + let area_changed = !(0.0..0.5).contains(&area_delta); let present_changed = (present_hz - self.last_phase_present_hz).abs() >= 0.5; let phase_changed = area_changed || present_changed; if phase_changed || self.x_init_pending { diff --git a/crates/moon-core/Cargo.toml b/crates/moon-core/Cargo.toml index b261dd278..58d754b9e 100644 --- a/crates/moon-core/Cargo.toml +++ b/crates/moon-core/Cargo.toml @@ -119,3 +119,6 @@ windows-sys = { version = "0.61", features = [ # Descriptor count on Exhausted log lines: `fcntl(F_GETFD)` is a kernel query # that allocates no descriptor, so the probe still answers at the open-file cliff. libc = "0.2" + +[lints] +workspace = true diff --git a/crates/moon-core/examples/db_read_timing.rs b/crates/moon-core/examples/db_read_timing.rs index 552e0cc00..9374748c1 100644 --- a/crates/moon-core/examples/db_read_timing.rs +++ b/crates/moon-core/examples/db_read_timing.rs @@ -342,7 +342,7 @@ fn dump_all(period_name: &str, from: i64, to: i64, pool: &Pool) { let monitor = moon_core::db::analytics::profit_monitor(&q(ProfitMetric::Quote)); println!("{period_name}|profit_monitor|{monitor:?}"); - if let Some(conn) = moon_core::db::open_reader().ok() { + if let Ok(conn) = moon_core::db::open_reader() { if let Ok(snap) = moon_core::db::read_snapshot(&conn) { let filter = ReportFilter::default(); if let Ok(cores) = moon_core::db::distinct_cores(&snap) { diff --git a/crates/moon-core/src/backup_store.rs b/crates/moon-core/src/backup_store.rs index e56e05aa9..3e985fdfa 100644 --- a/crates/moon-core/src/backup_store.rs +++ b/crates/moon-core/src/backup_store.rs @@ -193,7 +193,7 @@ impl<'a> SnapshotStore<'a> { return false; }; let mut found = Vec::::new(); - while let Some(entry) = entries.next() { + for entry in entries.by_ref() { let Ok(entry) = entry else { return false; }; diff --git a/crates/moon-core/src/backups.rs b/crates/moon-core/src/backups.rs index 8e68a8db7..580418ce5 100644 --- a/crates/moon-core/src/backups.rs +++ b/crates/moon-core/src/backups.rs @@ -264,7 +264,7 @@ fn launch_settings_job() { "settings", &SETTINGS_JOB_ACTIVE, false, - |now_ms| crate::config::backup_due_at(now_ms), + crate::config::backup_due_at, ); } diff --git a/crates/moon-core/src/config/detect_view/tests.rs b/crates/moon-core/src/config/detect_view/tests.rs index b3db645dd..26d8838b1 100644 --- a/crates/moon-core/src/config/detect_view/tests.rs +++ b/crates/moon-core/src/config/detect_view/tests.rs @@ -4,6 +4,8 @@ use super::*; /// detects_view.toml and for Copy/Paste): active size, per-size w/h/chart/rail, /// and every flag in every slot. #[test] +// Nested slot writes cannot move into one literal; each field stays beside the round-trip it proves. +#[allow(clippy::field_reassign_with_default)] fn detect_view_roundtrip_preserves_every_field() { let mut cfg = DetectViewCfg::default(); cfg.size = DETECT_SIZE_LARGE; diff --git a/crates/moon-core/src/config/layout.rs b/crates/moon-core/src/config/layout.rs index 92d54d29c..ddd965cee 100644 --- a/crates/moon-core/src/config/layout.rs +++ b/crates/moon-core/src/config/layout.rs @@ -2203,7 +2203,7 @@ impl WindowLayout { |_| false, ); let moved = match self.kind_defaults_mut(kind) { - Some(d) => std::mem::replace(&mut d.candle_view, Some(value)) != Some(value), + Some(d) => d.candle_view.replace(value) != Some(value), None => std::mem::replace(&mut self.candle_view, value) != value, }; split || moved @@ -2221,7 +2221,7 @@ impl WindowLayout { |_| false, ); let moved = match self.kind_defaults_mut(kind) { - Some(d) => std::mem::replace(&mut d.chart_graphics, Some(value)) != Some(value), + Some(d) => d.chart_graphics.replace(value) != Some(value), None => std::mem::replace(&mut self.chart_graphics, value) != value, }; split || moved diff --git a/crates/moon-core/src/config/migrate.rs b/crates/moon-core/src/config/migrate.rs index 322ac81e8..8a3beef28 100644 --- a/crates/moon-core/src/config/migrate.rs +++ b/crates/moon-core/src/config/migrate.rs @@ -51,7 +51,7 @@ fn config_from_legacy_enc(plain: &[u8], uid_floor: Option) -> anyhow::Resul groups: Vec, } - let old: Old = toml::from_str(std::str::from_utf8(&plain)?)?; + let old: Old = toml::from_str(std::str::from_utf8(plain)?)?; let servers = old .servers .into_iter() diff --git a/crates/moon-core/src/config/moonbot_import/schema_v7.rs b/crates/moon-core/src/config/moonbot_import/schema_v7.rs index 4c6b5f7be..872d169e4 100644 --- a/crates/moon-core/src/config/moonbot_import/schema_v7.rs +++ b/crates/moon-core/src/config/moonbot_import/schema_v7.rs @@ -237,7 +237,7 @@ pub fn parse_payload(payload: &[u8]) -> Result { } match kind { // Signals/Trading/Visual must be present, but their contents are skipped. - 1 | 2 | 3 => {} + 1..=3 => {} 4 => theme = Some(parse_theme(sub)?), 5 => ini = Some(parse_ini(sub)?), 6 => ui = Some(parse_ui(sub)?), @@ -409,6 +409,8 @@ pub(super) mod build { } /// Builds a UI v3 body with specified hotkey values and fixed values for everything else. + // Each `push` is one labelled wire byte. Folding them into a literal would drop those names. + #[allow(clippy::vec_init_then_push)] pub fn ui_body( order_sizes: [f64; 6], order_size_keys: [u16; 6], diff --git a/crates/moon-core/src/config/paths.rs b/crates/moon-core/src/config/paths.rs index bb7fb8627..b7a58c2e4 100644 --- a/crates/moon-core/src/config/paths.rs +++ b/crates/moon-core/src/config/paths.rs @@ -6,8 +6,9 @@ //! and settings: //! - macOS: `~/Library/Application Support/com.moonbot.moonterminal/` //! - Linux: `~/.config/com.moonbot.moonterminal/` -//! `servers.enc` is encrypted with its own file key, wrapped by one slot per machine (each -//! machine's key lives in its OS keyring) plus an optional password slot; see `crypto/`. +//! +//! `servers.enc` is encrypted with its own file key, wrapped by one slot per machine (each +//! machine's key lives in its OS keyring) plus an optional password slot; see `crypto/`. //! //! All paths are based on `data_dir()`; on Windows, `data_dir() == exe_dir()`. //! diff --git a/crates/moon-core/src/config/tests.rs b/crates/moon-core/src/config/tests.rs index 284ecae20..f35503b78 100644 --- a/crates/moon-core/src/config/tests.rs +++ b/crates/moon-core/src/config/tests.rs @@ -85,9 +85,11 @@ fn a_malformed_core_list_is_refused() { /// a pinned layout was impossible. #[test] fn a_plaintext_config_keeps_the_settings_it_was_given() { - let mut settings = super::schema::SettingsFile::default(); - settings.charts_split_by_core = false; - settings.chart_stack_height = 321; + let settings = super::schema::SettingsFile { + charts_split_by_core: false, + chart_stack_height: 321, + ..super::schema::SettingsFile::default() + }; let config = AppConfig::build_plaintext_config( None, diff --git a/crates/moon-core/src/config/theme/tests.rs b/crates/moon-core/src/config/theme/tests.rs index 1d0ed3a5a..e010af58f 100644 --- a/crates/moon-core/src/config/theme/tests.rs +++ b/crates/moon-core/src/config/theme/tests.rs @@ -36,8 +36,10 @@ fn share_roundtrip() { /// Old flat theme.toml becomes dark while the caller's light theme remains current. #[test] fn share_flat_legacy_goes_dark() { - let mut flat = ChartTheme::default(); - flat.bg = [10, 20, 30]; + let flat = ChartTheme { + bg: [10, 20, 30], + ..ChartTheme::default() + }; let text = toml::to_string_pretty(&flat).unwrap(); let mut current = ChartThemeSet::default(); current.light.bg = [200, 200, 200]; @@ -128,6 +130,8 @@ fn current_revision_keeps_a_deliberately_reselected_retired_value() { /// `theme.rs:ChartThemeSet::retire_old_defaults` must stamp a revision-zero custom file once; /// otherwise every launch re-examines and rewrites a palette with no old colours left to move. #[test] +// Each palette channel is named on its own line so a round-trip mismatch points at that channel. +#[allow(clippy::field_reassign_with_default)] fn revision_zero_customised_palette_is_stamped_once() { let mut set = ChartThemeSet::default(); set.palette_rev = absent_palette_rev(); @@ -331,8 +335,10 @@ fn sharing_future_dark_table_with_current_light_uses_the_lower_generation() { /// taking the higher live generation would prevent a later build from migrating the pasted side. #[test] fn sharing_older_dark_table_with_newer_light_uses_the_lower_generation() { - let mut current = ChartThemeSet::default(); - current.palette_rev = CURRENT_PALETTE_REV + 2; + let current = ChartThemeSet { + palette_rev: CURRENT_PALETTE_REV + 2, + ..ChartThemeSet::default() + }; let text = format!( "palette_rev = {}\n\n[dark]\nbg = [11, 22, 33]\n", CURRENT_PALETTE_REV + 1 @@ -372,8 +378,10 @@ fn sharing_retires_both_tables_but_never_the_live_flat_light_set() { assert_eq!(parsed, ChartThemeSet::default()); let flat_text = toml::to_string_pretty(&paired.dark).expect("flat themes serialize"); - let mut current = ChartThemeSet::default(); - current.light = paired.light.clone(); + let current = ChartThemeSet { + light: paired.light.clone(), + ..ChartThemeSet::default() + }; let live_light = current.light.clone(); let parsed = ChartThemeSet::parse_share(&flat_text, ¤t).expect("a flat theme parses"); diff --git a/crates/moon-core/src/crowd/standing/tests.rs b/crates/moon-core/src/crowd/standing/tests.rs index 8606e3e9d..2195b44f4 100644 --- a/crates/moon-core/src/crowd/standing/tests.rs +++ b/crates/moon-core/src/crowd/standing/tests.rs @@ -21,7 +21,7 @@ fn a_row_carries_both_sides_of_the_minute() { #[test] fn the_loud_come_first() { - let mut rows = vec![ + let mut rows = [ Standing::live("BTC", &stat(10.0, 5.0)), Standing::live("ETH", &stat(100.0, 5.0)), ]; diff --git a/crates/moon-core/src/data/orderbook.rs b/crates/moon-core/src/data/orderbook.rs index 3c014da29..acf50d46a 100644 --- a/crates/moon-core/src/data/orderbook.rs +++ b/crates/moon-core/src/data/orderbook.rs @@ -170,6 +170,11 @@ impl OrderBookModel { self.raw.len() } + /// Returns whether the book currently holds no levels. + pub fn is_empty(&self) -> bool { + self.raw.is_empty() + } + /// Returns the book's best `(bid, ask)`. If only one side is populated, its best price is /// returned in both positions for a zero spread. An empty book or invalid best price returns /// `None`. `raw` stores descending bids followed by ascending asks, so the first `!is_ask` diff --git a/crates/moon-core/src/db/analytics/groups.rs b/crates/moon-core/src/db/analytics/groups.rs index 95f662c92..fdf8a7720 100644 --- a/crates/moon-core/src/db/analytics/groups.rs +++ b/crates/moon-core/src/db/analytics/groups.rs @@ -237,8 +237,8 @@ pub(in crate::db) fn coin_groups_from_source( q: &Query, src: &str, ) -> ReadResult> { - let raw_src = raw_source(conn, &q)?; - groups(conn, &src, raw_src.as_deref(), &q, false, false) + let raw_src = raw_source(conn, q)?; + groups(conn, src, raw_src.as_deref(), q, false, false) } /// Build the lens-neutral source used by raw-profit and average-order enrichments. diff --git a/crates/moon-core/src/db/analytics/mod.rs b/crates/moon-core/src/db/analytics/mod.rs index c218c4616..b73ed559a 100644 --- a/crates/moon-core/src/db/analytics/mod.rs +++ b/crates/moon-core/src/db/analytics/mod.rs @@ -374,6 +374,8 @@ pub fn calendar_data( } /// Preflight result that prevents unsafe raw-money analytics from carrying scalar data. +// `Split` carries the full quote breakdown. Boxing it would touch every analytics consumer. +#[allow(clippy::large_enum_variant)] enum ScopeDecision { /// Scalar values share this explicit unit. Comparable { diff --git a/crates/moon-core/src/db/analytics/summary_stream.rs b/crates/moon-core/src/db/analytics/summary_stream.rs index 24ad4172f..47653398a 100644 --- a/crates/moon-core/src/db/analytics/summary_stream.rs +++ b/crates/moon-core/src/db/analytics/summary_stream.rs @@ -136,11 +136,7 @@ impl GroupAccumulator { self.worst = Some(self.worst.map_or(value, |worst| worst.min(value))); } if let Some(name) = &row.core_name { - if self - .core_name - .as_ref() - .map_or(true, |current| name > current) - { + if self.core_name.as_ref().is_none_or(|current| name > current) { self.core_name = Some(name.clone()); } } diff --git a/crates/moon-core/src/db/analytics/tests.rs b/crates/moon-core/src/db/analytics/tests.rs index 2195aa540..790755451 100644 --- a/crates/moon-core/src/db/analytics/tests.rs +++ b/crates/moon-core/src/db/analytics/tests.rs @@ -312,7 +312,7 @@ fn one_current_stream_preserves_every_summary_field() { profit: 6.0, raw_profit: 6.0, avg_order: 100.0, - quote: quote.clone(), + quote, wins: 1, pf: 2.5, best: 10.0, @@ -1096,7 +1096,7 @@ fn corrupt_replica_surfaces_error_not_empty() { let res = summary_on(&conn, &wide, false, false); assert!( - !matches!(res, Ok(_)), + !res.is_ok(), "ошибка чтения не должна превращаться в успешный — в том числе \ пустой или частичный — период: это и есть чинимый баг" ); diff --git a/crates/moon-core/src/db/analytics/time_zone.rs b/crates/moon-core/src/db/analytics/time_zone.rs index 6e97fe145..e44b90d49 100644 --- a/crates/moon-core/src/db/analytics/time_zone.rs +++ b/crates/moon-core/src/db/analytics/time_zone.rs @@ -86,11 +86,11 @@ pub(crate) fn install(conn: &Connection, axis: &ReportAxis) -> rusqlite::Result< // by two conversions cancelling. conn.create_scalar_function("mt_core_minute_of_day", 1, flags, move |ctx| { let secs = ctx.get::(0)?; - Ok(crate::util::display_time::minute_of_day(secs, chrono_tz::UTC).unwrap_or(0) as i64) + Ok(crate::util::display_time::minute_of_day(secs, chrono_tz::UTC).unwrap_or(0)) })?; conn.create_scalar_function("mt_core_minute_of_week", 1, flags, move |ctx| { let secs = ctx.get::(0)?; - Ok(crate::util::display_time::minute_of_week(secs, chrono_tz::UTC).unwrap_or(0) as i64) + Ok(crate::util::display_time::minute_of_week(secs, chrono_tz::UTC).unwrap_or(0)) })?; Ok(()) diff --git a/crates/moon-core/src/db/quote.rs b/crates/moon-core/src/db/quote.rs index a598d345e..030d05724 100644 --- a/crates/moon-core/src/db/quote.rs +++ b/crates/moon-core/src/db/quote.rs @@ -1183,7 +1183,9 @@ pub enum ProfitUnit { } /// Type-level boundary between comparable analytics and split raw totals. +// `Split` is the quote breakdown by value. Boxing it changes every match on this public enum. #[derive(Clone, Debug)] +#[allow(clippy::large_enum_variant)] pub enum ProfitScope { /// Scalar data whose values share one explicit unit. Comparable { unit: ProfitUnit, data: T }, diff --git a/crates/moon-core/src/db/report_axis.rs b/crates/moon-core/src/db/report_axis.rs index a4657dd88..2eee1fb3f 100644 --- a/crates/moon-core/src/db/report_axis.rs +++ b/crates/moon-core/src/db/report_axis.rs @@ -410,7 +410,7 @@ impl ReportAxis { /// the honest signal that the uncorrected single-branch predicate is still correct. pub fn measured_groups(&self, at: i64) -> Vec<(i32, Vec)> { let mut by_offset: HashMap> = HashMap::new(); - for (&core_uid, _) in self.segments.iter() { + for &core_uid in self.segments.keys() { if let Some(offset) = self.offset_secs(core_uid, at) { by_offset.entry(offset).or_default().push(core_uid); } diff --git a/crates/moon-core/src/db/report_read/tests.rs b/crates/moon-core/src/db/report_read/tests.rs index c6db9dd46..b817a384e 100644 --- a/crates/moon-core/src/db/report_read/tests.rs +++ b/crates/moon-core/src/db/report_read/tests.rs @@ -2807,7 +2807,7 @@ fn chart_history_preserves_optional_millisecond_columns_and_legacy_rows() { thread_local! { static CHART_HISTORY_SQL: std::cell::RefCell> = - std::cell::RefCell::new(Vec::new()); + const { std::cell::RefCell::new(Vec::new()) }; } /// Keep expanded chart-history SELECTs so the test can `EXPLAIN` the statement the function prepared. diff --git a/crates/moon-core/src/db/tests.rs b/crates/moon-core/src/db/tests.rs index e22a175cd..b85bf6af9 100644 --- a/crates/moon-core/src/db/tests.rs +++ b/crates/moon-core/src/db/tests.rs @@ -1110,7 +1110,7 @@ fn corrupt_replica_fails_report_query_instead_of_truncating() { let res = query_reports(&conn, &filter, "closedate", true, 5_000); assert!( - !matches!(res, Ok(_)), + !res.is_ok(), "сбой чтения обязан вернуть ошибку, а не частичную/пустую таблицу: \ такой файл экспорта неотличим от полного" ); @@ -1158,7 +1158,7 @@ fn corrupt_replica_fails_report_totals_instead_of_zeroing() { let res = query_totals(&conn, &filter); assert!( - !matches!(res, Ok(_)), + !res.is_ok(), "сбой агрегата обязан вернуть ошибку, а не (0.0, 0)" ); diff --git a/crates/moon-core/src/db/tuner/mod.rs b/crates/moon-core/src/db/tuner/mod.rs index 7f1baaa09..61e4ce4a6 100644 --- a/crates/moon-core/src/db/tuner/mod.rs +++ b/crates/moon-core/src/db/tuner/mod.rs @@ -881,7 +881,7 @@ pub fn suggest_field( /// Best range for one field over `(value, profit)` samples. fn best_range( - vals: &mut Vec<(f64, f64)>, + vals: &mut [(f64, f64)], min_n: usize, edges: usize, round: bool, diff --git a/crates/moon-core/src/db/tuner/threshold_search/search/tests.rs b/crates/moon-core/src/db/tuner/threshold_search/search/tests.rs index a848c2eaf..0beffd185 100644 --- a/crates/moon-core/src/db/tuner/threshold_search/search/tests.rs +++ b/crates/moon-core/src/db/tuner/threshold_search/search/tests.rs @@ -216,6 +216,8 @@ fn one_restart_reference(s: &Search, allow: &[bool], restart: usize, rng: &mut R } } let mut profit = 0.0f64; + // `t` indexes the failure flag and the profit column together. A one-slice iterator drops one. + #[allow(clippy::needless_range_loop)] for t in 0..n { if fail[t] == 0 { profit += s.cols.profits[t]; diff --git a/crates/moon-core/src/db/tuner/time/tests.rs b/crates/moon-core/src/db/tuner/time/tests.rs index 54e1d5a4d..6643368dc 100644 --- a/crates/moon-core/src/db/tuner/time/tests.rs +++ b/crates/moon-core/src/db/tuner/time/tests.rs @@ -166,8 +166,8 @@ fn suggest_time_never_worse_than_base() { .iter() .filter(|&&(wd, mn, _)| { s.week_span - .map_or(true, |(f, t)| span_ok(wd * 1440 + mn, f as i64, t as i64)) - && s.tod.map_or(true, |tw| match tw { + .is_none_or(|(f, t)| span_ok(wd * 1440 + mn, f as i64, t as i64)) + && s.tod.is_none_or(|tw| match tw { TimeWindow::Day(f, t) => span_ok(mn, f as i64, t as i64), TimeWindow::Hour(f, t) => span_ok(mn % 60, f as i64, t as i64), }) diff --git a/crates/moon-core/src/db/valuation/health.rs b/crates/moon-core/src/db/valuation/health.rs index 6ae8c126a..a2194589c 100644 --- a/crates/moon-core/src/db/valuation/health.rs +++ b/crates/moon-core/src/db/valuation/health.rs @@ -460,7 +460,7 @@ fn retry_delay(consecutive_failures: u32) -> Duration { /// Returns: /// `true` for the first few attempts and then periodically. const fn should_log(attempt: u32) -> bool { - matches!(attempt, 1 | 2 | 3 | 5 | 10) || (attempt > 10 && attempt % 20 == 0) + matches!(attempt, 1 | 2 | 3 | 5 | 10) || (attempt > 10 && attempt.is_multiple_of(20)) } /// Emit one failing-run line in the shape every stage shares. diff --git a/crates/moon-core/src/db/valuation/mod.rs b/crates/moon-core/src/db/valuation/mod.rs index 3813f3bd9..e8c180ed0 100644 --- a/crates/moon-core/src/db/valuation/mod.rs +++ b/crates/moon-core/src/db/valuation/mod.rs @@ -1132,10 +1132,10 @@ fn retire_live_family(files: &[PathBuf; 3], pending: &Path) -> std::io::Result<( } for path in files { if path.try_exists()? { - return Err(std::io::Error::new( - std::io::ErrorKind::Other, - format!("live valuation member remains: {}", path.display()), - )); + return Err(std::io::Error::other(format!( + "live valuation member remains: {}", + path.display() + ))); } } Ok(()) @@ -1388,7 +1388,7 @@ fn attach_store(conn: &Connection, path: &Path) -> ReadResult { let _lifecycle = CACHE_LIFECYCLE .read() .unwrap_or_else(|poisoned| poisoned.into_inner()); - match std::fs::metadata(&path) { + match std::fs::metadata(path) { Ok(_) => {} Err(error) if error.kind() == std::io::ErrorKind::NotFound => return Ok(false), Err(error) => { diff --git a/crates/moon-core/src/db/valuation/provider.rs b/crates/moon-core/src/db/valuation/provider.rs index 367d11d9a..dc6751848 100644 --- a/crates/moon-core/src/db/valuation/provider.rs +++ b/crates/moon-core/src/db/valuation/provider.rs @@ -146,9 +146,9 @@ impl HttpSpotRateSource { .get("https://data-api.binance.vision/api/v3/klines") .query("symbol", symbol) .query("interval", "1m") - .query("startTime", &start_ms.to_string()) - .query("endTime", &end_ms.to_string()) - .query("limit", &limit.to_string()) + .query("startTime", start_ms.to_string()) + .query("endTime", end_ms.to_string()) + .query("limit", limit.to_string()) .call() .map_err(classify_http_error)?; let status = response.status().as_u16(); @@ -182,9 +182,9 @@ impl HttpSpotRateSource { .query("category", "spot") .query("symbol", symbol) .query("interval", "1") - .query("start", &start_ms.to_string()) - .query("end", &end_ms.to_string()) - .query("limit", &limit.to_string()) + .query("start", start_ms.to_string()) + .query("end", end_ms.to_string()) + .query("limit", limit.to_string()) .call() .map_err(classify_http_error)?; let status = response.status().as_u16(); diff --git a/crates/moon-core/src/db/valuation/worker.rs b/crates/moon-core/src/db/valuation/worker.rs index 4b20d0a4d..df0be4dff 100644 --- a/crates/moon-core/src/db/valuation/worker.rs +++ b/crates/moon-core/src/db/valuation/worker.rs @@ -493,7 +493,12 @@ fn run_worker( if current.expire_stale(now_ms) { current.publish_snapshot(&generation, &dirty, now_ms); } - match attempt( + // Only `Ran { more: true }` short-circuits, and it must: the remaining currencies + // would otherwise be resolved a minute apart. Everything else falls through, + // `Attempt::CacheLost` included — a provider failure while the derived cache is + // also unhealthy belongs to the recovery stage below, whose own fault is the one + // that matters in that window. + if let Attempt::Done(StageTurn::Ran { more: true }) = attempt( &mut status, &mut sink, ValuationStage::CurrentRates, @@ -501,13 +506,7 @@ fn run_worker( &mut retry_after, || refresh_current_rates(source.as_ref(), &generation, &dirty, &mut current), ) { - // Only `Ran { more: true }` short-circuits, and it must: the remaining currencies - // would otherwise be resolved a minute apart. Everything else falls through, - // `Attempt::CacheLost` included — a provider failure while the derived cache is - // also unhealthy belongs to the recovery stage below, whose own fault is the one - // that matters in that window. - Attempt::Done(StageTurn::Ran { more: true }) => continue, - _ => {} + continue; } } else { // Current-rate mode is disabled, so this stage issues no provider request at all — the diff --git a/crates/moon-core/src/feed/live/client_settings.rs b/crates/moon-core/src/feed/live/client_settings.rs index c6272d59e..ebfeb0765 100644 --- a/crates/moon-core/src/feed/live/client_settings.rs +++ b/crates/moon-core/src/feed/live/client_settings.rs @@ -150,6 +150,8 @@ enum SequenceOp { } /// Pure next action selected from a retained settings snapshot. +// `Send` owns the settings snapshot this queue is about to write. Boxing it allocates on that path. +#[allow(clippy::large_enum_variant)] enum SequenceAction { /// No work is currently possible or required. Idle, diff --git a/crates/moon-core/src/feed/live/commands.rs b/crates/moon-core/src/feed/live/commands.rs index 9683d0e9d..f063088d6 100644 --- a/crates/moon-core/src/feed/live/commands.rs +++ b/crates/moon-core/src/feed/live/commands.rs @@ -1044,6 +1044,9 @@ pub(super) fn drain_commands( .map(|spec| anchor_on_core(spec.insert_after, server.id)) .collect(); let positions = plan_insert_positions(&ids, &anchors); + // `next_id` is the next strategy id, not a loop index. The suggested zip + // recomputes that start and is harder to see fail closed. + #[allow(clippy::explicit_counter_loop)] for (spec, at) in specs.iter().zip(positions) { let id = next_id; next_id += 1; diff --git a/crates/moon-core/src/feed/live/convert/tests.rs b/crates/moon-core/src/feed/live/convert/tests.rs index 32cd0a9d7..76afaebec 100644 --- a/crates/moon-core/src/feed/live/convert/tests.rs +++ b/crates/moon-core/src/feed/live/convert/tests.rs @@ -34,6 +34,8 @@ fn startup_status_maps_local_port_and_physical_packet_fields() { /// `KernelHealth` between releases; an exhaustive literal here turns every such /// addition into a red CI run on a commit that touched nothing near it. #[test] +// `..Default::default()` keeps this compiling when `KernelHealth` grows a field the converter ignores. +#[allow(clippy::needless_update)] fn kernel_health_maps_each_field_by_scope() { let h = moonproto::state::KernelHealth { process_cpu_percent: 12, diff --git a/crates/moon-core/src/feed/live/mod.rs b/crates/moon-core/src/feed/live/mod.rs index a97a85e1d..751567eaa 100644 --- a/crates/moon-core/src/feed/live/mod.rs +++ b/crates/moon-core/src/feed/live/mod.rs @@ -2701,11 +2701,10 @@ pub(super) fn run( // Do NOT copy market data here. The feed only signals that the provider has a fresh // read-model snapshot; the visible chart pulls the markets it needs itself. - if !dirty_markets.is_empty() { - if tx.send(FeedMsg::MarketDataChanged(dirty_markets)).is_err() { - let _ = client.disconnect(); - return Ok(()); - } + if !dirty_markets.is_empty() && tx.send(FeedMsg::MarketDataChanged(dirty_markets)).is_err() + { + let _ = client.disconnect(); + return Ok(()); } force_market_sample = false; diff --git a/crates/moon-core/src/feed/mod.rs b/crates/moon-core/src/feed/mod.rs index c438ab91b..9a906e82a 100644 --- a/crates/moon-core/src/feed/mod.rs +++ b/crates/moon-core/src/feed/mod.rs @@ -126,6 +126,8 @@ impl FeedTx { Self { data, wake } } + // The error returns the unsent message. Boxing it would change this public `Result`. + #[allow(clippy::result_large_err)] pub fn send(&self, msg: FeedMsg) -> Result<(), SendError> { self.data.send(msg)?; if let Some(wake) = &self.wake { @@ -301,7 +303,9 @@ impl OrderStopsForm { } /// Commands from the coordinator to a core backend that define the core's market role. +// `EditCoreConfig` carries the whole config. Boxing it allocates on the command path. #[derive(Debug, Clone)] +#[allow(clippy::large_enum_variant)] pub enum CoreCmd { /// Desired market role of the core as full state, not a delta. /// @@ -811,6 +815,8 @@ impl CoreCmdTx { /// /// A market-role snapshot is published only after the matching queue send succeeds. Holding the /// snapshot lock across both operations gives cloned senders and the consumer one order. + // The error returns the unsent command. Boxing it would change this public `Result`. + #[allow(clippy::result_large_err)] pub fn send(&self, cmd: CoreCmd) -> Result<(), SendError> { let assignment = match &cmd { CoreCmd::SetMarket { diff --git a/crates/moon-core/src/feed/trade.rs b/crates/moon-core/src/feed/trade.rs index 7c2b586ea..15950682d 100644 --- a/crates/moon-core/src/feed/trade.rs +++ b/crates/moon-core/src/feed/trade.rs @@ -632,6 +632,7 @@ pub(super) fn split_order_for_market( /// - an enabled stop's level comes from wire (≠0) → disable memory → strategy field /// ("StopLoss"/"TrailingStop", whose strategy percentages are negative → abs) → ClientSettings /// default (SL `price_drop_level` / TS `trailing_drop`). VStop uses wire/memory only. +/// /// The runtime compares against the live model through send-if-changed. SL/TS → `update_stops`, /// VStop → `update_vstop`. pub(super) fn set_order_stop( @@ -961,7 +962,7 @@ fn restore_from_wire_or_memory( if level != 0.0 && level.is_finite() { return Some((fixed, level, extra)); } - stop_memory_get(server_id, uid, kind).map(|(f, l, s)| (f, l, s)) + stop_memory_get(server_id, uid, kind) } /// Explicit per-order stop overrides made FROM THE TERMINAL, scoped to the session. diff --git a/crates/moon-core/src/feed/types.rs b/crates/moon-core/src/feed/types.rs index 2f17d63f4..40f217ac0 100644 --- a/crates/moon-core/src/feed/types.rs +++ b/crates/moon-core/src/feed/types.rs @@ -1254,7 +1254,9 @@ pub struct CoreTimeOffsetStatus { } /// Account updates and transient notifications delivered from one core's feed. +// `CoreConfig` is delivered by value on the feed. Boxing it allocates on every such message. #[derive(Debug, Clone)] +#[allow(clippy::large_enum_variant)] pub enum FeedMsg { /// Already deduplicated actual trade edges; independent of order-table publication. TradeSounds(Vec), diff --git a/crates/moon-core/src/market/candles.rs b/crates/moon-core/src/market/candles.rs index e25fcf7f3..018b5bfe4 100644 --- a/crates/moon-core/src/market/candles.rs +++ b/crates/moon-core/src/market/candles.rs @@ -1321,9 +1321,7 @@ impl CandleSeries { // Buckets from here on are ours to accumulate into; earlier ones keep a base candle whose // volume is already complete. See `push_trades`. self.live_from = local - .iter() - .skip(usize::from(skip_partial_first)) - .next() + .get(usize::from(skip_partial_first)) .map_or(f64::INFINITY, |c| c.t_open_ms); local.clear(); diff --git a/crates/moon-core/src/market/kline_cache.rs b/crates/moon-core/src/market/kline_cache.rs index 5e1b20c18..53df65e79 100644 --- a/crates/moon-core/src/market/kline_cache.rs +++ b/crates/moon-core/src/market/kline_cache.rs @@ -697,7 +697,7 @@ fn upsert_one( } } for r in day_rows { - merged.insert((r.t_open_ms as i64 - day_start) as u32, r.clone()); + merged.insert((r.t_open_ms as i64 - day_start) as u32, *r); } let blob = pack_rows_v2(merged.values(), day_start); conn.execute( diff --git a/crates/moon-core/src/market/kline_cache/tests.rs b/crates/moon-core/src/market/kline_cache/tests.rs index bcdb63a4c..60b32bc82 100644 --- a/crates/moon-core/src/market/kline_cache/tests.rs +++ b/crates/moon-core/src/market/kline_cache/tests.rs @@ -202,7 +202,7 @@ fn retention_removes_expired_rows_from_both_cache_tables() { #[test] fn pack_unpack_roundtrip() { let day_start = 19_000i64 * DAY_MS; - let rows = vec![ + let rows = [ candle(day_start as f64, 5.0), candle((day_start + 60_000) as f64, 6.0), ]; diff --git a/crates/moon-core/src/market/source/history.rs b/crates/moon-core/src/market/source/history.rs index 9c195691e..5e7537090 100644 --- a/crates/moon-core/src/market/source/history.rs +++ b/crates/moon-core/src/market/source/history.rs @@ -446,7 +446,7 @@ impl MarketDataSource { let zone_unbounded = candle_params.is_some_and(|cp| { cp.trades_from_rel_ms == crate::market::candles::TRADES_FROM_UNBOUNDED }); - let display_trades = candle_params.map_or(true, |cp| cp.trades_from_rel_ms.is_finite()); + let display_trades = candle_params.is_none_or(|cp| cp.trades_from_rel_ms.is_finite()); let trades_from_rel = if zone_unbounded { crate::market::candles::TRADES_FROM_UNBOUNDED } else { @@ -718,15 +718,14 @@ impl MarketDataSource { subscribed: false, }, ); - if !entry.subscribed || entry.kind_min != deep_kind_min { - if client + if (!entry.subscribed || entry.kind_min != deep_kind_min) + && client .streams() .subscribe_candles([market], deep_kind) .is_ok() - { - entry.subscribed = true; - entry.kind_min = deep_kind_min; - } + { + entry.subscribed = true; + entry.kind_min = deep_kind_min; } entry.last_want = now_i; } @@ -783,7 +782,7 @@ impl MarketDataSource { let native_kind = deep_history_kind(native_kind_min); let have_native = snapshot .tf_candles(market, native_kind) - .map_or(false, |r| !r.is_empty()); + .is_some_and(|r| !r.is_empty()); let cache_covers = cursor.cache_rows_kind == native_kind_min && cursor.cache_rows.len() >= 30; if !have_native && !cache_covers { @@ -865,7 +864,7 @@ impl MarketDataSource { crate::market::candles::bucket_open_ms(now_unix_ms, base_tf_native_ms); let deep_stale = rows .and_then(|r| r.last()) - .map_or(true, |r| (r.unix_millis() as f64) < cur_bucket_ms); + .is_none_or(|r| (r.unix_millis() as f64) < cur_bucket_ms); let kind_changed = cursor.last_deep_kind != Some(deep_kind); let retry_delay = Duration::from_secs(cursor.deep_retry_delay_s.max(HISTORY_RETRY_MIN_S) as u64); @@ -873,7 +872,7 @@ impl MarketDataSource { && (kind_changed || cursor .last_deep_request - .map_or(true, |t| t.elapsed() > retry_delay)) + .is_none_or(|t| t.elapsed() > retry_delay)) { cursor.last_deep_request = Some(Instant::now()); cursor.last_deep_kind = Some(deep_kind); @@ -1069,19 +1068,18 @@ impl MarketDataSource { times.last().copied().unwrap_or(i64::MIN); cursor.cache_written_len = times.len(); } - (Some(_), None) => { + (Some(_), None) // The provider exchange identity is unavailable, so the cache cannot // address these rows. Make this visible in the log once per panel. Keep // the real signature unchanged, but use the initial marker to suppress // repeated logging. - if cursor.cache_written_sig == 0 { + if cursor.cache_written_sig == 0 => { cursor.cache_written_sig = 1; log::warn!( "kline cache: провайдер {provider} без ExchangeId — \ ряды {market} не кэшируются" ); } - } _ => {} } } @@ -1236,10 +1234,10 @@ impl MarketDataSource { .unwrap_or(0.0); let warn_due = cursor .last_gap_diag - .map_or(true, |t| t.elapsed() > Duration::from_secs(30)); + .is_none_or(|t| t.elapsed() > Duration::from_secs(30)); let scan_due = cursor .last_gap_scan - .map_or(true, |t| t.elapsed() > Duration::from_secs(30)); + .is_none_or(|t| t.elapsed() > Duration::from_secs(30)); // Detect gaps inside the sequence where the next candle begins after the previous // one ends. This is where the scroll-to-history then return-to-live gap was hidden. let mut max_hole = 0.0f64; @@ -1546,7 +1544,7 @@ pub(crate) fn deep_writeback_start( if !same_key || since_full >= DEEP_FULL_WRITEBACK_EVERY || written_last == i64::MIN - || times.first().map_or(true, |f| *f < written_first) + || times.first().is_none_or(|f| *f < written_first) || times.len() < written_len { 0 @@ -1679,9 +1677,9 @@ pub(crate) fn poll_cache_prefix( cursor.cache_want_from = Some(want_from); // A read that did not happen must not be remembered as a completed one; retry it, but no more // often than every `CACHE_RETRY_MS` so a busy worker is not asked again on every frame. - let retry_due = cursor.cache_retry_at.map_or(true, |t| { - t.elapsed() >= Duration::from_millis(CACHE_RETRY_MS) - }); + let retry_due = cursor + .cache_retry_at + .is_none_or(|t| t.elapsed() >= Duration::from_millis(CACHE_RETRY_MS)); if cursor.cache_pending.is_some() || !retry_due { return; } diff --git a/crates/moon-core/src/market/source/history/tests.rs b/crates/moon-core/src/market/source/history/tests.rs index 93e828333..5eca03a06 100644 --- a/crates/moon-core/src/market/source/history/tests.rs +++ b/crates/moon-core/src/market/source/history/tests.rs @@ -247,9 +247,11 @@ fn visible_fit_windowed_coarse_scan_equals_a_full_scan() { .filter(|(_, tf)| f64::from(*tf) == hour) .map(|(c, _)| c.t_open_ms) .collect(); - let mut cursor = ChartHistoryCursor::default(); - cursor.coarse_fill = fill.clone(); - cursor.coarse_fill_max_tf = Some(hour); + let cursor = ChartHistoryCursor { + coarse_fill: fill.clone(), + coarse_fill_max_tf: Some(hour), + ..ChartHistoryCursor::default() + }; let last = t; let mut overhang_hits = 0; for q in 0..3_000 { diff --git a/crates/moon-core/src/market/source/read.rs b/crates/moon-core/src/market/source/read.rs index 7df82fae3..6ca57d8fc 100644 --- a/crates/moon-core/src/market/source/read.rs +++ b/crates/moon-core/src/market/source/read.rs @@ -662,7 +662,7 @@ impl MarketDataSource { out.mark = m .price .mark_price_found - .then(|| m.price.mark_price) + .then_some(m.price.mark_price) .and_then(positive); out.price_step = positive(m.price.chart_price_step); out.vol_24h = positive(m.volume); diff --git a/crates/moon-core/src/market/trade_replay/rest/okx/tests.rs b/crates/moon-core/src/market/trade_replay/rest/okx/tests.rs index ce261092f..86ddc72c0 100644 --- a/crates/moon-core/src/market/trade_replay/rest/okx/tests.rs +++ b/crates/moon-core/src/market/trade_replay/rest/okx/tests.rs @@ -66,7 +66,7 @@ fn okx_uses_the_market_specific_base_volume_cell() { let spot = parse_klines(&fixture("spot"), SPOT_VOLUME_CELL).expect("spot fixture parses"); let swap = parse_klines(&fixture("swap"), SWAP_VOLUME_CELL).expect("swap fixture parses"); - assert!((spot[0].volume - 10.030_456_94).abs() < 0.000_01); + assert!((spot[0].volume - 10.030_457).abs() < 0.000_01); assert!((swap[0].volume - 144.84).abs() < 0.001); assert!( swap[0].volume < 1_000.0, diff --git a/crates/moon-core/src/market/trade_replay/worker.rs b/crates/moon-core/src/market/trade_replay/worker.rs index 635fc89b3..234d06565 100644 --- a/crates/moon-core/src/market/trade_replay/worker.rs +++ b/crates/moon-core/src/market/trade_replay/worker.rs @@ -133,7 +133,9 @@ const TRADE_PAGE_BUDGET: usize = 240; /// An authoritative EMPTY is an answer too, and a valuable one: a delisted or halted market /// answers empty every time, so refetching it on each reopen spends the host's budget to learn /// something already known. +// `Ready` owns the series a reopen draws. Boxing it allocates on every remembered answer. #[derive(Clone, Debug)] +#[allow(clippy::large_enum_variant)] enum Remembered { /// Rows to draw. Ready { diff --git a/crates/moon-core/src/strat_db/stats.rs b/crates/moon-core/src/strat_db/stats.rs index 54638621f..af8208ba2 100644 --- a/crates/moon-core/src/strat_db/stats.rs +++ b/crates/moon-core/src/strat_db/stats.rs @@ -417,7 +417,7 @@ pub fn deleted_heads() -> Vec { )) else { return Vec::new(); }; - stmt.query_map([], |r| head_from_row(r)) + stmt.query_map([], head_from_row) .map(|rows| rows.flatten().collect()) .unwrap_or_default() } @@ -446,7 +446,7 @@ pub fn head_row(core_uid: u64, strategy_id: i64) -> Option { conn.query_row( &format!("SELECT {HEAD_COLS} FROM strategies WHERE core_uid=?1 AND strategy_id=?2"), rusqlite::params![core_uid as i64, strategy_id], - |r| head_from_row(r), + head_from_row, ) .optional() .ok() diff --git a/crates/moon-core/src/telegram/init_data.rs b/crates/moon-core/src/telegram/init_data.rs index fe926a8e2..9fe4e2b95 100644 --- a/crates/moon-core/src/telegram/init_data.rs +++ b/crates/moon-core/src/telegram/init_data.rs @@ -241,7 +241,7 @@ pub fn authorize_paired_identity( authorized_chat_ids: &[i64], ) -> Result { let chat_id = signed.pairing_chat_id(); - if authorized_chat_ids.iter().any(|id| *id == chat_id) { + if authorized_chat_ids.contains(&chat_id) { Ok(chat_id) } else { Err(InitDataError::Unpaired) diff --git a/crates/moon-core/src/telegram/report/tests.rs b/crates/moon-core/src/telegram/report/tests.rs index ad578e947..c1c232d76 100644 --- a/crates/moon-core/src/telegram/report/tests.rs +++ b/crates/moon-core/src/telegram/report/tests.rs @@ -25,11 +25,10 @@ fn callback_exchange_list_flag_defaults_to_collapsed() { let mut request = ReportRequest::new(Period::Today, false); assert!(!request.exchanges_open); assert_eq!(request.callback(), "r:e:t:0"); - assert_eq!( - ReportRequest::parse_callback("r:e:t:0") + assert!( + !ReportRequest::parse_callback("r:e:t:0") .unwrap() - .exchanges_open, - false + .exchanges_open ); request.exchanges_open = true; assert_eq!(request.callback(), "r:ek:t:0"); diff --git a/crates/moon-core/src/telegram/web.rs b/crates/moon-core/src/telegram/web.rs index 437b8e5de..907fb85a3 100644 --- a/crates/moon-core/src/telegram/web.rs +++ b/crates/moon-core/src/telegram/web.rs @@ -254,7 +254,7 @@ impl App { ) && request.uri().path().starts_with("/api/") { status_response(StatusCode::NOT_FOUND, "not_found") - } else if request.method() != &Method::GET { + } else if request.method() != Method::GET { status_response(StatusCode::METHOD_NOT_ALLOWED, "method") } else { status_response(StatusCode::NOT_FOUND, "not_found") @@ -297,6 +297,8 @@ impl App { } /// Validate initData HMAC and paired identity. Body is not consumed. + // Failures are HTTP responses. Boxing `Response` would change every `?` on this path. + #[allow(clippy::result_large_err)] fn authenticate( &self, request: &Request, @@ -360,6 +362,8 @@ fn check_peer_and_headers(headers: &HeaderMap, host: Option<&str>) -> Result<(), } /// Require `application/json` (optional charset) on API POSTs. +// The error is the HTTP response itself. Boxing it changes the helper's `Result`. +#[allow(clippy::result_large_err)] fn require_json_content_type(headers: &HeaderMap) -> Result<(), Response> { let value = headers .get("content-type") @@ -376,6 +380,8 @@ fn require_json_content_type(headers: &HeaderMap) -> Result<(), Response> } /// Read a JSON object body capped at [`MAX_BODY_BYTES`]. +// The error is the HTTP response itself. Boxing it changes the helper's `Result`. +#[allow(clippy::result_large_err)] fn read_limited_json_object(request: Request) -> Result> { if let Some(len) = request.body().len() { if len > MAX_BODY_BYTES { diff --git a/crates/moon-core/src/update/discovery.rs b/crates/moon-core/src/update/discovery.rs index fdef0eebb..310d1e778 100644 --- a/crates/moon-core/src/update/discovery.rs +++ b/crates/moon-core/src/update/discovery.rs @@ -195,14 +195,12 @@ impl ReleaseDiscovery { let page_two_due = staged[1].is_none() || first_changed || now_unix >= self.page_two_refresh_at; - if page_two_due && first_low { - if first_changed || staged[1].is_none() { - return Err(DiscoveryError::new( - DiscoveryRetry::RateLimited, - defer_until, - anyhow!("GitHub release scan deferred before its required second page"), - )); - } + if page_two_due && first_low && (first_changed || staged[1].is_none()) { + return Err(DiscoveryError::new( + DiscoveryRetry::RateLimited, + defer_until, + anyhow!("GitHub release scan deferred before its required second page"), + )); } let mut next_page_two_refresh_at = self.page_two_refresh_at; if page_two_due && !first_low { diff --git a/crates/moon-core/tests/ci_gate_contract.rs b/crates/moon-core/tests/ci_gate_contract.rs index 2e69ead2c..f5274544f 100644 --- a/crates/moon-core/tests/ci_gate_contract.rs +++ b/crates/moon-core/tests/ci_gate_contract.rs @@ -40,18 +40,18 @@ const TEST_JOB: &str = "tests"; /// Every job that compiles the workspace and therefore runs the lockfile contract's three steps /// (verify -> refresh MoonUI -> assert nothing else moved). `audit` is deliberately excluded: it /// runs cargo-deny over the already-committed lock and never touches it. -const COMPILING_JOBS: [&str; 3] = ["windows", TEST_JOB, "macos-probe"]; +const COMPILING_JOBS: [&str; 4] = ["windows", TEST_JOB, "macos-probe", "clippy"]; -/// The two compiling jobs that BLOCK a merge — the Windows `.exe` release gate and the `tests` -/// gate — and so must neither be silently skipped nor downgraded to a probe. `macos-probe` is -/// deliberately excluded here even though it shares the same `if:` condition: it already carries +/// The compiling jobs that BLOCK a merge — the Windows `.exe` release gate, the `tests` gate, and +/// the `clippy` gate — and so must neither be silently skipped nor downgraded to a probe. +/// `macos-probe` is deliberately excluded here even though it shares the same `if:` condition: it already carries /// `continue-on-error: true` and never blocks, so there is no gate left for a quiet skip to hide /// behind, and it needs no guard of its own. -const GATING_JOBS: [&str; 2] = ["windows", TEST_JOB]; +const GATING_JOBS: [&str; 3] = ["windows", TEST_JOB, "clippy"]; /// The only job-level `if:` a `GATING_JOBS` member may carry, normalized the same way /// [`normalize_if_condition`] normalizes what it reads from `build.yml`. The weekly `schedule:` -/// trigger (`build.yml`'s `on:` header) exists for the `audit` job alone, so the two gates skip +/// trigger (`build.yml`'s `on:` header) exists for the `audit` job alone, so these gates skip /// it and nothing else. const EXPECTED_SCHEDULE_EXCLUSION: &str = "github.event_name != 'schedule'"; @@ -88,6 +88,9 @@ const AUDIT_JOB: &str = "audit"; /// The formatting gate's job key, separate from compilation and test gates. const FMT_JOB: &str = "fmt"; +/// The clippy gate's job key. It compiles, so it is also in [`COMPILING_JOBS`] and [`GATING_JOBS`]. +const CLIPPY_JOB: &str = "clippy"; + fn workspace_dir() -> PathBuf { PathBuf::from(env!("CARGO_MANIFEST_DIR")) .parent() @@ -482,6 +485,58 @@ fn the_fmt_job_is_a_gate_that_actually_runs_cargo_fmt() { ); } +/// Breakage guarded: changing `.github/workflows/build.yml`'s `clippy` job command away from +/// `cargo clippy --workspace --all-targets --locked -- -D warnings`, dropping `--locked` or +/// `-D warnings`, or demoting the job with `continue-on-error: true`. A warning would then merge +/// while the check still looked green. +#[test] +fn the_clippy_job_is_a_gate_that_actually_runs_cargo_clippy() { + let text = workflow_text(); + let body = job_body(&text, CLIPPY_JOB) + .unwrap_or_else(|| panic!("build.yml must keep a `{CLIPPY_JOB}:` lint job")); + + for line in &body { + let Some(value) = line.trim().strip_prefix("continue-on-error:") else { + continue; + }; + assert_eq!( + value.trim(), + "false", + "job `{CLIPPY_JOB}` runs the lint gate, so a failure must block: `{}`", + line.trim() + ); + } + + let skipped = body.iter().find(|line| line.starts_with(" if:")); + let line = skipped.unwrap_or_else(|| { + panic!("job `{CLIPPY_JOB}` compiles, so it must skip the audit-only weekly schedule") + }); + assert_eq!( + normalize_if_condition(line), + EXPECTED_SCHEDULE_EXCLUSION, + "job `{CLIPPY_JOB}` must run on push, pull_request, and workflow_dispatch: `{}`", + line.trim() + ); + + let command = body + .iter() + .filter_map(|line| line.trim().strip_prefix("run:")) + .map(str::trim) + .find(|command| command.contains("cargo clippy")) + .unwrap_or_else(|| panic!("job `{CLIPPY_JOB}` must run cargo clippy")); + assert_eq!( + command, "cargo clippy --workspace --all-targets --locked -- -D warnings", + "job `{CLIPPY_JOB}` must deny warnings on the whole workspace: `{command}`" + ); + for sep in [";", "&&", "||", "|"] { + assert!( + !command.contains(sep), + "job `{CLIPPY_JOB}` must not chain anything onto clippy — `{sep}` can swallow a \ + failure: `{command}`" + ); + } +} + /// Breakage guarded: a well-meaning reorder in `.github/workflows/build.yml`'s lockfile contract /// (verify -> refresh MoonUI -> assert nothing else moved), in any compiling job. `cargo fetch /// --locked` only proves the committed lock agrees with the manifests when it runs BEFORE diff --git a/crates/moon-ui-gpui/Cargo.toml b/crates/moon-ui-gpui/Cargo.toml index e377fa020..ccc0154fa 100644 --- a/crates/moon-ui-gpui/Cargo.toml +++ b/crates/moon-ui-gpui/Cargo.toml @@ -167,3 +167,6 @@ core-graphics = "0.24" # test build only and never the shipped binary. MoonUI's own `moon-ui-components` declares it the # same way. gpui = { package = "moon-gpui", git = "https://github.com/Moonbot-Tech/MoonUI", branch = "master", features = ["test-support"] } + +[lints] +workspace = true diff --git a/crates/moon-ui-gpui/build.rs b/crates/moon-ui-gpui/build.rs index b6f4c185b..e503a3496 100644 --- a/crates/moon-ui-gpui/build.rs +++ b/crates/moon-ui-gpui/build.rs @@ -69,15 +69,14 @@ fn embed_group_icons() -> std::io::Result<()> { let mut ids: Vec = Vec::new(); for entry in std::fs::read_dir(dir)? { let path = entry?.path(); - if path.extension().and_then(|s| s.to_str()) == Some("png") { - if let Some(id) = path + if path.extension().and_then(|s| s.to_str()) == Some("png") + && let Some(id) = path .file_stem() .and_then(|s| s.to_str()) .and_then(|s| s.parse::().ok()) - { - ids.push(id); - max_id = max_id.max(id); - } + { + ids.push(id); + max_id = max_id.max(id); } } let mut present = vec![false; max_id + 1]; diff --git a/crates/moon-ui-gpui/src/analytics/bg.rs b/crates/moon-ui-gpui/src/analytics/bg.rs index 8774b3eeb..e60f36137 100644 --- a/crates/moon-ui-gpui/src/analytics/bg.rs +++ b/crates/moon-ui-gpui/src/analytics/bg.rs @@ -203,7 +203,7 @@ impl AnalyticsView { } cx.spawn(async move |this, cx| { let result = cx.background_spawn(async move { db() }).await; - let _ = cx.update(|cx| { + cx.update(|cx| { let _ = this.update(cx, |this, cx| { this.db_ops = this.db_ops.saturating_sub(1); store(this, result, cx); @@ -243,7 +243,7 @@ impl AnalyticsView { let result = cx .background_spawn(async move { with_read_cancellation(worker_cancellation, db) }) .await; - let _ = cx.update(|cx| { + cx.update(|cx| { let _ = this.update(cx, |this, cx| { this.latest_reads.finish(&cancellation); this.db_ops = this.db_ops.saturating_sub(1); diff --git a/crates/moon-ui-gpui/src/analytics/bg/tests.rs b/crates/moon-ui-gpui/src/analytics/bg/tests.rs index 5b4c70d86..d2fa3c622 100644 --- a/crates/moon-ui-gpui/src/analytics/bg/tests.rs +++ b/crates/moon-ui-gpui/src/analytics/bg/tests.rs @@ -62,8 +62,8 @@ fn stale_completion_preserves_newer_lane_owner() { fn dropping_the_registry_cancels_detached_requests() { let token = { let mut reads = LatestReads::default(); - let token = reads.replace(&[ReadLane::Summary]); - token + + reads.replace(&[ReadLane::Summary]) }; assert!(token.is_cancelled()); diff --git a/crates/moon-ui-gpui/src/analytics/calendar/day.rs b/crates/moon-ui-gpui/src/analytics/calendar/day.rs index b2fd9a7c9..3cb914bae 100644 --- a/crates/moon-ui-gpui/src/analytics/calendar/day.rs +++ b/crates/moon-ui-gpui/src/analytics/calendar/day.rs @@ -193,12 +193,11 @@ impl AnalyticsView { for h in 0..24usize { if let Some(c) = hour_start(d, h as u32, self.bound_zone()).and_then(|start| map.get(&start)) + && c.has_activity() { - if c.has_activity() { - hour_max = hour_max.max(c.totals.profit.abs()); - hour_sum[h] += c.totals.profit; - hour_cnt[h] += 1; - } + hour_max = hour_max.max(c.totals.profit.abs()); + hour_sum[h] += c.totals.profit; + hour_cnt[h] += 1; } } let next = next_day(d, self.bound_zone()); diff --git a/crates/moon-ui-gpui/src/analytics/calendar/mod.rs b/crates/moon-ui-gpui/src/analytics/calendar/mod.rs index a40ad13e1..159f403b6 100644 --- a/crates/moon-ui-gpui/src/analytics/calendar/mod.rs +++ b/crates/moon-ui-gpui/src/analytics/calendar/mod.rs @@ -9,6 +9,7 @@ //! row on top (with the delta to the previous month), a plus/minus-day bar //! below. Clicking a day switches to "Day" mode; //! - "Day" — hour-by-hour detail of a single day. See `day`. +//! //! Each mode builds its own query (`cal_query` in mod.rs); the window's period //! bar is hidden here. @@ -480,14 +481,14 @@ impl AnalyticsView { .into_any_element(); } }; - if self.cal_mode == CalMode::Month { - if let Err(note) = self.cal_prev.view(|_| false) { - return v_flex() - .size_full() - .child(self.cal_nav(p, cx)) - .child(note_el("analytics-calendar-prev-read", note, 18.0, p, cx)) - .into_any_element(); - } + if self.cal_mode == CalMode::Month + && let Err(note) = self.cal_prev.view(|_| false) + { + return v_flex() + .size_full() + .child(self.cal_nav(p, cx)) + .child(note_el("analytics-calendar-prev-read", note, 18.0, p, cx)) + .into_any_element(); }; // Frame: the nav bar (like Summary's period bar) + the mode's content. let content = match self.cal_mode { diff --git a/crates/moon-ui-gpui/src/analytics/mod.rs b/crates/moon-ui-gpui/src/analytics/mod.rs index c4ac9c131..7f28e1cbd 100644 --- a/crates/moon-ui-gpui/src/analytics/mod.rs +++ b/crates/moon-ui-gpui/src/analytics/mod.rs @@ -1353,7 +1353,7 @@ impl AnalyticsView { cx.spawn(async move |this, cx| { let executor = cx.update(|cx| cx.background_executor().clone()); executor.timer(wait).await; - let _ = cx.update(|cx| { + cx.update(|cx| { let _ = this.update(cx, |this, cx| { this.report_refresh.timer_fired(); this.schedule_report_refresh(cx); @@ -1446,7 +1446,7 @@ impl AnalyticsView { cx.spawn(async move |this, cx| { let executor = cx.update(|cx| cx.background_executor().clone()); executor.timer(wait).await; - let _ = cx.update(|cx| { + cx.update(|cx| { let _ = this.update(cx, |this, cx| { this.core_refresh_timer_armed = false; if !this.core_refresh_needed { @@ -1617,7 +1617,7 @@ impl AnalyticsView { cx.spawn(async move |this, cx| { let executor = cx.update(|cx| cx.background_executor().clone()); executor.timer(BUSY_OVERLAY_DELAY).await; - let _ = cx.update(|cx| { + cx.update(|cx| { let _ = this.update(cx, |this, cx| { // Detached timers outlive their batches, so match the captured start time // before repainting; a later batch has its own delay and timer. @@ -2344,13 +2344,12 @@ pub fn open( owner_display: Option, cx: &mut App, ) { - if let Some(handle) = backend.read(cx).analytics_window { - if handle + if let Some(handle) = backend.read(cx).analytics_window + && handle .update(cx, |_, window, _| window.activate_window()) .is_ok() - { - return; - } + { + return; } let saved = backend.read(cx).layout.analytics_window; let bounds = saved.map_or( diff --git a/crates/moon-ui-gpui/src/analytics/profit_monitor/mod.rs b/crates/moon-ui-gpui/src/analytics/profit_monitor/mod.rs index 1cf647cb5..d8aaff7e9 100644 --- a/crates/moon-ui-gpui/src/analytics/profit_monitor/mod.rs +++ b/crates/moon-ui-gpui/src/analytics/profit_monitor/mod.rs @@ -628,7 +628,7 @@ impl ProfitMonitorView { cx.spawn(async move |this, cx| { let executor = cx.update(|cx| cx.background_executor().clone()); executor.timer(wait).await; - let _ = cx.update(|cx| { + cx.update(|cx| { let _ = this.update(cx, |this, cx| { if this.clock_timer_generation != generation { return; @@ -751,7 +751,7 @@ impl ProfitMonitorView { moon_core::db::analytics::profit_monitor_snapshot(&query) }) .await; - let _ = cx.update(|cx| { + cx.update(|cx| { let _ = this.update(cx, |this, cx| { this.db_active = false; if this.seq == request { @@ -842,7 +842,7 @@ impl ProfitMonitorView { cx.spawn(async move |this, cx| { let executor = cx.update(|cx| cx.background_executor().clone()); executor.timer(wait).await; - let _ = cx.update(|cx| { + cx.update(|cx| { let _ = this.update(cx, |this, cx| { this.refresh.timer_fired(); this.schedule_refresh(cx); diff --git a/crates/moon-ui-gpui/src/analytics/profit_monitor/table.rs b/crates/moon-ui-gpui/src/analytics/profit_monitor/table.rs index 2c35a6446..a95805d44 100644 --- a/crates/moon-ui-gpui/src/analytics/profit_monitor/table.rs +++ b/crates/moon-ui-gpui/src/analytics/profit_monitor/table.rs @@ -555,6 +555,8 @@ pub(super) fn table( palette, cx, ); + // The virtual-list closure shares this UI-thread list. The entry type is not `Send`. + #[allow(clippy::arc_with_non_send_sync)] let entries = Arc::new(entries); let row_count = entries.len(); let list_entries = entries.clone(); diff --git a/crates/moon-ui-gpui/src/analytics/profit_monitor/tests.rs b/crates/moon-ui-gpui/src/analytics/profit_monitor/tests.rs index 414d131b7..1cb79407a 100644 --- a/crates/moon-ui-gpui/src/analytics/profit_monitor/tests.rs +++ b/crates/moon-ui-gpui/src/analytics/profit_monitor/tests.rs @@ -275,6 +275,8 @@ fn retired_bot_preference_falls_back_to_core() { /// scale multiplier, or shifting a threshold makes the budget or one adjacent pair red and blocks /// or clips the narrow window. #[test] +// The compared values are named constants. The assertion reddens when a layout constant stops fitting. +#[allow(clippy::assertions_on_constants)] fn responsive_layout_degrades_at_the_documented_boundaries() { assert!( super::MIN_WINDOW_WIDTH diff --git a/crates/moon-ui-gpui/src/analytics/render.rs b/crates/moon-ui-gpui/src/analytics/render.rs index 1f5c0ba15..3c90733e1 100644 --- a/crates/moon-ui-gpui/src/analytics/render.rs +++ b/crates/moon-ui-gpui/src/analytics/render.rs @@ -252,14 +252,14 @@ fn quote_split_note( if coverage.unavailable_orders > 0 { text.push_str(" · "); text.push_str( - &t!( + t!( mode.key( "analytics.quote_valuation_unavailable", "report.valuation_current_unavailable" ), n = coverage.unavailable_orders ) - .to_string(), + .as_ref(), ); } // Without this the ratio above simply stops moving, which reads as a slow backfill @@ -268,13 +268,13 @@ fn quote_split_note( if let Some(facts) = stalled { text.push_str(" · "); text.push_str( - &t!( + t!( "analytics.quote_valuation_stalled", stage = facts.stage, kind = facts.kind, minutes = facts.minutes ) - .to_string(), + .as_ref(), ); } text diff --git a/crates/moon-ui-gpui/src/analytics/summary/charts.rs b/crates/moon-ui-gpui/src/analytics/summary/charts.rs index 95cb1d3cb..a5090d2cd 100644 --- a/crates/moon-ui-gpui/src/analytics/summary/charts.rs +++ b/crates/moon-ui-gpui/src/analytics/summary/charts.rs @@ -364,11 +364,11 @@ pub(super) fn distinct_core_colors( let mut out = vec![None; configured.len()]; let mut taken: Vec = Vec::with_capacity(configured.len()); for &i in &order { - if let Some(c) = configured[i].1.map(rgb) { - if !taken.iter().any(|&t| too_close(t, c)) { - taken.push(c); - out[i] = Some(c); - } + if let Some(c) = configured[i].1.map(rgb) + && !taken.iter().any(|&t| too_close(t, c)) + { + taken.push(c); + out[i] = Some(c); } } // Mid-shade first (index 2 of `SHADES`, the saturated one the picker leads with), then the diff --git a/crates/moon-ui-gpui/src/analytics/summary/cumulative.rs b/crates/moon-ui-gpui/src/analytics/summary/cumulative.rs index bc270bc48..f613b759a 100644 --- a/crates/moon-ui-gpui/src/analytics/summary/cumulative.rs +++ b/crates/moon-ui-gpui/src/analytics/summary/cumulative.rs @@ -91,7 +91,7 @@ pub(super) fn core_legend( let text_w = |s: &str| design::mono_caption_text_width(cx, s, 400.0); // Always keep room for the tail: a row that packs to the last pixel and then discovers it // must say "+3 more" has nowhere to put it. - let tail_w = text_w(&t!("analytics.popup_more", n = order.len()).to_string()) + gap; + let tail_w = text_w(t!("analytics.popup_more", n = order.len()).as_ref()) + gap; let mut used = 0.0; let mut shown = 0usize; for &ci in &order { diff --git a/crates/moon-ui-gpui/src/analytics/summary/mod.rs b/crates/moon-ui-gpui/src/analytics/summary/mod.rs index 0f713f549..41b9d6a73 100644 --- a/crates/moon-ui-gpui/src/analytics/summary/mod.rs +++ b/crates/moon-ui-gpui/src/analytics/summary/mod.rs @@ -447,7 +447,7 @@ impl AnalyticsView { /// Row of KPI tiles with deltas against the previous period. fn kpi_row(&self, d: &Summary, p: MoonPalette, cx: &Context) -> impl IntoElement { let (cur, prev) = (&d.cur, &d.prev); - let profit_el = colored_value(p, cur.profit, format!("{}", fmt_signed(cur.profit))); + let profit_el = colored_value(p, cur.profit, fmt_signed(cur.profit).to_string()); let dd_el = div() .text_color(moon(p.orange)) .child(format!( diff --git a/crates/moon-ui-gpui/src/analytics/toolbar.rs b/crates/moon-ui-gpui/src/analytics/toolbar.rs index 363a8eaf1..6d3fc981c 100644 --- a/crates/moon-ui-gpui/src/analytics/toolbar.rs +++ b/crates/moon-ui-gpui/src/analytics/toolbar.rs @@ -739,7 +739,7 @@ impl AnalyticsView { crate::controls::core_combo( "an-core", &cores, - &venues, + venues, &selected, crate::controls::CoreAllRowMode::ImplicitOnly, all_label, @@ -915,7 +915,7 @@ impl AnalyticsView { cx.spawn(async move |this, cx| { let executor = cx.update(|cx| cx.background_executor().clone()); executor.timer(wait).await; - let _ = cx.update(|cx| { + cx.update(|cx| { let _ = this.update(cx, |this, cx| { this.integrity_poll_armed = false; cx.notify(); diff --git a/crates/moon-ui-gpui/src/analytics/tuner/coins/load.rs b/crates/moon-ui-gpui/src/analytics/tuner/coins/load.rs index 4b54c1f29..5bd753c68 100644 --- a/crates/moon-ui-gpui/src/analytics/tuner/coins/load.rs +++ b/crates/moon-ui-gpui/src/analytics/tuner/coins/load.rs @@ -308,7 +308,7 @@ impl AnalyticsView { self.coins.kpi_task = Some(cx.spawn(async move |this, cx| { let executor = cx.update(|cx| cx.background_executor().clone()); executor.timer(KPI_DEBOUNCE).await; - let _ = cx.update(|cx| { + cx.update(|cx| { let _ = this.update(cx, |this, cx| { // A later edit landed during the wait — that one owns the recompute. if this.coins.kpi_seq == req { diff --git a/crates/moon-ui-gpui/src/analytics/tuner/coins/save.rs b/crates/moon-ui-gpui/src/analytics/tuner/coins/save.rs index 06f6bcfe6..a20b04c37 100644 --- a/crates/moon-ui-gpui/src/analytics/tuner/coins/save.rs +++ b/crates/moon-ui-gpui/src/analytics/tuner/coins/save.rs @@ -169,7 +169,7 @@ impl AnalyticsView { (targets, live, unresolved) }) .await; - let _ = cx.update(|cx| { + cx.update(|cx| { let _ = this.update(cx, |this, cx| { 'completion: { let now: Vec<(i64, Option)> = this @@ -245,11 +245,9 @@ impl AnalyticsView { let empties = per_target.iter().any(|v| { v.iter().any(|(k, val)| k == key && val.trim().is_empty()) }); - if empties { - if let Some(Some(note)) = notes.get_mut(i) { - note.push_str(" · "); - note.push_str(&t!("analytics.tuner.save_clears")); - } + if empties && let Some(Some(note)) = notes.get_mut(i) { + note.push_str(" · "); + note.push_str(&t!("analytics.tuner.save_clears")); } } let mut warns = Vec::new(); diff --git a/crates/moon-ui-gpui/src/analytics/tuner/filter/actions.rs b/crates/moon-ui-gpui/src/analytics/tuner/filter/actions.rs index ff1396e61..c713b242e 100644 --- a/crates/moon-ui-gpui/src/analytics/tuner/filter/actions.rs +++ b/crates/moon-ui-gpui/src/analytics/tuner/filter/actions.rs @@ -223,13 +223,13 @@ impl AnalyticsView { ); let by_field: HashMap<&str, _> = res.fields.into_iter().map(|f| (f.field, f)).collect(); - for fi in 0..FIELDS.len() { + for (fi, field) in FIELDS.iter().enumerate() { // Leave fields that were not searched alone (fixed/disabled). if !this.tuner.enabled[fi] { continue; } let (from, to) = by_field - .get(FIELDS[fi].col) + .get(field.col) .map(|f| (fmt_bound(f.from), fmt_bound(f.to))) .unwrap_or_default(); this.tuner.bounds[0][fi] = (from, to); diff --git a/crates/moon-ui-gpui/src/analytics/tuner/filter/mod.rs b/crates/moon-ui-gpui/src/analytics/tuner/filter/mod.rs index ceb9a74a9..d145383b9 100644 --- a/crates/moon-ui-gpui/src/analytics/tuner/filter/mod.rs +++ b/crates/moon-ui-gpui/src/analytics/tuner/filter/mod.rs @@ -581,8 +581,8 @@ impl AnalyticsView { .unwrap_or_else(|| Arc::new(StratFilters::default())); let mut grid = v_flex().w_full().child(head); let mut last_class: Option = None; - for fi in 0..FIELDS.len() { - let class = FIELDS[fi].class; + for (fi, field) in FIELDS.iter().enumerate() { + let class = field.class; // Headers: the MoonBot section (the parent), then the indented // subgroup (BV/SV inside Volumes, the Δ2/Δ3 slots inside Deltas). let sub = class.parent() != class; diff --git a/crates/moon-ui-gpui/src/analytics/tuner/save.rs b/crates/moon-ui-gpui/src/analytics/tuner/save.rs index 58bbb4e9f..bc62fe11e 100644 --- a/crates/moon-ui-gpui/src/analytics/tuner/save.rs +++ b/crates/moon-ui-gpui/src/analytics/tuner/save.rs @@ -326,10 +326,10 @@ impl AnalyticsView { } // In Classic, a missing target core applies the copy to every core containing the // strategy; Auto rejects that legacy aggregate target before reaching this loop. - if let Some(tc) = target_core { - if cid != tc { - continue; - } + if let Some(tc) = target_core + && cid != tc + { + continue; } let Some(row) = cd.strategies.iter().find(|r| r.id == sid) else { continue; @@ -807,32 +807,32 @@ impl AnalyticsView { }; let mut list = v_flex().w_full().gap_0(); // Copy dialog: the editable name of the new strategy as the first row. - if dlg.copy { - if let Some(input) = self.tuner.inputs.get("copy-name") { - list = list.child( - h_flex() - .w_full() - .px(design::ui_px(cx, 10.0)) - .py(design::ui_px(cx, 5.0)) - .gap(design::ui_px(cx, 8.0)) - .items_center() - .child( - div() - .flex_none() - .font_family(design::ui_font()) - .text_size(design::t_caption(cx)) - .text_color(moon(p.text_muted)) - .child(t!("analytics.tuner.copy_name_lbl").to_string()), - ) - .child( - div().flex_1().min_w_0().child( - MoonInput::new("tun-copy-name") - .state(input) - .size(design::INPUT_SIZE), - ), + if dlg.copy + && let Some(input) = self.tuner.inputs.get("copy-name") + { + list = list.child( + h_flex() + .w_full() + .px(design::ui_px(cx, 10.0)) + .py(design::ui_px(cx, 5.0)) + .gap(design::ui_px(cx, 8.0)) + .items_center() + .child( + div() + .flex_none() + .font_family(design::ui_font()) + .text_size(design::t_caption(cx)) + .text_color(moon(p.text_muted)) + .child(t!("analytics.tuner.copy_name_lbl").to_string()), + ) + .child( + div().flex_1().min_w_0().child( + MoonInput::new("tun-copy-name") + .state(input) + .size(design::INPUT_SIZE), ), - ); - } + ), + ); } // Bulk: name the strategies that will be written — some may be filtered out of the list, // and the count-only title would otherwise let a hidden target be written unseen. diff --git a/crates/moon-ui-gpui/src/backend/core_warn.rs b/crates/moon-ui-gpui/src/backend/core_warn.rs index dbe2ca012..6248b98a4 100644 --- a/crates/moon-ui-gpui/src/backend/core_warn.rs +++ b/crates/moon-ui-gpui/src/backend/core_warn.rs @@ -455,6 +455,9 @@ struct CoreTrack { impl CoreTrack { /// Average `(process, system)` CPU over the last `CPU_WINDOW_SECS` seconds. + /// + /// A zero sample count is unknown CPU, not 0%. `checked_div` returns `None` + /// for that divisor, which is that unknown state. fn averaged(&self, now_sec: i64) -> (Option, Option) { let (mut proc, mut system, mut n) = (0u32, 0u32, 0u32); for bucket in &self.cpu { @@ -464,14 +467,10 @@ impl CoreTrack { n += bucket.samples; } } - if n == 0 { - (None, None) - } else { - ( - Some((proc / n).min(255) as u8), - Some((system / n).min(255) as u8), - ) - } + ( + proc.checked_div(n).map(|v| v.min(255) as u8), + system.checked_div(n).map(|v| v.min(255) as u8), + ) } } @@ -1156,11 +1155,7 @@ fn server_snapshot(samples: &[CoreSample], ip: IpAddr) -> WarnSnapshot { sys_cpu: freshest_u8(|sys| sys.system_cpu_percent) .unwrap_or(0) .min(100), - occ_mem: if total > 0 { - (used_sum * 100 / total).min(100) as u8 - } else { - 0 - }, + occ_mem: (used_sum * 100).checked_div(total).unwrap_or(0).min(100) as u8, free_mb: free.unwrap_or(0), used_mb: used_sum.min(u64::from(u32::MAX)) as u32, logical_cpus: freshest_u8(|sys| sys.logical_cpu_count).unwrap_or(0), @@ -1255,11 +1250,7 @@ fn build_ring_samples(samples: &[CoreSample]) -> Vec { // per-core lines stay consistent with it (total unavailable → 0). let free = freshest(&|sys| sys.free_physical_memory_mb).map(u64::from); let total = free.map(|free| used_sum + free).unwrap_or(0); - let occ = if total > 0 { - (used_sum * 100 / total).min(100) as u8 - } else { - 0 - }; + let occ = (used_sum * 100).checked_div(total).unwrap_or(0).min(100) as u8; out.push(RingSample { subject: RingSubject::Server(*ip), cpu: sys_cpu, @@ -1270,7 +1261,10 @@ fn build_ring_samples(samples: &[CoreSample]) -> Vec { for sample in cores { let proc_cpu = sample.sys.process_cpu_percent.unwrap_or(0).min(100); let proc_mem = match sample.sys.used_memory_mb { - Some(used) if total > 0 => (u64::from(used) * 100 / total).min(100) as u8, + Some(used) => (u64::from(used) * 100) + .checked_div(total) + .unwrap_or(0) + .min(100) as u8, _ => 0, }; // Per-core pings, but only while Ready — a dropped core keeps its last (possibly diff --git a/crates/moon-ui-gpui/src/backend/core_warn/tests.rs b/crates/moon-ui-gpui/src/backend/core_warn/tests.rs index 69ceb8039..a3273825b 100644 --- a/crates/moon-ui-gpui/src/backend/core_warn/tests.rs +++ b/crates/moon-ui-gpui/src/backend/core_warn/tests.rs @@ -123,6 +123,8 @@ fn sustained_cpu_opens_then_closes_one_episode() { // Drop CPU; the averaging window needs a few low seconds to fall under the threshold. let mut sec = CPU_SUSTAIN_SECS as i64 + 4; + // `sec` is the simulated clock, stepped beside each tick. Folding it into the header hides that. + #[allow(clippy::explicit_counter_loop)] for _ in 0..5 { tick_one(&mut engine, sec, 1, IP, Some(5), Some(500)); sec += 1; @@ -156,6 +158,8 @@ fn memory_growth_opens_then_closes_per_core_episode() { // Fall back to baseline: current returns to the window minimum, so growth is zero. let mut sec = rise.len() as i64 + 1; + // `sec` is the simulated clock, stepped beside each tick. Folding it into the header hides that. + #[allow(clippy::explicit_counter_loop)] for _ in 0..3 { tick_one(&mut engine, sec, 7, IP, Some(10), Some(400)); sec += 1; diff --git a/crates/moon-ui-gpui/src/backend/figures.rs b/crates/moon-ui-gpui/src/backend/figures.rs index 89dccc0b1..295b039e0 100644 --- a/crates/moon-ui-gpui/src/backend/figures.rs +++ b/crates/moon-ui-gpui/src/backend/figures.rs @@ -220,11 +220,11 @@ impl Backend { /// Sets the core's default Alerts-strategy ID. A changed value marks the configuration for the /// coordination loop's routine save; an unknown core or unchanged value is ignored. pub(crate) fn set_alert_def_strategy(&mut self, core: CoreId, strategy_id: u64) { - if let Some(s) = self.config.servers.iter_mut().find(|s| s.id == core) { - if s.default_alert_strategy != strategy_id { - s.default_alert_strategy = strategy_id; - self.config_dirty = true; - } + if let Some(s) = self.config.servers.iter_mut().find(|s| s.id == core) + && s.default_alert_strategy != strategy_id + { + s.default_alert_strategy = strategy_id; + self.config_dirty = true; } } @@ -344,12 +344,12 @@ impl Backend { /// is cleared even if the figure was already absent; command-send errors are ignored. pub(crate) fn remove_figure(&mut self, core: CoreId, market: &str, id: u64) { let removed = self.figures.borrow_mut().remove(core, market, id); - if let Some(fig) = removed { - if fig.alert { - let _ = self - .session - .chart_alert_delete(core, market.to_string(), id); - } + if let Some(fig) = removed + && fig.alert + { + let _ = self + .session + .chart_alert_delete(core, market.to_string(), id); } if self .fig_selected diff --git a/crates/moon-ui-gpui/src/backend/mod.rs b/crates/moon-ui-gpui/src/backend/mod.rs index 168ef6691..0e76f2d0f 100644 --- a/crates/moon-ui-gpui/src/backend/mod.rs +++ b/crates/moon-ui-gpui/src/backend/mod.rs @@ -420,7 +420,7 @@ impl Backend { &self, group: &str, ) -> Option<&(CoreId, String)> { - (self.current_open_main_group().as_deref() == Some(group)) + (self.current_open_main_group() == Some(group)) .then_some(()) .and(self.open_main_request.pending_target()) } @@ -433,7 +433,7 @@ impl Backend { /// Returns: /// Request revision when the pending core currently belongs to `group`, otherwise zero. pub(crate) fn pending_open_main_revision_for_group(&self, group: &str) -> u64 { - if self.current_open_main_group().as_deref() == Some(group) { + if self.current_open_main_group() == Some(group) { self.open_main_request.revision() } else { 0 @@ -1792,12 +1792,11 @@ impl Backend { /// /// `force` recomputes immediately when sorting changes the first core. pub(crate) fn refresh_header_ticker_default(&mut self, force: bool) { - if !force { - if let Some((core, _)) = &self.header_ticker_default { - if self.session.sessions().iter().any(|s| s.id == *core) { - return; - } - } + if !force + && let Some((core, _)) = &self.header_ticker_default + && self.session.sessions().iter().any(|s| s.id == *core) + { + return; } let now = Instant::now(); if !force @@ -1829,12 +1828,11 @@ impl Backend { /// otherwise the precomputed default cache is returned. Rendering neither searches markets nor /// mutates the backend. pub(crate) fn header_ticker(&self) -> Option<(CoreId, String)> { - if let Some(sel) = &self.layout.header_ticker { - if let Some(core) = self.core_of_uid(sel.core_uid) { - if self.session.sessions().iter().any(|s| s.id == core) { - return Some((core, sel.market.clone())); - } - } + if let Some(sel) = &self.layout.header_ticker + && let Some(core) = self.core_of_uid(sel.core_uid) + && self.session.sessions().iter().any(|s| s.id == core) + { + return Some((core, sel.market.clone())); } self.header_ticker_default .as_ref() @@ -2232,16 +2230,15 @@ impl Backend { } fn chart_text_market_for(&self, core: CoreId) -> Option { - if let Some(last) = self.chart_text_last.get(&core) { - if self + if let Some(last) = self.chart_text_last.get(&core) + && self .chart_text_refs .get(&(core, last.clone())) .copied() .unwrap_or(0) > 0 - { - return Some(last.clone()); - } + { + return Some(last.clone()); } self.chart_text_refs .iter() @@ -2323,7 +2320,8 @@ impl Backend { let mut want: Vec<(CoreId, String)> = self .chart_orderbook_refs .iter() - .filter_map(|((core, market), count)| (*count > 0).then(|| (*core, market.clone()))) + .filter(|&((_core, _market), count)| *count > 0) + .map(|((core, market), _count)| (*core, market.clone())) .collect(); want.sort_unstable_by(|a, b| a.0.cmp(&b.0).then_with(|| a.1.cmp(&b.1))); if self.desired_orderbook != want { @@ -2336,7 +2334,8 @@ impl Backend { let mut desired: Vec<(CoreId, String)> = self .chart_market_refs .iter() - .filter_map(|((core, market), count)| (*count > 0).then(|| (*core, market.clone()))) + .filter(|&((_core, _market), count)| *count > 0) + .map(|((core, market), _count)| (*core, market.clone())) .collect(); desired.sort_unstable_by(|a, b| a.0.cmp(&b.0).then_with(|| a.1.cmp(&b.1))); if self.desired != desired { @@ -2454,10 +2453,10 @@ impl Backend { // trimming — `warn_last_prune_ms == 0` fires it on the first tick. if now_ms - self.warn_last_prune_ms >= WARN_PRUNE_INTERVAL_MS { self.warn_last_prune_ms = now_ms; - if let Some(store) = self.warn_store.as_ref() { - if let Err(err) = store.prune_slices(now_ms - WARN_SLICE_RETENTION_MS) { - log::warn!("core warning slice prune failed: {err}"); - } + if let Some(store) = self.warn_store.as_ref() + && let Err(err) = store.prune_slices(now_ms - WARN_SLICE_RETENTION_MS) + { + log::warn!("core warning slice prune failed: {err}"); } } // Persist each closed episode plus its full topology (collect the follow-up re-captures into a diff --git a/crates/moon-ui-gpui/src/backend/open_request.rs b/crates/moon-ui-gpui/src/backend/open_request.rs index 6c7188690..17db4c7f9 100644 --- a/crates/moon-ui-gpui/src/backend/open_request.rs +++ b/crates/moon-ui-gpui/src/backend/open_request.rs @@ -18,7 +18,9 @@ pub(crate) struct OpenMainRequest { } /// Durable history policy applied to one exact Main-chart target. +// `Report` owns the filter snapshot for one click. Boxing it changes every history-scope match. #[derive(Clone, Debug, Default, PartialEq, Eq)] +#[allow(clippy::large_enum_variant)] pub(crate) enum ChartHistoryScope { /// Load all durable closed trades for the exact target core and market aliases. #[default] diff --git a/crates/moon-ui-gpui/src/backend/telegram.rs b/crates/moon-ui-gpui/src/backend/telegram.rs index c52afbfc7..53bc0c18b 100644 --- a/crates/moon-ui-gpui/src/backend/telegram.rs +++ b/crates/moon-ui-gpui/src/backend/telegram.rs @@ -198,12 +198,12 @@ impl Backend { if self.telegram.service.is_none() { return; } - if self.telegram.configuration_pending { - if let Some(service) = self.telegram.service.as_ref() { - let configured = service.configure(&self.config.telegram); - let localized = service.set_labels(telegram_labels()); - self.telegram.configuration_pending = !configured || !localized; - } + if self.telegram.configuration_pending + && let Some(service) = self.telegram.service.as_ref() + { + let configured = service.configure(&self.config.telegram); + let localized = service.set_labels(telegram_labels()); + self.telegram.configuration_pending = !configured || !localized; } let mut changed = false; for _ in 0..64 { @@ -228,10 +228,11 @@ impl Backend { let saved = candidate.save_telegram().is_ok(); if saved { self.config = candidate; - if newly_paired && let Some(preview) = self.preview.as_mut() { - if !preview.telegram.authorized_chat_ids.contains(&chat_id) { - preview.telegram.authorized_chat_ids.push(chat_id); - } + if newly_paired + && let Some(preview) = self.preview.as_mut() + && !preview.telegram.authorized_chat_ids.contains(&chat_id) + { + preview.telegram.authorized_chat_ids.push(chat_id); } if let Some(service) = self.telegram.service.as_ref() { self.telegram.configuration_pending |= diff --git a/crates/moon-ui-gpui/src/chart_tabs/add_stack.rs b/crates/moon-ui-gpui/src/chart_tabs/add_stack.rs index 9d9d57404..4e1c157ee 100644 --- a/crates/moon-ui-gpui/src/chart_tabs/add_stack.rs +++ b/crates/moon-ui-gpui/src/chart_tabs/add_stack.rs @@ -656,20 +656,21 @@ impl AddChartStack { // In COMPRESS mode, automatic AddToChart tabs place a new chart in the FIRST retained empty // slot without moving or resizing neighbors. Custom tabs set `hold_vacated = false` and do // not use this path. - if compress && self.hold_vacated { - if let Some(i) = self.charts.iter().position(|e| e.vacated) { - self.charts[i].core = core; - self.charts[i].market = market.to_string(); - self.charts[i].arrived_at = Instant::now(); - self.charts[i].vacated = false; - self.charts[i].owner = owner; - self.touch_count_change(); // A chart reused an empty slot; reset the debounce. - self.flash_arrival(i, cx); - let panel = self.charts[i].panel.clone(); - Self::show_market_with_history(&panel, core, market, ttl_ms, cx); - cx.notify(); - return; - } + if compress + && self.hold_vacated + && let Some(i) = self.charts.iter().position(|e| e.vacated) + { + self.charts[i].core = core; + self.charts[i].market = market.to_string(); + self.charts[i].arrived_at = Instant::now(); + self.charts[i].vacated = false; + self.charts[i].owner = owner; + self.touch_count_change(); // A chart reused an empty slot; reset the debounce. + self.flash_arrival(i, cx); + let panel = self.charts[i].panel.clone(); + Self::show_market_with_history(&panel, core, market, ttl_ms, cx); + cx.notify(); + return; } // Append a new chart; render sorting raises pinned charts in FIT-stretch mode. @@ -964,10 +965,8 @@ impl AddChartStack { for e in &self.charts { e.panel.update(cx, |p, pcx| { p.set_default_x_ppm(ppm); - if apply { - if let Some(v) = ppm { - p.apply_x_ppm(v, pcx); - } + if apply && let Some(v) = ppm { + p.apply_x_ppm(v, pcx); } }); } @@ -1309,9 +1308,7 @@ impl Render for AddChartStack { border, // An empty retained COMPRESS slot maps to `None`, so render shows a transparent tile. move |s, ix| { - let Some(&real_ix) = panel_order.get(ix) else { - return None; - }; + let &real_ix = panel_order.get(ix)?; s.charts .get(real_ix) .filter(|e| !e.vacated) diff --git a/crates/moon-ui-gpui/src/chart_tabs/apply_all/tests.rs b/crates/moon-ui-gpui/src/chart_tabs/apply_all/tests.rs index 02d63d842..55cd75b14 100644 --- a/crates/moon-ui-gpui/src/chart_tabs/apply_all/tests.rs +++ b/crates/moon-ui-gpui/src/chart_tabs/apply_all/tests.rs @@ -82,7 +82,7 @@ fn an_empty_target_set_addresses_nothing() { /// to store a value that has nowhere to be stored — and cannot perform a press another popup armed. #[test] fn only_values_that_have_a_default_can_become_one() { - let storable = vec![StackSetting::CandleView(CandleViewCfg::default())]; + let storable = [StackSetting::CandleView(CandleViewCfg::default())]; assert!(storable.iter().all(|v| v.global_slot().is_some())); let layout = layout_values( &loud_snapshot(), diff --git a/crates/moon-ui-gpui/src/chart_tabs/candle_popup.rs b/crates/moon-ui-gpui/src/chart_tabs/candle_popup.rs index d4626821d..57e86825d 100644 --- a/crates/moon-ui-gpui/src/chart_tabs/candle_popup.rs +++ b/crates/moon-ui-gpui/src/chart_tabs/candle_popup.rs @@ -87,6 +87,8 @@ fn zone_step_text(step: u16, max_label: &str) -> String { } /// Build a multiline hint below a control. +// `t!` is not a `str`. The suggested rewrite drops `key` and calls a different macro. +#[allow(clippy::unnecessary_to_owned)] fn hint_block(key: &str, p: MoonPalette, cx: &App) -> impl IntoElement { v_flex().children( t!(key) diff --git a/crates/moon-ui-gpui/src/chart_tabs/common.rs b/crates/moon-ui-gpui/src/chart_tabs/common.rs index 66b29048b..017dacee9 100644 --- a/crates/moon-ui-gpui/src/chart_tabs/common.rs +++ b/crates/moon-ui-gpui/src/chart_tabs/common.rs @@ -85,7 +85,9 @@ pub(crate) fn seg_row( /// Not `Copy`: the caption configuration it carries owns a name string per row, and a value that /// silently copies a heap-owning payload through a walk that touches every tab is a cost nobody /// sees until it is large. +// Caption rows own strings. Boxing one arm would allocate on every tab-settings copy. #[derive(Clone, Debug, PartialEq)] +#[allow(clippy::large_enum_variant)] pub(crate) enum StackSetting { /// Layout mode plus separate Fit and Scroll heights. Layout(Option, Option, Option), diff --git a/crates/moon-ui-gpui/src/chart_tabs/custom.rs b/crates/moon-ui-gpui/src/chart_tabs/custom.rs index be6f21e9c..996d88f3c 100644 --- a/crates/moon-ui-gpui/src/chart_tabs/custom.rs +++ b/crates/moon-ui-gpui/src/chart_tabs/custom.rs @@ -651,7 +651,7 @@ impl ChartTabs { specs .iter() .find(|s| s.matches(&self.group, num, bucket)) - .map_or(true, |s| { + .is_none_or(|s| { s.custom_coins.as_deref() != Some(coins.as_slice()) || s.compare_anchor != anchor || s.compare_orderbook_only != broom @@ -669,12 +669,12 @@ impl ChartTabs { /// Re-persist the active custom tab's tickers after its composition changes. pub(super) fn persist_custom_active(&mut self, cx: &mut Context) { - if let Tab::Custom(n, b) = self.active.clone() { - if let Some(stack) = self.add_stack(n, &b) { - let coins = stack.read(cx).coins(cx); - let label = self.custom_label(n); - self.persist_custom(cx, n, &b, &coins, &label); - } + if let Tab::Custom(n, b) = self.active.clone() + && let Some(stack) = self.add_stack(n, &b) + { + let coins = stack.read(cx).coins(cx); + let label = self.custom_label(n); + self.persist_custom(cx, n, &b, &coins, &label); } } @@ -867,7 +867,7 @@ impl ChartTabs { cx.spawn(async move |this, cx| { let executor = cx.update(|cx| cx.background_executor().clone()); executor.timer(Duration::from_secs(5)).await; - let _ = cx.update(|cx| { + cx.update(|cx| { this.update(cx, |this, cx| { // Is the timer still current, with the tab still inactive in the strip? let still = this.custom_gate_gen.get(&n) == Some(&want_gen) diff --git a/crates/moon-ui-gpui/src/chart_tabs/detached_host/mod.rs b/crates/moon-ui-gpui/src/chart_tabs/detached_host/mod.rs index e7a7a6c84..afd444c5d 100644 --- a/crates/moon-ui-gpui/src/chart_tabs/detached_host/mod.rs +++ b/crates/moon-ui-gpui/src/chart_tabs/detached_host/mod.rs @@ -218,7 +218,7 @@ impl DetachedChartHost { // Restore this detached panel's saved per-tab display settings from charts.json. let (group2, num2, bucket2) = (group.clone(), num, bucket.clone()); let saved = backend.read(cx).chart_specs.iter().find_map(|s| { - s.matches(&group2, num2, &bucket2).then(|| { + s.matches(&group2, num2, &bucket2).then_some({ ( s.layout_mode, s.layout_height_fit, @@ -555,7 +555,8 @@ impl DetachedChartHost { cx: &mut Context, ) -> bool { use crate::hotkeys::HotkeyAction; - let handled = match action { + + match action { // Resolved against THIS window's own hover trail, exactly as in the group window: a // detached host owns a stack rather than a single chart, so it names no panel either. HotkeyAction::ChartShot => { @@ -629,8 +630,7 @@ impl DetachedChartHost { crate::hotkeys::apply(other, b, bcx, &self.group, target.clone(), active_core) }) } - }; - handled + } } /// Return whether this window is a detached Custom tab whose spec has `custom_coins`. @@ -739,16 +739,14 @@ impl DetachedChartHost { .chart_specs .iter_mut() .find(|s| s.matches(&group, num, &bucket)) - { - if s.custom_coins.as_deref() != Some(coins.as_slice()) + && (s.custom_coins.as_deref() != Some(coins.as_slice()) || s.compare_anchor != anchor - || s.compare_orderbook_only != broom - { - s.custom_coins = Some(coins); - s.compare_anchor = anchor; - s.compare_orderbook_only = broom; - b.chart_specs_dirty = true; - } + || s.compare_orderbook_only != broom) + { + s.custom_coins = Some(coins); + s.compare_anchor = anchor; + s.compare_orderbook_only = broom; + b.chart_specs_dirty = true; } }); } diff --git a/crates/moon-ui-gpui/src/chart_tabs/detached_host/render.rs b/crates/moon-ui-gpui/src/chart_tabs/detached_host/render.rs index 52e6da64d..1362e1943 100644 --- a/crates/moon-ui-gpui/src/chart_tabs/detached_host/render.rs +++ b/crates/moon-ui-gpui/src/chart_tabs/detached_host/render.rs @@ -45,21 +45,21 @@ impl Render for DetachedChartHost { }; if rev != self.last_x_sync_rev { self.last_x_sync_rev = rev; - if let Some((handle, ppm)) = req { - if handle == window.window_handle() { - self.panel - .update(cx, |s, c| s.set_x_ppm(Some(ppm), true, c)); - let backend = self.backend.clone(); - let (num, bucket) = (self.num, self.bucket.clone()); - common::upsert_spec( - &backend, - &self.group.clone(), - num, - &bucket, - cx, - move |s| s.x_ppm = Some(ppm), - ); - } + if let Some((handle, ppm)) = req + && handle == window.window_handle() + { + self.panel + .update(cx, |s, c| s.set_x_ppm(Some(ppm), true, c)); + let backend = self.backend.clone(); + let (num, bucket) = (self.num, self.bucket.clone()); + common::upsert_spec( + &backend, + &self.group.clone(), + num, + &bucket, + cx, + move |s| s.x_ppm = Some(ppm), + ); } } } diff --git a/crates/moon-ui-gpui/src/chart_tabs/main_stack.rs b/crates/moon-ui-gpui/src/chart_tabs/main_stack.rs index aa615bba3..0203625d8 100644 --- a/crates/moon-ui-gpui/src/chart_tabs/main_stack.rs +++ b/crates/moon-ui-gpui/src/chart_tabs/main_stack.rs @@ -912,10 +912,8 @@ impl MainChartStack { for e in &self.charts { e.panel.update(cx, |p, pcx| { p.set_default_x_ppm(ppm); - if apply { - if let Some(v) = ppm { - p.apply_x_ppm(v, pcx); - } + if apply && let Some(v) = ppm { + p.apply_x_ppm(v, pcx); } }); } @@ -1716,11 +1714,11 @@ impl Render for MainChartStack { }; // The body flexes into whatever the row leaves, so the chart loses exactly the row's // height and nothing has to subtract it by hand. - return v_flex() + v_flex() .size_full() .child(tab_row) .child(div().flex_1().w_full().min_h(px(0.0)).child(body)) - .into_any_element(); + .into_any_element() } } diff --git a/crates/moon-ui-gpui/src/chart_tabs/mod.rs b/crates/moon-ui-gpui/src/chart_tabs/mod.rs index c1d3a12a1..d3b40ca0c 100644 --- a/crates/moon-ui-gpui/src/chart_tabs/mod.rs +++ b/crates/moon-ui-gpui/src/chart_tabs/mod.rs @@ -573,10 +573,10 @@ impl ChartTabs { // active, so an edit relayed while an AddToChart tab is shown would be written into // that tab's spec. It cannot be relayed later either — the edit belongs to the chart it // was made on — so it is taken and dropped rather than held. - if let Some(cfg) = main.update(cx, |stack, _| stack.take_pending_labels()) { - if matches!(this.active, Tab::Main) { - this.apply_labels(cfg, cx); - } + if let Some(cfg) = main.update(cx, |stack, _| stack.take_pending_labels()) + && matches!(this.active, Tab::Main) + { + this.apply_labels(cfg, cx); } // Main owns focus changes, while ChartTabs owns the visible anchor-aware group target. this.sync_main_chart_target(cx); @@ -1035,10 +1035,10 @@ impl ChartTabs { fn main_chart_target(&self, cx: &App) -> Option<(CoreId, String)> { // A locked comparison anchor acts like Main fullscreen for trading: its `(core, market)` // becomes the group target for F1-F6, S1-S6, and cancel-buy hotkeys. - if let Some(stack) = self.active_stack() { - if let Some(anchor) = stack.read(cx).compare_anchor() { - return Some(anchor); - } + if let Some(stack) = self.active_stack() + && let Some(anchor) = stack.read(cx).compare_anchor() + { + return Some(anchor); } self.main.read(cx).active_target(cx) } @@ -1067,10 +1067,9 @@ impl ChartTabs { // Only the ACTIVE tab's edit is taken, and only it is persisted here. A DETACHED // stack carries this observer too and is never active — its edits belong to the // window's own host, which relays them itself, so taking one here would swallow it. - if is_active { - if let Some(cfg) = stack.update(cx, |stack, _| stack.take_pending_labels()) { - this.apply_labels(cfg, cx); - } + if is_active && let Some(cfg) = stack.update(cx, |stack, _| stack.take_pending_labels()) + { + this.apply_labels(cfg, cx); } if is_active { this.sync_main_chart_target(cx); @@ -1183,12 +1182,11 @@ impl ChartTabs { } fn sync_seen_for_active(&mut self, cx: &App) { - if let Tab::Add(n, c) = self.active.clone() { - if let Some((_, _, panel)) = self.add.iter().find(|(num, cc, _)| *num == n && *cc == c) - { - let cnt = panel.read(cx).pane_count(cx); - self.seen.insert((n, c), cnt); - } + if let Tab::Add(n, c) = self.active.clone() + && let Some((_, _, panel)) = self.add.iter().find(|(num, cc, _)| *num == n && *cc == c) + { + let cnt = panel.read(cx).pane_count(cx); + self.seen.insert((n, c), cnt); } } @@ -1397,12 +1395,12 @@ fn chart_pane_label( // of the raw 100000-series number; `custom_coins` in the spec identifies it. { let specs = &backend.read(cx).chart_specs; - if let Some(s) = specs.iter().find(|s| s.matches(group, n, bucket)) { - if s.custom_coins.is_some() { - return s.custom_label.clone().unwrap_or_else(|| { - t!("chart.tab.custom", n = n - CUSTOM_NUM_BASE + 1).to_string() - }); - } + if let Some(s) = specs.iter().find(|s| s.matches(group, n, bucket)) + && s.custom_coins.is_some() + { + return s.custom_label.clone().unwrap_or_else(|| { + t!("chart.tab.custom", n = n - CUSTOM_NUM_BASE + 1).to_string() + }); } } let mut label = if group.is_empty() { diff --git a/crates/moon-ui-gpui/src/chartdx/base.rs b/crates/moon-ui-gpui/src/chartdx/base.rs index 3e4e6f262..30bf8abde 100644 --- a/crates/moon-ui-gpui/src/chartdx/base.rs +++ b/crates/moon-ui-gpui/src/chartdx/base.rs @@ -79,9 +79,10 @@ impl BaseCache { anyhow::bail!("chart base cache cannot rebuild zero-sized target"); } let generation = gpu.device_generation(); - let recreate = self.tex.as_ref().map_or(true, |tex| { - tex.w != w || tex.h != h || tex.generation != generation - }); + let recreate = self + .tex + .as_ref() + .is_none_or(|tex| tex.w != w || tex.h != h || tex.generation != generation); if recreate { self.tex = Some(Self::create_tex(device, w, h, generation)); } diff --git a/crates/moon-ui-gpui/src/chartdx/combo.rs b/crates/moon-ui-gpui/src/chartdx/combo.rs index 822f3309c..ea631685a 100644 --- a/crates/moon-ui-gpui/src/chartdx/combo.rs +++ b/crates/moon-ui-gpui/src/chartdx/combo.rs @@ -432,12 +432,14 @@ impl ComboLayer { let v_margin = combo_v_margin_px(bh); let tex_h_total = tex_h + 2 * v_margin as u32; let band_px = volume_band_px(bh); - if self.tex.as_ref().map_or(true, |c| { - c.key.tex_w != tex_w || c.key.tex_h_total != tex_h_total - }) { + if self + .tex + .as_ref() + .is_none_or(|c| c.key.tex_w != tex_w || c.key.tex_h_total != tex_h_total) + { self.tex = Some(Self::create_tex(device, tex_w, tex_h_total, v_margin)); } - if self.vol_tex.as_ref().map_or(true, |c| { + if self.vol_tex.as_ref().is_none_or(|c| { c.key.tex_w != tex_w || c.key.band_px != band_px || c.key.chart_h != tex_h }) { self.vol_tex = Some(Self::create_vol_tex(device, tex_w, band_px, tex_h)); @@ -1071,10 +1073,10 @@ impl ComboLayer { tex_w_bits: tex_w.to_bits(), time_to_px_bits: time_to_px.to_bits(), }; - if let Some((cached_key, cached)) = self.volume_window_cache { - if cached_key == key { - return cached; - } + if let Some((cached_key, cached)) = self.volume_window_cache + && cached_key == key + { + return cached; } let time_left = bake_t0 - 2.0 / time_to_px; let time_right = bake_t0 + (tex_w + 2.0) / time_to_px; diff --git a/crates/moon-ui-gpui/src/chartdx/combo/tests.rs b/crates/moon-ui-gpui/src/chartdx/combo/tests.rs index 4740d1a6a..3884908e2 100644 --- a/crates/moon-ui-gpui/src/chartdx/combo/tests.rs +++ b/crates/moon-ui-gpui/src/chartdx/combo/tests.rs @@ -433,7 +433,7 @@ fn new_row_in_span_damages_without_eviction() { cross(30.0, 0, 1.0), ]); let old = logical_ring(&layer); - assert!(old.len() + 1 <= 8, "fixture must not evict"); + assert!(old.len() < 8, "fixture must not evict"); assert!( damage_of(&layer, &[cross(120.0, 0, 4.0)], cross_span, volume_span), "a new row inside the volume span must damage without eviction" diff --git a/crates/moon-ui-gpui/src/chartdx/data_state/market.rs b/crates/moon-ui-gpui/src/chartdx/data_state/market.rs index ef112db18..6202a8ba3 100644 --- a/crates/moon-ui-gpui/src/chartdx/data_state/market.rs +++ b/crates/moon-ui-gpui/src/chartdx/data_state/market.rs @@ -923,10 +923,7 @@ impl ChartDataState { // core streams some, and the hide-candles zone below refuses to blank a bucket // that has no crosses to replace it. Re-stamped on every full range read; an // incremental drain returns just the live edge, so it only fills a NaN (below). - pr.combo_left_rel = history - .combo_left_rel_ms - .map(|v| v as f32) - .unwrap_or(f32::NAN); + pr.combo_left_rel = history.combo_left_rel_ms.unwrap_or(f32::NAN); // Restart the pan budget from the reset that ACTUALLY happened, whatever raised // it. Stamping back where the decision was made would also credit a frame whose // read returned nothing, and would miss the capacity-driven re-read that resets @@ -953,10 +950,10 @@ impl ChartDataState { // NaN — with filled candles that paints candle bodies over crosses of the same // colour, and nothing re-stamps it until the next reset. Close the NaN from // the drain; a later reset re-stamps it anyway. - if pr.combo_left_rel.is_nan() { - if let Some(left) = history.combo_left_rel_ms { - pr.combo_left_rel = left; - } + if pr.combo_left_rel.is_nan() + && let Some(left) = history.combo_left_rel_ms + { + pr.combo_left_rel = left; } pr.gpu_prepare_dirty = true; pixels_changed |= damage; diff --git a/crates/moon-ui-gpui/src/chartdx/data_state/orders.rs b/crates/moon-ui-gpui/src/chartdx/data_state/orders.rs index 67c96205b..315da5bc3 100644 --- a/crates/moon-ui-gpui/src/chartdx/data_state/orders.rs +++ b/crates/moon-ui-gpui/src/chartdx/data_state/orders.rs @@ -79,16 +79,18 @@ impl ChartDataState { // Which venues have a core behind them, for the column's dimming. Read ONCE for the sync, // like the caption gates beside it: it is a property of the connected cores, not of a pane, // and a walk per pane would repeat it for every chart in a stack. - let arb_reachable: Vec<(u8, String)> = labels_cfg - .any_drawn(|f| f == ChartLabelField::ArbColumn) - .then(|| { - session - .core_venues() - .values() - .map(|venue| (venue.id.code, venue.dex.clone())) - .collect() - }) - .unwrap_or_default(); + let arb_reachable: Vec<(u8, String)> = + if labels_cfg.any_drawn(|f| f == ChartLabelField::ArbColumn) { + { + session + .core_venues() + .values() + .map(|venue| (venue.id.code, venue.dex.clone())) + .collect() + } + } else { + Default::default() + }; // The LATEST detect this core fired, which is a live event. Same argument as the position // captions above. let wants_detect_cfg = !frozen @@ -238,7 +240,7 @@ impl ChartDataState { // The fill arrow goes too, as in the live chart's archived pass below: the frozen // store draws only in the lines style now (`frozen_store_drawn`), where the exit // line starting at the fill already says the entry filled. - let frozen_graphics = frozen.then(|| moon_core::config::ChartGraphicsCfg { + let frozen_graphics = frozen.then_some(moon_core::config::ChartGraphicsCfg { hide_closed_sell_line: false, hide_entry_fill_arrow: true, ..self.chart_graphics @@ -735,18 +737,18 @@ fn build_order_labels( notional: None, }); } - if let Some(bp) = buy { - if bp > 0.0 { - let pct = signed_pct(sp, bp, short); - push( - LineKind::Sell, - sp, - with_tag(fmt_pct(pct)), - short, - pct_color(theme, pct), - PRIO_SELL_PCT, - ); - } + if let Some(bp) = buy + && bp > 0.0 + { + let pct = signed_pct(sp, bp, short); + push( + LineKind::Sell, + sp, + with_tag(fmt_pct(pct)), + short, + pct_color(theme, pct), + PRIO_SELL_PCT, + ); } let remaining = o.exit_size(); if remaining > 0.0 && sp > 0.0 { @@ -766,18 +768,18 @@ fn build_order_labels( } // Show stop percentage from the buy price above the line for shorts and below for longs. // The primary label bypasses YTextFill, matching the Delphi stop-loss label block. - if let (Some(stp), Some(bp)) = (stop, buy) { - if bp > 0.0 { - let pct = signed_pct(stp, bp, short); - push( - LineKind::Stop, - stp, - with_tag(fmt_pct(pct)), - short, - pct_color(theme, pct), - PRIO_STOP_PCT, - ); - } + if let (Some(stp), Some(bp)) = (stop, buy) + && bp > 0.0 + { + let pct = signed_pct(stp, bp, short); + push( + LineKind::Stop, + stp, + with_tag(fmt_pct(pct)), + short, + pct_color(theme, pct), + PRIO_STOP_PCT, + ); } } } diff --git a/crates/moon-ui-gpui/src/chartdx/data_state/state.rs b/crates/moon-ui-gpui/src/chartdx/data_state/state.rs index 8387adc08..62f2e0c58 100644 --- a/crates/moon-ui-gpui/src/chartdx/data_state/state.rs +++ b/crates/moon-ui-gpui/src/chartdx/data_state/state.rs @@ -328,8 +328,8 @@ impl ChartDataState { let mut sig = 0xcbf29ce484222325; sig = mix_sig(sig, core); - sig = mix_sig(sig, str_sig(&market)); - if let Some(revs) = source.market_revisions(core, &market) { + sig = mix_sig(sig, str_sig(market)); + if let Some(revs) = source.market_revisions(core, market) { // Every revision this market has, including the chart archive. The inner gate in // `market.rs` deliberately mixes a SUBSET by hand; this one wants the lot, so it asks // for the lot rather than re-listing the fields and drifting from them. diff --git a/crates/moon-ui-gpui/src/chartdx/orderbook.rs b/crates/moon-ui-gpui/src/chartdx/orderbook.rs index 8ea4bb7a7..329f6e26e 100644 --- a/crates/moon-ui-gpui/src/chartdx/orderbook.rs +++ b/crates/moon-ui-gpui/src/chartdx/orderbook.rs @@ -220,9 +220,10 @@ impl OrderBookLayer { let tex_w = bw.round().max(1.0) as u32; let v_margin = book_v_margin_px(bh); let tex_h_total = bh.round().max(1.0) as u32 + 2 * v_margin as u32; - let need_new = self.tex.as_ref().map_or(true, |t| { - t.tex_w != tex_w || t.key.tex_h_total != tex_h_total - }); + let need_new = self + .tex + .as_ref() + .is_none_or(|t| t.tex_w != tex_w || t.key.tex_h_total != tex_h_total); if need_new { self.tex = Some(Self::create_tex(device, tex_w, tex_h_total, v_margin)); } diff --git a/crates/moon-ui-gpui/src/chartdx/pane.rs b/crates/moon-ui-gpui/src/chartdx/pane.rs index 07d3cda93..eeac3daf4 100644 --- a/crates/moon-ui-gpui/src/chartdx/pane.rs +++ b/crates/moon-ui-gpui/src/chartdx/pane.rs @@ -193,10 +193,8 @@ impl Container { .pane .as_ref() .is_some_and(|p| !p.pinned && p.source.deadline_ms().is_some_and(|d| now_ms >= d)); - if remove { - if let Some(p) = self.pane.take() { - return vec![(p.core, p.market)]; - } + if remove && let Some(p) = self.pane.take() { + return vec![(p.core, p.market)]; } Vec::new() } diff --git a/crates/moon-ui-gpui/src/chartdx/render_state.rs b/crates/moon-ui-gpui/src/chartdx/render_state.rs index 1d8f8f28b..80feec578 100644 --- a/crates/moon-ui-gpui/src/chartdx/render_state.rs +++ b/crates/moon-ui-gpui/src/chartdx/render_state.rs @@ -757,12 +757,12 @@ impl RenderState { // normal path: the shot restores the caption itself as soon as it has its picture, and this // only fires when that chain never completed. Leaving it armed would keep the EXCHANGE on // the user's own screen, where the core name belongs. - if let Some(deadline) = self.shot_caption_until { - if now >= deadline { - self.shot_caption_until = None; - self.shot_caption_frames = 0; - wants_present = true; - } + if let Some(deadline) = self.shot_caption_until + && now >= deadline + { + self.shot_caption_until = None; + self.shot_caption_frames = 0; + wants_present = true; } // Preserve independent reasons to update the camera before adding an overlay-only present. let camera_present = wants_present; diff --git a/crates/moon-ui-gpui/src/chartdx/text/labels/tests.rs b/crates/moon-ui-gpui/src/chartdx/text/labels/tests.rs index 199ee7f21..0b4790d91 100644 --- a/crates/moon-ui-gpui/src/chartdx/text/labels/tests.rs +++ b/crates/moon-ui-gpui/src/chartdx/text/labels/tests.rs @@ -452,11 +452,13 @@ fn arb_inputs( #[test] fn the_column_prints_one_line_per_venue() { let cfg = cfg_of(&[ChartLabelField::ArbColumn]); - let mut view = ArbViewCfg::default(); - view.venues = vec![ - moon_core::config::ArbVenueCfg::new(moon_core::market::ArbVenue::from_code(4)), - moon_core::config::ArbVenueCfg::new(moon_core::market::ArbVenue::from_code(9)), - ]; + let view = ArbViewCfg { + venues: vec![ + moon_core::config::ArbVenueCfg::new(moon_core::market::ArbVenue::from_code(4)), + moon_core::config::ArbVenueCfg::new(moon_core::market::ArbVenue::from_code(9)), + ], + ..ArbViewCfg::default() + }; let texts = texts_with( &cfg, @@ -501,10 +503,12 @@ fn each_line_addresses_its_own_run() { #[test] fn the_roster_decides_what_each_line_prints() { let cfg = cfg_of(&[ChartLabelField::ArbColumn]); - let mut view = ArbViewCfg::default(); - view.venues = vec![moon_core::config::ArbVenueCfg::new( - moon_core::market::ArbVenue::from_code(4), - )]; + let mut view = ArbViewCfg { + venues: vec![moon_core::config::ArbVenueCfg::new( + moon_core::market::ArbVenue::from_code(4), + )], + ..ArbViewCfg::default() + }; view.show = moon_core::config::ArbShow::Price; let price_only = texts_with(&cfg, arb_inputs(vec![arb_quote(4, 101.0)], view.clone())); @@ -544,11 +548,13 @@ fn the_venue_name_is_the_lines_prefix() { #[test] fn the_column_lines_its_cells_up() { let cfg = cfg_of(&[ChartLabelField::ArbColumn]); - let mut view = ArbViewCfg::default(); - view.venues = vec![ - moon_core::config::ArbVenueCfg::new(moon_core::market::ArbVenue::from_code(4)), - moon_core::config::ArbVenueCfg::new(moon_core::market::ArbVenue::from_code(101)), - ]; + let view = ArbViewCfg { + venues: vec![ + moon_core::config::ArbVenueCfg::new(moon_core::market::ArbVenue::from_code(4)), + moon_core::config::ArbVenueCfg::new(moon_core::market::ArbVenue::from_code(101)), + ], + ..ArbViewCfg::default() + }; // "BinanceF" is eight characters and "UpBit" five; the prices differ in width too. let quotes = vec![arb_quote(4, 9.5), arb_quote(101, 101.25)]; @@ -590,14 +596,14 @@ fn the_column_lines_its_cells_up() { #[test] fn the_roster_floor_drops_quiet_venues() { let cfg = cfg_of(&[ChartLabelField::ArbColumn]); - let mut view = ArbViewCfg { + let view = ArbViewCfg { min_abs_pct: 1.0, + venues: vec![ + moon_core::config::ArbVenueCfg::new(moon_core::market::ArbVenue::from_code(4)), + moon_core::config::ArbVenueCfg::new(moon_core::market::ArbVenue::from_code(9)), + ], ..ArbViewCfg::default() }; - view.venues = vec![ - moon_core::config::ArbVenueCfg::new(moon_core::market::ArbVenue::from_code(4)), - moon_core::config::ArbVenueCfg::new(moon_core::market::ArbVenue::from_code(9)), - ]; let texts = texts_with( &cfg, diff --git a/crates/moon-ui-gpui/src/chartdx/text/prepare.rs b/crates/moon-ui-gpui/src/chartdx/text/prepare.rs index 3382030ec..01a4786f3 100644 --- a/crates/moon-ui-gpui/src/chartdx/text/prepare.rs +++ b/crates/moon-ui-gpui/src/chartdx/text/prepare.rs @@ -115,54 +115,52 @@ impl RenderState { } else { 0.0 }; - if band_on { - if let Some(stats) = volume_stats { - let band = volume_band_h; - // The band is LINEAR and its second line sits at the ratio itself, mirroring - // the sides shader exactly. - let avg_frac = volume_style.m[3].clamp(0.0, 1.0); - // Moonbot's `Ind. Pos`: the scale is a bracket whose stem the shader stands - // at the same offset this reads, and each label prints to the RIGHT of the - // tick at its level — on either side, so the right-hand bracket's labels run - // into the margin, exactly as the reference prints them. - let bracket_x = plot_left - + moon_chart::volume_bars::scale_bracket_offset(plot_w, volume_scale_right); - let label_x = bracket_x - + moon_chart::volume_bars::VOLUME_SCALE_TICK_PX - + moon_chart::volume_bars::VOLUME_SCALE_LABEL_GAP_PX; - let label_ax = 0.0; - for (frac, value) in [(1.0f32, stats.max), (avg_frac, stats.avg)] { - // Too close to the band floor to read: skip rather than overprint. The - // room a label needs follows its own line height, so the larger, bolder - // scale face cannot start hanging past the plot's bottom edge unnoticed. - if !super::volume_scale_label_fits(band, frac) { - continue; - } - // The room a label has is what lies between its tick and the plot's - // right edge — the right-hand bracket leaves it the margin only. - let Some(label) = super::volume_scale_label( - value, - &self.panes[idx].quote, - plot_right - label_x, - |text| { - super::measure_sized_text_run( - &mut self.text_runs, - self.text_run_cursor, - ctx, - text, - VOLUME_SCALE_FONT_SIZE, - VOLUME_SCALE_LINE_H, - VOLUME_SCALE_WEIGHT, - ) - .width - .as_f32() - }, - ) else { - continue; - }; - let y = plot_bottom - band * frac; - self.draw_volume_scale_text(ctx, &label, label_x, y, label_ax, 0.5, ink)?; + if band_on && let Some(stats) = volume_stats { + let band = volume_band_h; + // The band is LINEAR and its second line sits at the ratio itself, mirroring + // the sides shader exactly. + let avg_frac = volume_style.m[3].clamp(0.0, 1.0); + // Moonbot's `Ind. Pos`: the scale is a bracket whose stem the shader stands + // at the same offset this reads, and each label prints to the RIGHT of the + // tick at its level — on either side, so the right-hand bracket's labels run + // into the margin, exactly as the reference prints them. + let bracket_x = plot_left + + moon_chart::volume_bars::scale_bracket_offset(plot_w, volume_scale_right); + let label_x = bracket_x + + moon_chart::volume_bars::VOLUME_SCALE_TICK_PX + + moon_chart::volume_bars::VOLUME_SCALE_LABEL_GAP_PX; + let label_ax = 0.0; + for (frac, value) in [(1.0f32, stats.max), (avg_frac, stats.avg)] { + // Too close to the band floor to read: skip rather than overprint. The + // room a label needs follows its own line height, so the larger, bolder + // scale face cannot start hanging past the plot's bottom edge unnoticed. + if !super::volume_scale_label_fits(band, frac) { + continue; } + // The room a label has is what lies between its tick and the plot's + // right edge — the right-hand bracket leaves it the margin only. + let Some(label) = super::volume_scale_label( + value, + &self.panes[idx].quote, + plot_right - label_x, + |text| { + super::measure_sized_text_run( + &mut self.text_runs, + self.text_run_cursor, + ctx, + text, + VOLUME_SCALE_FONT_SIZE, + VOLUME_SCALE_LINE_H, + VOLUME_SCALE_WEIGHT, + ) + .width + .as_f32() + }, + ) else { + continue; + }; + let y = plot_bottom - band * frac; + self.draw_volume_scale_text(ctx, &label, label_x, y, label_ax, 0.5, ink)?; } } // Horizontal volumes: the zone's corner caption names the time window and the price @@ -930,28 +928,28 @@ impl RenderState { } // Draw the cursor's percentage deviation from the nearest book side right of // the separator, below the line. - if let Some(r) = cursor_ref { - if r > 0.0 { - let pct = (cursor_price - r) / r * 100.0; - let m = self.draw_label_text( - ctx, - &fmt_pct(pct), - right_x, - cy_log + gap, - 0.0, - 0.0, - cur_col, - )?; - placed.push(PlacedLabel { - x: right_x, - y: cy_log + gap, - ax: 0.0, - ay: 0.0, - w: m.width.as_f32(), - h: m.line_height.as_f32(), - solid: true, - }); - } + if let Some(r) = cursor_ref + && r > 0.0 + { + let pct = (cursor_price - r) / r * 100.0; + let m = self.draw_label_text( + ctx, + &fmt_pct(pct), + right_x, + cy_log + gap, + 0.0, + 0.0, + cur_col, + )?; + placed.push(PlacedLabel { + x: right_x, + y: cy_log + gap, + ax: 0.0, + ay: 0.0, + w: m.width.as_f32(), + h: m.line_height.as_f32(), + solid: true, + }); } } } else { diff --git a/crates/moon-ui-gpui/src/controls/fmt.rs b/crates/moon-ui-gpui/src/controls/fmt.rs index e3dd80041..005d9288f 100644 --- a/crates/moon-ui-gpui/src/controls/fmt.rs +++ b/crates/moon-ui-gpui/src/controls/fmt.rs @@ -33,9 +33,7 @@ pub fn fmt_field2_signed(v: f32) -> String { /// Trailing zeros are removed, which also hides floating-point noise such as 0.6000000238. pub fn fmt_adaptive(v: f64) -> String { let a = v.abs(); - let decimals: usize = if a == 0.0 { - 0 - } else if a >= 100.0 { + let decimals: usize = if a == 0.0 || a >= 100.0 { 0 } else if a >= 10.0 { 1 diff --git a/crates/moon-ui-gpui/src/core_order.rs b/crates/moon-ui-gpui/src/core_order.rs index ec6efb536..a16815a04 100644 --- a/crates/moon-ui-gpui/src/core_order.rs +++ b/crates/moon-ui-gpui/src/core_order.rs @@ -224,6 +224,8 @@ impl CoreOrder { /// Order the live sessions kept by `keep` (group / scope filter). /// /// The predicate controls membership; this method only canonicalizes the retained rows. + // The name matches the other ordering entry points. Dropping `self` would lose the rank table. + #[allow(clippy::wrong_self_convention)] pub(crate) fn from_sessions( &self, sessions: &[moon_core::session::CoreSession], @@ -246,6 +248,8 @@ impl CoreOrder { /// Database-only cores whose server is absent from the current config share the `u32::MAX` /// rank `rank` hands out for anything unknown, so they land after every configured core and /// keep the query order — `sort_by_key` is a STABLE sort. + // Same rank table as `from_sessions`. A `from_*` rename would churn every caller. + #[allow(clippy::wrong_self_convention)] pub(crate) fn from_db(&self, mut rows: Vec<(CoreId, String)>) -> OrderedCores { rows.sort_by_key(|(id, _)| self.rank(*id)); OrderedCores(rows) diff --git a/crates/moon-ui-gpui/src/design.rs b/crates/moon-ui-gpui/src/design.rs index b24e522e0..5e6b86cb9 100644 --- a/crates/moon-ui-gpui/src/design.rs +++ b/crates/moon-ui-gpui/src/design.rs @@ -1161,7 +1161,7 @@ impl<'a> MonoBodyTextMeasurer<'a> { pub(crate) fn new(cx: &'a App) -> Self { let tokens = MoonTheme::active_tokens(cx); let size = px(tokens.font(base_text(cx))); - let family = SharedString::from(tokens.font_family(true)); + let family = tokens.font_family(true); let text_system = cx.text_system(); let mut fonts = HashMap::with_capacity(2); for weight in [FontWeight::NORMAL, FontWeight::SEMIBOLD] { diff --git a/crates/moon-ui-gpui/src/diagnostics/debug_window.rs b/crates/moon-ui-gpui/src/diagnostics/debug_window.rs index e0fa738b5..98485aa0f 100644 --- a/crates/moon-ui-gpui/src/diagnostics/debug_window.rs +++ b/crates/moon-ui-gpui/src/diagnostics/debug_window.rs @@ -434,13 +434,12 @@ pub(crate) fn open_debug_perf_window( group: String, owner: Option, ) { - if let Some(handle) = backend.read(cx).debug_window { - if handle + if let Some(handle) = backend.read(cx).debug_window + && handle .update(cx, |_, window, _| window.activate_window()) .is_ok() - { - return; - } + { + return; } let opts = windowing::debug_window_options( @@ -527,7 +526,7 @@ pub(crate) fn debug_chart_target(b: &Backend) -> Option<(CoreId, String)> { pub(crate) fn spawn_debug_chart_windows(cx: &mut App, backend: Entity) { let Some((core, group, market, epoch, theme, owner)) = ({ let b = backend.read(cx); - debug_chart_target(&b).map(|(core, market)| { + debug_chart_target(b).map(|(core, market)| { let group = b .session .sessions() diff --git a/crates/moon-ui-gpui/src/firetest/stages/order_cancel.rs b/crates/moon-ui-gpui/src/firetest/stages/order_cancel.rs index 774a325f5..53273bde2 100644 --- a/crates/moon-ui-gpui/src/firetest/stages/order_cancel.rs +++ b/crates/moon-ui-gpui/src/firetest/stages/order_cancel.rs @@ -255,23 +255,21 @@ impl Runtime { run.cancel_submit_ms.unwrap_or_default(), ); } - if run.closed_store_ms.is_none() { - if let Some(state) = core.order_lines.order_state(uid) { - if let (Some(closed_store_ms), Some(closed_rev)) = - (state.closed_store_ms, state.closed_rev) - { - let closed_ms_i64 = closed_store_ms.round() as i64; - run.closed_store_ms = Some(closed_ms_i64); - run.closed_order_lines_rev = Some(closed_rev); - run.closed_reason = state.closed_reason; - firetest_info(&format!( - "[firetest] order_cancel_lag closed uid={uid} order_lines_rev={} reason={:?} cancel_to_order_lines_ms={}", - closed_rev, - state.closed_reason, - closed_ms_i64 - run.cancel_submit_ms.unwrap_or(closed_ms_i64) - )); - } - } + if run.closed_store_ms.is_none() + && let Some(state) = core.order_lines.order_state(uid) + && let (Some(closed_store_ms), Some(closed_rev)) = + (state.closed_store_ms, state.closed_rev) + { + let closed_ms_i64 = closed_store_ms.round() as i64; + run.closed_store_ms = Some(closed_ms_i64); + run.closed_order_lines_rev = Some(closed_rev); + run.closed_reason = state.closed_reason; + firetest_info(&format!( + "[firetest] order_cancel_lag closed uid={uid} order_lines_rev={} reason={:?} cancel_to_order_lines_ms={}", + closed_rev, + state.closed_reason, + closed_ms_i64 - run.cancel_submit_ms.unwrap_or(closed_ms_i64) + )); } let Some(closed_rev) = run.closed_order_lines_rev else { self.wait_log("order_cancel_lag waiting for cancelled order snapshot"); diff --git a/crates/moon-ui-gpui/src/firetest/stages/perf.rs b/crates/moon-ui-gpui/src/firetest/stages/perf.rs index 73a79c084..d123fe434 100644 --- a/crates/moon-ui-gpui/src/firetest/stages/perf.rs +++ b/crates/moon-ui-gpui/src/firetest/stages/perf.rs @@ -114,10 +114,10 @@ impl Runtime { return 0; } let mut applied = 0usize; - if let Some(chart) = backend.live_chart_consumers().into_iter().next() { - if chart.set_firetest_text_labels(count) { - applied += 1; - } + if let Some(chart) = backend.live_chart_consumers().into_iter().next() + && chart.set_firetest_text_labels(count) + { + applied += 1; } firetest_info(&format!( "[firetest] text overlay labels={count} applied_to={applied}" diff --git a/crates/moon-ui-gpui/src/firetest/storm/win.rs b/crates/moon-ui-gpui/src/firetest/storm/win.rs index 67cc9e430..c7a22ec3d 100644 --- a/crates/moon-ui-gpui/src/firetest/storm/win.rs +++ b/crates/moon-ui-gpui/src/firetest/storm/win.rs @@ -92,7 +92,7 @@ pub(in crate::firetest) fn start_mouse_storm( let elapsed = start.elapsed(); if target > elapsed { std::thread::sleep(target - elapsed); - } else if sent % 128 == 0 { + } else if sent.is_multiple_of(128) { std::thread::yield_now(); } } diff --git a/crates/moon-ui-gpui/src/hotkeys.rs b/crates/moon-ui-gpui/src/hotkeys.rs index 63a0435e3..89800f1c1 100644 --- a/crates/moon-ui-gpui/src/hotkeys.rs +++ b/crates/moon-ui-gpui/src/hotkeys.rs @@ -1110,6 +1110,10 @@ pub fn apply( // no switch, no pill and no summary on screen to say it happened — and the next order // would go out under it. Refusing lets the key propagate, exactly like the no-core arm. A::ManualStrategy(_) if b.is_auto_overview_scope(group) => false, + // Clippy's suggested collapsible_match rewrite does not compile: folding + // the inner `if` into this match leaves `Some(_)` uncovered because the + // previous `ManualStrategy` arm is a guard. + #[allow(clippy::collapsible_match)] A::ManualStrategy(i) => match active_core { Some(core) => { if crate::controls::select_manual_strategy(b, core, i) { diff --git a/crates/moon-ui-gpui/src/media/exchange_logos.rs b/crates/moon-ui-gpui/src/media/exchange_logos.rs index fe14997ce..08a8e7876 100644 --- a/crates/moon-ui-gpui/src/media/exchange_logos.rs +++ b/crates/moon-ui-gpui/src/media/exchange_logos.rs @@ -132,7 +132,7 @@ pub(crate) fn prewarm() { /// Returns: /// Nothing; concurrent callers block until the winning initializer completes. fn prewarm_once(gate: &OnceLock<()>, warm: impl FnOnce()) { - gate.get_or_init(|| warm()); + gate.get_or_init(warm); } #[cfg(test)] diff --git a/crates/moon-ui-gpui/src/media/sound.rs b/crates/moon-ui-gpui/src/media/sound.rs index da44b60e7..40ae52fad 100644 --- a/crates/moon-ui-gpui/src/media/sound.rs +++ b/crates/moon-ui-gpui/src/media/sound.rs @@ -50,7 +50,7 @@ pub(crate) fn rescan(cx: &mut App, done: impl FnOnce(&mut App) + 'static) { let catalog = cx .background_spawn(async move { sources::scan(&dir) }) .await; - let _ = cx.update(|cx| { + cx.update(|cx| { install(catalog); done(cx); }); diff --git a/crates/moon-ui-gpui/src/panels/alerts/controls.rs b/crates/moon-ui-gpui/src/panels/alerts/controls.rs index afb6d1bb4..819ea9ea9 100644 --- a/crates/moon-ui-gpui/src/panels/alerts/controls.rs +++ b/crates/moon-ui-gpui/src/panels/alerts/controls.rs @@ -91,7 +91,7 @@ impl AlertsPanel { &cores, selection, crate::controls::CoreAllRowMode::ImplicitOrComplete, - &t!("alerts.all_cores").to_string(), + t!("alerts.all_cores").as_ref(), &|n| t!("alerts.cores_n", n = n).to_string(), ) .label @@ -118,7 +118,7 @@ impl AlertsPanel { crate::controls::core_combo( "alerts-cores", &cores, - &venues, + venues, selection, crate::controls::CoreAllRowMode::ImplicitOrComplete, t!("alerts.all_cores").to_string(), diff --git a/crates/moon-ui-gpui/src/panels/assets/mod.rs b/crates/moon-ui-gpui/src/panels/assets/mod.rs index 308b646cd..5a35f1adb 100644 --- a/crates/moon-ui-gpui/src/panels/assets/mod.rs +++ b/crates/moon-ui-gpui/src/panels/assets/mod.rs @@ -560,14 +560,12 @@ impl AssetsView { { return Some(core); } - if workspace_owned { - if let Some(scope) = scope.as_ref() { - let ids = scope.ids(); - return self - .overview_wallet_pick - .filter(|core| ids.contains(core)) - .or_else(|| ids.first().copied()); - } + if workspace_owned && let Some(scope) = scope.as_ref() { + let ids = scope.ids(); + return self + .overview_wallet_pick + .filter(|core| ids.contains(core)) + .or_else(|| ids.first().copied()); } None } diff --git a/crates/moon-ui-gpui/src/panels/assets/spot_order.rs b/crates/moon-ui-gpui/src/panels/assets/spot_order.rs index 3480c059c..165b98b08 100644 --- a/crates/moon-ui-gpui/src/panels/assets/spot_order.rs +++ b/crates/moon-ui-gpui/src/panels/assets/spot_order.rs @@ -116,9 +116,8 @@ fn live_order_on_market(backend: &Backend, core: CoreId, market: &str) -> Option let orders = &store.core(core)?.orders; let on_market = || orders.iter().filter(|o| o.market == market); on_market() - .filter(|o| crate::panels::orders::executed(o)) - .last() - .or_else(|| on_market().last()) + .rfind(|o| crate::panels::orders::executed(o)) + .or_else(|| on_market().next_back()) .map(|o| o.uid) } diff --git a/crates/moon-ui-gpui/src/panels/assets/table.rs b/crates/moon-ui-gpui/src/panels/assets/table.rs index 6c527726d..b56e78573 100644 --- a/crates/moon-ui-gpui/src/panels/assets/table.rs +++ b/crates/moon-ui-gpui/src/panels/assets/table.rs @@ -261,7 +261,7 @@ impl AssetsView { cores, selection, crate::controls::CoreAllRowMode::ImplicitOrComplete, - &t!("assets.all_cores").to_string(), + t!("assets.all_cores").as_ref(), &|n| t!("assets.cores_n", n = n).to_string(), ) .label @@ -282,7 +282,7 @@ impl AssetsView { crate::controls::core_combo( "assets-core", cores, - &venues, + venues, selection, crate::controls::CoreAllRowMode::ImplicitOrComplete, t!("assets.all_cores").to_string(), @@ -452,32 +452,32 @@ impl AssetsView { .child(super::balances::figure(Some(agg), p, cx)), ) .on_click(cx.listener(move |this, _, window, cx| { - if let AssetsScope::Group(_) = &this.scope { - if let Some(scope) = this.effective_scope(this.backend.read(cx)) { - if scope.is_auto_core() { - // A selected Auto core pins the wallet host to the rail. - return; - } - if scope.is_workspace_owned() { - // Auto Overview: pick a transfer host without touching the rail or - // Classic's retained `selected_core`. - if this.overview_wallet_pick != Some(cid) { - this.overview_wallet_pick = Some(cid); - if let Err(error) = - this.backend.read(cx).session.refresh_transfer_assets(cid) - { - log::warn!("assets refresh failed for core {cid}: {error}"); - window.push_notification( - MoonNotification::error(error.to_string()), - cx, - ); - } - let backend = this.backend.clone(); - this.rebuild_cache(backend.read(cx)); - cx.notify(); + if let AssetsScope::Group(_) = &this.scope + && let Some(scope) = this.effective_scope(this.backend.read(cx)) + { + if scope.is_auto_core() { + // A selected Auto core pins the wallet host to the rail. + return; + } + if scope.is_workspace_owned() { + // Auto Overview: pick a transfer host without touching the rail or + // Classic's retained `selected_core`. + if this.overview_wallet_pick != Some(cid) { + this.overview_wallet_pick = Some(cid); + if let Err(error) = + this.backend.read(cx).session.refresh_transfer_assets(cid) + { + log::warn!("assets refresh failed for core {cid}: {error}"); + window.push_notification( + MoonNotification::error(error.to_string()), + cx, + ); } - return; + let backend = this.backend.clone(); + this.rebuild_cache(backend.read(cx)); + cx.notify(); } + return; } } if this.selected_core != Some(cid) { diff --git a/crates/moon-ui-gpui/src/panels/assets/window.rs b/crates/moon-ui-gpui/src/panels/assets/window.rs index 2364618ec..9749bfba5 100644 --- a/crates/moon-ui-gpui/src/panels/assets/window.rs +++ b/crates/moon-ui-gpui/src/panels/assets/window.rs @@ -49,13 +49,12 @@ pub fn open( cx: &mut App, ) { // Focus the existing singleton instead of opening a duplicate. - if let Some(handle) = backend.read(cx).assets_window { - if handle + if let Some(handle) = backend.read(cx).assets_window + && handle .update(cx, |_, window, _| window.activate_window()) .is_ok() - { - return; - } + { + return; } let saved = backend.read(cx).layout.assets_window; let bounds = saved.map_or( diff --git a/crates/moon-ui-gpui/src/panels/chart/mod.rs b/crates/moon-ui-gpui/src/panels/chart/mod.rs index 51dd726ee..3dd62e4c8 100644 --- a/crates/moon-ui-gpui/src/panels/chart/mod.rs +++ b/crates/moon-ui-gpui/src/panels/chart/mod.rs @@ -60,8 +60,10 @@ use windows::core::PCWSTR; #[cfg(windows)] fn monitor_refresh_hz() -> u32 { unsafe { - let mut mode = DEVMODEW::default(); - mode.dmSize = std::mem::size_of::() as u16; + let mut mode = DEVMODEW { + dmSize: std::mem::size_of::() as u16, + ..DEVMODEW::default() + }; if EnumDisplaySettingsW(PCWSTR::null(), ENUM_CURRENT_SETTINGS, &mut mode).as_bool() && mode.dmDisplayFrequency > 1 { @@ -597,7 +599,7 @@ impl ChartPanel { // A fresh panel holds no override yet, so its effective values ARE its kind's defaults. let settings_sig = { let b = backend.read(cx); - chart_settings_sig(&b, None, None, None, kind) + chart_settings_sig(b, None, None, None, kind) }; let display_time_revision = backend.read(cx).display_time_revision.clone(); cx.observe(&display_time_revision, |this, _revision, cx| { @@ -648,7 +650,7 @@ impl ChartPanel { ( sig, chart_settings_sig( - &b, + b, this.chart_graphics, this.candle_view, this.chart_labels.clone(), @@ -831,7 +833,7 @@ impl ChartPanel { // A fresh panel holds no override yet, so its effective values ARE the global defaults. let settings_sig = { let b = backend.read(cx); - chart_settings_sig(&b, None, None, None, kind) + chart_settings_sig(b, None, None, None, kind) }; let display_time_revision = backend.read(cx).display_time_revision.clone(); cx.observe(&display_time_revision, |this, _revision, cx| { @@ -881,7 +883,7 @@ impl ChartPanel { ( sig, chart_settings_sig( - &b, + b, this.chart_graphics, this.candle_view, this.chart_labels.clone(), @@ -1508,7 +1510,7 @@ impl ChartPanel { self.settings_sig = { let b = self.backend.read(cx); chart_settings_sig( - &b, + b, self.chart_graphics, cfg, self.chart_labels.clone(), @@ -1540,7 +1542,7 @@ impl ChartPanel { self.settings_sig = { let b = self.backend.read(cx); chart_settings_sig( - &b, + b, cfg, self.candle_view, self.chart_labels.clone(), @@ -1573,7 +1575,7 @@ impl ChartPanel { self.settings_sig = { let b = self.backend.read(cx); chart_settings_sig( - &b, + b, self.chart_graphics, self.candle_view, cfg, @@ -1867,7 +1869,7 @@ impl ChartPanel { let settings_sig = { let b = self.backend.read(cx); chart_settings_sig( - &b, + b, self.chart_graphics, self.candle_view, self.chart_labels.clone(), diff --git a/crates/moon-ui-gpui/src/panels/chart/refs.rs b/crates/moon-ui-gpui/src/panels/chart/refs.rs index 17ed22890..cded051dc 100644 --- a/crates/moon-ui-gpui/src/panels/chart/refs.rs +++ b/crates/moon-ui-gpui/src/panels/chart/refs.rs @@ -77,12 +77,11 @@ impl ChartPanel { /// Ask the core for strategy-filter rows on this panel's active market, or clear the request. pub(super) fn sync_chart_text(&mut self, cx: &mut App) { - let want = if self.historical { - None - } else if !self - .settings_sig - .chart_labels - .any_drawn(|f| f == ChartLabelField::StrategyFilters) + let want = if self.historical + || !self + .settings_sig + .chart_labels + .any_drawn(|f| f == ChartLabelField::StrategyFilters) { None } else { diff --git a/crates/moon-ui-gpui/src/panels/chart/render.rs b/crates/moon-ui-gpui/src/panels/chart/render.rs index a3175afd6..fc10424b9 100644 --- a/crates/moon-ui-gpui/src/panels/chart/render.rs +++ b/crates/moon-ui-gpui/src/panels/chart/render.rs @@ -475,12 +475,10 @@ impl Render for ChartPanel { f32::from(bounds.size.height) * zoom, platform_sf, ); - if is_main { - if let Some(probe) = firetest_probe { - backend.update(cx, |b, _| { - crate::firetest::observe_chart_probe(b, probe); - }); - } + if is_main && let Some(probe) = firetest_probe { + backend.update(cx, |b, _| { + crate::firetest::observe_chart_probe(b, probe); + }); } }, ) diff --git a/crates/moon-ui-gpui/src/panels/chart/render_input.rs b/crates/moon-ui-gpui/src/panels/chart/render_input.rs index 50acd7d6a..3900c7ab5 100644 --- a/crates/moon-ui-gpui/src/panels/chart/render_input.rs +++ b/crates/moon-ui-gpui/src/panels/chart/render_input.rs @@ -314,14 +314,13 @@ pub(super) fn scroll_wheel( // In an AddToChart stack, wheel input over the left price-axis strip scrolls the stack rather // than zooming: leave the event unconsumed so it bubbles to MoonVirtualList. Over the graph or // book, handle zoom below and stop propagation so the stack does not scroll too. - if this.num.is_some() && within { - if let Some(idx) = this.input.pane_at(pos.0, pos.1) { - if let Some((_, rect)) = this.input.pane_rects.iter().find(|(i, _)| *i == idx) { - if pos.0 <= rect.x + moon_chart::PRICE_AXIS_W * sf { - return; - } - } - } + if this.num.is_some() + && within + && let Some(idx) = this.input.pane_at(pos.0, pos.1) + && let Some((_, rect)) = this.input.pane_rects.iter().find(|(i, _)| *i == idx) + && pos.0 <= rect.x + moon_chart::PRICE_AXIS_W * sf + { + return; } // Lines represent discrete mouse-wheel clicks, commonly +/-1 or +/-3 on Windows. Pixels are // precise trackpad/Magic Mouse input on macOS, delivered as a continuous inertial stream. @@ -1084,10 +1083,8 @@ pub(super) fn mouse_move( } let cursor_changed = prev_cursor != this.input.cursor || prev_hovered != this.input.hovered_pane; - if cursor_changed { - if this.sync_native_cursor(cx) { - crate::diag::bump(&crate::diag::CHART_CURSOR_UPDATE); - } + if cursor_changed && this.sync_native_cursor(cx) { + crate::diag::bump(&crate::diag::CHART_CURSOR_UPDATE); } // Dragging changes cameras/axes and GPUI-side controls. Cursor-only motion remains in retained // gpu_canvas, which presents the crosshair/readout without cx.notify(). diff --git a/crates/moon-ui-gpui/src/panels/chart/trade.rs b/crates/moon-ui-gpui/src/panels/chart/trade.rs index 8dcf3415b..1453d5a8c 100644 --- a/crates/moon-ui-gpui/src/panels/chart/trade.rs +++ b/crates/moon-ui-gpui/src/panels/chart/trade.rs @@ -633,9 +633,7 @@ impl ChartPanel { if !self.order_gestures_allowed(cx) { return None; } - let Some(pane) = self.input.pane_at(pos.0, pos.1) else { - return None; - }; + let pane = self.input.pane_at(pos.0, pos.1)?; // No line lives in the horizontal-volume zone — lines run from their start to the plot's // right edge and on into the book, never left of the plot — so nothing there is a hit. Said // HERE rather than at each caller: a press in the zone is neither a grab nor a cancel, @@ -644,16 +642,11 @@ impl ChartPanel { if self.hvol_pane_at(pos).is_some() { return None; } - let Some((core, market)) = self + let (core, market) = self .chart - .with_container(|container| container.target(pane)) - else { - return None; - }; - let Some(plot) = self.local_plot_rect(pane) else { - return None; - }; - let Some((center, range, epoch_ms, left_rel, window_ms)) = + .with_container(|container| container.target(pane))?; + let plot = self.local_plot_rect(pane)?; + let (center, range, epoch_ms, left_rel, window_ms) = self.chart.with_container(|container| { container.pane(pane).map(|pane| { let (left, window) = pane.view.visible_x(plot.w); @@ -665,10 +658,7 @@ impl ChartPanel { window, ) }) - }) - else { - return None; - }; + })?; if plot.h <= 1.0 || !(range > 0.0) || !(window_ms > 0.0) { return None; } @@ -898,7 +888,7 @@ impl ChartPanel { // denotes manual/join orders. The row's `coin` was resolved with this core's exchange // rules and is what the menu writes into the coin blacklists. let b = self.backend.read(cx); - if !self.workspace_action_allowed(&b, core) { + if !self.workspace_action_allowed(b, core) { return false; } let order = b @@ -1312,7 +1302,7 @@ impl ChartPanel { if hit.on_start_cross { return false; } - if !self.workspace_action_allowed(&self.backend.read(cx), hit.core) { + if !self.workspace_action_allowed(self.backend.read(cx), hit.core) { return false; } let price = hit.price as f64; diff --git a/crates/moon-ui-gpui/src/panels/chart/warn.rs b/crates/moon-ui-gpui/src/panels/chart/warn.rs index ba7e61362..ac09095a0 100644 --- a/crates/moon-ui-gpui/src/panels/chart/warn.rs +++ b/crates/moon-ui-gpui/src/panels/chart/warn.rs @@ -464,10 +464,10 @@ fn merge_axis(cluster: &mut WarnCluster, episode: &WarnEpisode) { // history, so it has nothing to draw on a time axis. WarnAxis::ApiExpiry | WarnAxis::ApiQuota => {} } - if let Some(core) = episode.core_id { - if !cluster.cores.contains(&core) { - cluster.cores.push(core); - } + if let Some(core) = episode.core_id + && !cluster.cores.contains(&core) + { + cluster.cores.push(core); } } diff --git a/crates/moon-ui-gpui/src/panels/core_status/mod.rs b/crates/moon-ui-gpui/src/panels/core_status/mod.rs index 56511e2ef..2d33868e9 100644 --- a/crates/moon-ui-gpui/src/panels/core_status/mod.rs +++ b/crates/moon-ui-gpui/src/panels/core_status/mod.rs @@ -59,9 +59,10 @@ use moon_core::session::core_update::CoreUpdatePhase; use rust_i18n::t; /// Chart X-axis span selectable in the detached window. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] enum ChartWindow { /// Last five minutes. + #[default] Min5, /// Last hour. Hour1, @@ -85,12 +86,6 @@ impl ChartWindow { } } -impl Default for ChartWindow { - fn default() -> Self { - Self::Min5 - } -} - /// Available Core Status presentations. #[derive(Debug, Clone, Copy, PartialEq, Eq)] enum CoreStatusMode { @@ -1358,7 +1353,7 @@ impl CoreStatusView { cores, selection, crate::controls::CoreAllRowMode::ImplicitOrComplete, - &t!("core_status.all_cores").to_string(), + t!("core_status.all_cores").as_ref(), &|n| t!("core_status.cores_n", n = n).to_string(), ) .label @@ -1386,7 +1381,7 @@ impl CoreStatusView { crate::controls::core_combo( "core-status-core", cores, - &venues, + venues, selection, crate::controls::CoreAllRowMode::ImplicitOrComplete, t!("core_status.all_cores").to_string(), diff --git a/crates/moon-ui-gpui/src/panels/core_status/server_view.rs b/crates/moon-ui-gpui/src/panels/core_status/server_view.rs index 84e864aff..c3c0e0b70 100644 --- a/crates/moon-ui-gpui/src/panels/core_status/server_view.rs +++ b/crates/moon-ui-gpui/src/panels/core_status/server_view.rs @@ -243,51 +243,50 @@ pub(super) fn grouped_server_view( let tree = MoonTree::custom(state, move |entry, meta, _window, app| { let p = MoonPalette::active(app); if entry.is_root() { - if let Some(server_index) = server_positions.get(entry.item().id()).copied() { - if let Some(group) = groups.get(server_index) { - let editing_input = (editing == Some(group.key)) - .then(|| edit_input.clone()) - .flatten(); - // Highlight follows the body-click selection (chart target). - return MoonListItem::new(meta.index) - .selected(chart_selected == Some(group.key)) - .child(server_row( - group, - ip_masked, - entry.is_expanded(), - editing_input, - widths, - &weak_view, - &backend, - p, - app, - )); - } - } - } else if let Some(&(server_index, core_index)) = core_positions.get(entry.item().id()) { - if let Some(core) = groups - .get(server_index) - .and_then(|group| group.cores.get(core_index)) + if let Some(server_index) = server_positions.get(entry.item().id()).copied() + && let Some(group) = groups.get(server_index) { - // The highlight follows the SELECTION now, not the chart: a plain click still - // charts the core and selects it alone, so the single-click case looks exactly as - // it did, while a Ctrl or Shift click builds a set the chart has no opinion about. + let editing_input = (editing == Some(group.key)) + .then(|| edit_input.clone()) + .flatten(); + // Highlight follows the body-click selection (chart target). return MoonListItem::new(meta.index) - .selected(selection.contains(Some(core.id))) - .child(core_row( - core, + .selected(chart_selected == Some(group.key)) + .child(server_row( + group, + ip_masked, + entry.is_expanded(), + editing_input, widths, - TreeSelection { - order: &order, - cores: &order_cores, - selection: &selection, - }, &weak_view, &backend, p, app, )); } + } else if let Some(&(server_index, core_index)) = core_positions.get(entry.item().id()) + && let Some(core) = groups + .get(server_index) + .and_then(|group| group.cores.get(core_index)) + { + // The highlight follows the SELECTION now, not the chart: a plain click still + // charts the core and selects it alone, so the single-click case looks exactly as + // it did, while a Ctrl or Shift click builds a set the chart has no opinion about. + return MoonListItem::new(meta.index) + .selected(selection.contains(Some(core.id))) + .child(core_row( + core, + widths, + TreeSelection { + order: &order, + cores: &order_cores, + selection: &selection, + }, + &weak_view, + &backend, + p, + app, + )); } MoonListItem::new(meta.index) .selected(false) diff --git a/crates/moon-ui-gpui/src/panels/core_status/time_offset/tests.rs b/crates/moon-ui-gpui/src/panels/core_status/time_offset/tests.rs index f6f615419..860deaab7 100644 --- a/crates/moon-ui-gpui/src/panels/core_status/time_offset/tests.rs +++ b/crates/moon-ui-gpui/src/panels/core_status/time_offset/tests.rs @@ -48,7 +48,7 @@ fn unknown_never_renders_as_utc() { /// `tz_offset_rank`: every measured value, whatever its sign, sorts before `Unknown`. #[test] fn the_rank_puts_unknown_last() { - let mut cells = vec![ + let mut cells = [ TzOffsetCell::Unknown, TzOffsetCell::Measured { offset_secs: 7_200 }, TzOffsetCell::Measured { diff --git a/crates/moon-ui-gpui/src/panels/detects/mod.rs b/crates/moon-ui-gpui/src/panels/detects/mod.rs index 2458610e1..fc008473e 100644 --- a/crates/moon-ui-gpui/src/panels/detects/mod.rs +++ b/crates/moon-ui-gpui/src/panels/detects/mod.rs @@ -531,9 +531,7 @@ impl DetectsPanel { }) .is_ok() }); - if !alive { - return; - } + if !alive {} }) .detach(); } diff --git a/crates/moon-ui-gpui/src/panels/log/controls.rs b/crates/moon-ui-gpui/src/panels/log/controls.rs index f6536bcf7..22cd0f121 100644 --- a/crates/moon-ui-gpui/src/panels/log/controls.rs +++ b/crates/moon-ui-gpui/src/panels/log/controls.rs @@ -82,7 +82,7 @@ impl LogPanel { LogSource::Aggregate | LogSource::Exchange(_) | LogSource::Local => None, }) .collect(); - let sections = crate::controls::core_menu_sections(&cores, &venues); + let sections = crate::controls::core_menu_sections(&cores, venues); let view = cx.entity(); let mut items = Vec::with_capacity(sources.len() + sections.len() + 1); for (index, item) in sources diff --git a/crates/moon-ui-gpui/src/panels/log/render.rs b/crates/moon-ui-gpui/src/panels/log/render.rs index 3563795d6..3e5f49d8d 100644 --- a/crates/moon-ui-gpui/src/panels/log/render.rs +++ b/crates/moon-ui-gpui/src/panels/log/render.rs @@ -417,7 +417,7 @@ pub(super) fn exchange_core_ids(b: &Backend, group: &str, exchange: ExchangeId) .iter() .map(|server| (server.id, server.group.as_str())), &live, - &venues, + venues, group, exchange, ) diff --git a/crates/moon-ui-gpui/src/panels/orders/controls.rs b/crates/moon-ui-gpui/src/panels/orders/controls.rs index 11db5f7b0..303afe78f 100644 --- a/crates/moon-ui-gpui/src/panels/orders/controls.rs +++ b/crates/moon-ui-gpui/src/panels/orders/controls.rs @@ -51,7 +51,7 @@ impl OrdersPanel { cores, selection, crate::controls::CoreAllRowMode::ImplicitOrComplete, - &t!("orders.all_cores").to_string(), + t!("orders.all_cores").as_ref(), &|n| t!("orders.cores_n", n = n).to_string(), ) .label @@ -83,7 +83,7 @@ impl OrdersPanel { crate::controls::core_combo( "orders-source", cores, - &venues, + venues, selection, crate::controls::CoreAllRowMode::ImplicitOrComplete, t!("orders.all_cores").to_string(), diff --git a/crates/moon-ui-gpui/src/panels/report/actions.rs b/crates/moon-ui-gpui/src/panels/report/actions.rs index f9b1bb73a..73d0bbee7 100644 --- a/crates/moon-ui-gpui/src/panels/report/actions.rs +++ b/crates/moon-ui-gpui/src/panels/report/actions.rs @@ -267,7 +267,7 @@ impl ReportPanel { let prefs = { let backend = self.backend.read(cx); next_prefs_for_period_pick( - crate::persistence::table_persist::report_filters(&backend, &id), + crate::persistence::table_persist::report_filters(backend, &id), period_bucket, picked_period, &super::state::ReportFilterSet { @@ -700,7 +700,7 @@ impl ReportPanel { let indices = selection::ordered_source_indices( &self.cols, &source_indices, - &self.table_state.read(cx), + self.table_state.read(cx), ); if self.selection.len() == 0 || indices.is_empty() { return; @@ -968,7 +968,7 @@ impl ReportPanel { .autohide(false) } }; - let _ = cx.update(|app| { + cx.update(|app| { let _ = handle.update(app, |_, window, app| { use moon_ui::MoonWindowExt as _; window.push_notification(note, app); diff --git a/crates/moon-ui-gpui/src/panels/report/controls.rs b/crates/moon-ui-gpui/src/panels/report/controls.rs index 63396a7b3..7580429b1 100644 --- a/crates/moon-ui-gpui/src/panels/report/controls.rs +++ b/crates/moon-ui-gpui/src/panels/report/controls.rs @@ -536,7 +536,7 @@ impl ReportPanel { &cores, selection, crate::controls::CoreAllRowMode::ImplicitOrComplete, - &t!("report.all_cores").to_string(), + t!("report.all_cores").as_ref(), &|n| t!("report.cores_n", n = n).to_string(), ) .label @@ -557,7 +557,7 @@ impl ReportPanel { let combo = crate::controls::core_combo( "rep-core", &cores, - &venues, + venues, selection, crate::controls::CoreAllRowMode::ImplicitOrComplete, t!("report.all_cores").to_string(), diff --git a/crates/moon-ui-gpui/src/panels/report/export.rs b/crates/moon-ui-gpui/src/panels/report/export.rs index 2d5ba85f8..bff50baed 100644 --- a/crates/moon-ui-gpui/src/panels/report/export.rs +++ b/crates/moon-ui-gpui/src/panels/report/export.rs @@ -341,11 +341,11 @@ fn write_xlsx( } // Write identifier columns as text so Excel cannot convert large i64 values to imprecise // scientific notation. - if id_text_col(name) { - if let Value::Integer(n) = val { - ws.write_string(r, c, n.to_string())?; - continue; - } + if id_text_col(name) + && let Value::Integer(n) = val + { + ws.write_string(r, c, n.to_string())?; + continue; } match val { Value::Integer(n) => { diff --git a/crates/moon-ui-gpui/src/panels/report/query.rs b/crates/moon-ui-gpui/src/panels/report/query.rs index a30e5c9ec..8625e274e 100644 --- a/crates/moon-ui-gpui/src/panels/report/query.rs +++ b/crates/moon-ui-gpui/src/panels/report/query.rs @@ -361,7 +361,7 @@ impl ReportPanel { cx.spawn(async move |this, cx| { let executor = cx.update(|cx| cx.background_executor().clone()); executor.timer(wait).await; - let _ = cx.update(|cx| { + cx.update(|cx| { let _ = this.update(cx, |this, cx| { if this.generation_refresh.timer_fired(timer_token) { cx.notify(); @@ -425,7 +425,7 @@ impl ReportPanel { }) .await; - let _ = cx.update(|cx| { + cx.update(|cx| { let _ = this.update(cx, |this, cx| { if !report_query_result_is_current( request_id, diff --git a/crates/moon-ui-gpui/src/panels/report/state.rs b/crates/moon-ui-gpui/src/panels/report/state.rs index 41c8cc21b..60a965208 100644 --- a/crates/moon-ui-gpui/src/panels/report/state.rs +++ b/crates/moon-ui-gpui/src/panels/report/state.rs @@ -367,7 +367,7 @@ impl ReportPanel { .and_then(|connection| db::load_comment_pane(&connection, detached)) }) .await; - let _ = cx.update(|cx| { + cx.update(|cx| { let _ = this.update(cx, |this, cx| { if this.detached != detached || this.show_comment != expected @@ -405,7 +405,7 @@ impl ReportPanel { let metadata = executor .spawn(async move { ReportInitialMetadata::load() }) .await; - let _ = cx.update(|cx| { + cx.update(|cx| { let _ = this.update(cx, |this, cx| { let ReportInitialMetadata { cores, diff --git a/crates/moon-ui-gpui/src/panels/report/totals.rs b/crates/moon-ui-gpui/src/panels/report/totals.rs index fe4e0e203..4c14f929c 100644 --- a/crates/moon-ui-gpui/src/panels/report/totals.rs +++ b/crates/moon-ui-gpui/src/panels/report/totals.rs @@ -185,16 +185,16 @@ fn traded_volume_amount(volume: &db::TradedVolume) -> Option { // persisted figure for a rate-derived USDT one, and under the current-rate mode take that // mode's wording with it. A unified figure also exists only over a fully reconstructed and // fully valued scope, which is why it can never carry a gap. - if matches!(volume.scope(), db::QuoteScope::Mixed) { - if let Some(usdt) = volume.usdt { - let (visible, exact) = native_volume(usdt, db::QuoteCurrency::usdt()); - return Some(VolumeAmountText { - visible, - exact, - unified: true, - gap: None, - }); - } + if matches!(volume.scope(), db::QuoteScope::Mixed) + && let Some(usdt) = volume.usdt + { + let (visible, exact) = native_volume(usdt, db::QuoteCurrency::usdt()); + return Some(VolumeAmountText { + visible, + exact, + unified: true, + gap: None, + }); } let mut visible = Vec::new(); let mut exact = Vec::new(); @@ -559,37 +559,37 @@ pub(super) fn footer_facts( true, )); } - if unified.is_none() { - if let Some(coverage) = mixed.then_some(totals.valuation).flatten() { - if coverage.eligible_orders > 0 { - tail.push(fact( - t!( - mode.key( - "report.valuation_progress", - "report.valuation_current_progress" - ), - ready = coverage.valued_orders, - total = coverage.eligible_orders - ) - .to_string(), - FactTone::Warn, - false, - )); - } - if coverage.unavailable_orders > 0 { - tail.push(fact( - t!( - mode.key( - "report.valuation_unavailable", - "report.valuation_current_unavailable" - ), - n = coverage.unavailable_orders - ) - .to_string(), - FactTone::Warn, - false, - )); - } + if unified.is_none() + && let Some(coverage) = mixed.then_some(totals.valuation).flatten() + { + if coverage.eligible_orders > 0 { + tail.push(fact( + t!( + mode.key( + "report.valuation_progress", + "report.valuation_current_progress" + ), + ready = coverage.valued_orders, + total = coverage.eligible_orders + ) + .to_string(), + FactTone::Warn, + false, + )); + } + if coverage.unavailable_orders > 0 { + tail.push(fact( + t!( + mode.key( + "report.valuation_unavailable", + "report.valuation_current_unavailable" + ), + n = coverage.unavailable_orders + ) + .to_string(), + FactTone::Warn, + false, + )); } } if let Some(amount) = traded_volume_amount(&totals.traded_volume) { diff --git a/crates/moon-ui-gpui/src/persistence/auto_dock_persist.rs b/crates/moon-ui-gpui/src/persistence/auto_dock_persist.rs index 9944bba65..93d3e2b4c 100644 --- a/crates/moon-ui-gpui/src/persistence/auto_dock_persist.rs +++ b/crates/moon-ui-gpui/src/persistence/auto_dock_persist.rs @@ -10,7 +10,9 @@ use moon_core::config::{paths, write_file_atomic}; use moon_ui::DockTopologyByName; /// Classified startup result for the shared Auto topology file. +// `Loaded` owns the topology read once at startup. Boxing it does not shrink the steady state. #[derive(Clone, Debug, PartialEq)] +#[allow(clippy::large_enum_variant)] pub(crate) enum AutoDockLoad { /// No file exists yet, so first Auto entry may seed and persist the requested default. Missing, diff --git a/crates/moon-ui-gpui/src/persistence/chart_persist.rs b/crates/moon-ui-gpui/src/persistence/chart_persist.rs index 7eaa754d9..ba29346a7 100644 --- a/crates/moon-ui-gpui/src/persistence/chart_persist.rs +++ b/crates/moon-ui-gpui/src/persistence/chart_persist.rs @@ -108,37 +108,27 @@ impl StackOrientation { /// chart prints, and `startup::action_buttons_migration` turns whatever a tab stored here into /// caption rows and clears the key. Nothing writes it again; the type survives so that one pass can /// still read a profile written before the move. -#[derive(Clone, Copy, PartialEq, Eq, Debug, Serialize, Deserialize)] +#[derive(Clone, Copy, PartialEq, Eq, Debug, Serialize, Deserialize, Default)] pub enum ChartBtnPos { Hide, Left, Center, + #[default] Right, } -impl Default for ChartBtnPos { - fn default() -> Self { - ChartBtnPos::Right - } -} - /// Per-tab price-axis position relative to the chart and order book. `Left` places the gutter left /// of the plot and is the historical default. `Right` places it to the right beyond the order book, /// so the axis does not separate the plot from the book. `Hide` removes the axis and returns its /// space to the plot. A missing specification value means `Left`. -#[derive(Clone, Copy, PartialEq, Eq, Debug, Serialize, Deserialize)] +#[derive(Clone, Copy, PartialEq, Eq, Debug, Serialize, Deserialize, Default)] pub enum PriceAxisPos { Hide, + #[default] Left, Right, } -impl Default for PriceAxisPos { - fn default() -> Self { - PriceAxisPos::Left - } -} - /// Persistent state for one chart tab. `num == 0` identifies Main; `num >= 1` identifies numbered /// AddToChart or custom tabs. #[derive(Clone, Serialize, Deserialize)] @@ -338,7 +328,7 @@ impl ChartTabSpec { /// Returns the canonical tab key from `bucket`, or derives it from legacy `core`: Some(core) /// becomes Core and None becomes Shared. pub fn bucket(&self) -> ChartBucket { - self.bucket.clone().unwrap_or_else(|| match self.core { + self.bucket.clone().unwrap_or(match self.core { Some(id) => ChartBucket::Core(id), None => ChartBucket::Shared, }) @@ -388,17 +378,17 @@ pub fn remap_core_ids(specs: &mut [ChartTabSpec], servers: &[ServerConfig]) { }; let mut remapped = 0usize; for spec in specs.iter_mut() { - if let Some(ChartBucket::Core(n)) = spec.bucket { - if let Some(uid) = pos_to_uid(n) { - spec.bucket = Some(ChartBucket::Core(uid)); - remapped += 1; - } + if let Some(ChartBucket::Core(n)) = spec.bucket + && let Some(uid) = pos_to_uid(n) + { + spec.bucket = Some(ChartBucket::Core(uid)); + remapped += 1; } - if let Some(n) = spec.core { - if let Some(uid) = pos_to_uid(n) { - spec.core = Some(uid); - remapped += 1; - } + if let Some(n) = spec.core + && let Some(uid) = pos_to_uid(n) + { + spec.core = Some(uid); + remapped += 1; } } log::info!( diff --git a/crates/moon-ui-gpui/src/persistence/dock_persist.rs b/crates/moon-ui-gpui/src/persistence/dock_persist.rs index 4044a176c..e21dd9433 100644 --- a/crates/moon-ui-gpui/src/persistence/dock_persist.rs +++ b/crates/moon-ui-gpui/src/persistence/dock_persist.rs @@ -130,10 +130,10 @@ pub(crate) fn save_all_to_path(map: &DockMap, path: &Path) -> bool { /// /// Missing, non-panel, or non-string metadata falls back to the empty group name. fn group_of(info: &PanelInfo) -> String { - if let PanelInfo::Panel(v) = info { - if let Some(g) = v.get("group").and_then(|g| g.as_str()) { - return g.to_string(); - } + if let PanelInfo::Panel(v) = info + && let Some(g) = v.get("group").and_then(|g| g.as_str()) + { + return g.to_string(); } String::new() } diff --git a/crates/moon-ui-gpui/src/screener/view.rs b/crates/moon-ui-gpui/src/screener/view.rs index f9ace62e0..13a517505 100644 --- a/crates/moon-ui-gpui/src/screener/view.rs +++ b/crates/moon-ui-gpui/src/screener/view.rs @@ -239,10 +239,10 @@ impl ScreenerView { continue; } names.insert(s.id, SharedString::from(s.name.clone())); - if let ScrSource::Core(only) = self.source { - if s.id != only { - continue; - } + if let ScrSource::Core(only) = self.source + && s.id != only + { + continue; } let Some(provider) = source.provider_of(s.id) else { continue; @@ -509,7 +509,7 @@ impl ScreenerView { .map(|(_, n)| n.clone()) .unwrap_or_else(|| format!("#{id}")), }; - let sections = crate::controls::core_menu_sections(&cores, &venues); + let sections = crate::controls::core_menu_sections(&cores, venues); let view = cx.entity(); let all_view = view.clone(); let mut items = vec![ @@ -764,13 +764,12 @@ pub fn open( cx: &mut App, ) { // Focus an existing live window instead of opening a duplicate. - if let Some(handle) = backend.read(cx).screener_window { - if handle + if let Some(handle) = backend.read(cx).screener_window + && handle .update(cx, |_, window, _| window.activate_window()) .is_ok() - { - return; - } + { + return; } let saved = backend.read(cx).layout.screener_window; let bounds = saved.map_or( diff --git a/crates/moon-ui-gpui/src/settings/badges.rs b/crates/moon-ui-gpui/src/settings/badges.rs index df6cf2993..51ee9e335 100644 --- a/crates/moon-ui-gpui/src/settings/badges.rs +++ b/crates/moon-ui-gpui/src/settings/badges.rs @@ -54,11 +54,11 @@ fn badge_input( if matches!(ev, MoonInputEvent::Change) { let val = emitter.read(cx).value().to_string(); this.backend.update(cx, |b, bcx| { - if let Some(p) = b.preview.as_mut() { - if let Some(e) = p.badges.entries.get_mut(idx) { - apply(e, val); - bcx.notify(); - } + if let Some(p) = b.preview.as_mut() + && let Some(e) = p.badges.entries.get_mut(idx) + { + apply(e, val); + bcx.notify(); } }); } @@ -94,11 +94,11 @@ fn entry_color( }; super::draft_color(window, cx, init, move |p, cc| { let is_light = p.ui_theme_mode.is_light(); - if let Some(e) = p.badges.entries.get_mut(idx) { - if get(e, is_light) != cc { - set(e, is_light, cc); - return true; - } + if let Some(e) = p.badges.entries.get_mut(idx) + && get(e, is_light) != cc + { + set(e, is_light, cc); + return true; } false }) @@ -233,11 +233,11 @@ impl SettingsView { /// Delete draft entry `idx`, then rebuild editor state. fn delete_badge(&mut self, idx: usize, window: &mut Window, cx: &mut Context) { self.backend.update(cx, |b, bcx| { - if let Some(p) = b.preview.as_mut() { - if idx < p.badges.entries.len() { - p.badges.entries.remove(idx); - bcx.notify(); - } + if let Some(p) = b.preview.as_mut() + && idx < p.badges.entries.len() + { + p.badges.entries.remove(idx); + bcx.notify(); } }); self.badges = build(&self.backend, window, cx); @@ -280,11 +280,11 @@ impl SettingsView { SharedString::from(format!("badge-active-{idx}")), active, move |p, v| { - if let Some(e) = p.badges.entries.get_mut(idx) { - if e.active != v { - e.active = v; - return true; - } + if let Some(e) = p.badges.entries.get_mut(idx) + && e.active != v + { + e.active = v; + return true; } false }, @@ -297,11 +297,11 @@ impl SettingsView { SharedString::from(format!("badge-dist-{idx}")), distinguish, move |p, v| { - if let Some(e) = p.badges.entries.get_mut(idx) { - if e.distinguish_dir != v { - e.distinguish_dir = v; - return true; - } + if let Some(e) = p.badges.entries.get_mut(idx) + && e.distinguish_dir != v + { + e.distinguish_dir = v; + return true; } false }, @@ -314,11 +314,11 @@ impl SettingsView { SharedString::from(format!("badge-outline-{idx}")), outline, move |p, v| { - if let Some(e) = p.badges.entries.get_mut(idx) { - if e.outline != v { - e.outline = v; - return true; - } + if let Some(e) = p.badges.entries.get_mut(idx) + && e.outline != v + { + e.outline = v; + return true; } false }, diff --git a/crates/moon-ui-gpui/src/settings/common.rs b/crates/moon-ui-gpui/src/settings/common.rs index c9648d12f..22a16d3c2 100644 --- a/crates/moon-ui-gpui/src/settings/common.rs +++ b/crates/moon-ui-gpui/src/settings/common.rs @@ -231,10 +231,10 @@ pub(super) fn draft_color( }; let c = hsla_u8(*h); this.backend.update(cx, |b, bcx| { - if let Some(p) = b.preview.as_mut() { - if apply(p, c) { - bcx.notify(); - } + if let Some(p) = b.preview.as_mut() + && apply(p, c) + { + bcx.notify(); } }); }) @@ -300,10 +300,10 @@ pub(super) fn draft_slider_on( let f = f.end(); let f = if f == 0.0 { 0.0 } else { f }; this.backend.update(cx, |b, bcx| { - if let Some(p) = b.preview.as_mut() { - if apply(p, f, bcx) { - bcx.notify(); - } + if let Some(p) = b.preview.as_mut() + && apply(p, f, bcx) + { + bcx.notify(); } }); }) diff --git a/crates/moon-ui-gpui/src/settings/connections/mod.rs b/crates/moon-ui-gpui/src/settings/connections/mod.rs index 84ed3a312..0302eda3f 100644 --- a/crates/moon-ui-gpui/src/settings/connections/mod.rs +++ b/crates/moon-ui-gpui/src/settings/connections/mod.rs @@ -197,16 +197,15 @@ fn conn_input( if matches!(ev, MoonInputEvent::Change) { let val = emitter.read(cx).value().to_string(); this.backend.update(cx, |b, bcx| { - if let Some(p) = b.preview.as_mut() { - if let Some(s) = p.servers.get_mut(i) { - if get(s) != val { - set(s, val); - if sync_groups { - sync_groups_from_servers(&p.servers, &mut p.groups); - } - bcx.notify(); - } + if let Some(p) = b.preview.as_mut() + && let Some(s) = p.servers.get_mut(i) + && get(s) != val + { + set(s, val); + if sync_groups { + sync_groups_from_servers(&p.servers, &mut p.groups); } + bcx.notify(); } }); // This keystroke may have re-ranked the list under the field being typed into; the @@ -233,11 +232,11 @@ fn conn_color( init: [u8; 3], ) -> Entity { super::draft_color(window, cx, init, move |p, c| { - if let Some(s) = p.servers.get_mut(i) { - if s.color != c { - s.color = c; - return true; - } + if let Some(s) = p.servers.get_mut(i) + && s.color != c + { + s.color = c; + return true; } false }) diff --git a/crates/moon-ui-gpui/src/settings/connections/tab.rs b/crates/moon-ui-gpui/src/settings/connections/tab.rs index 00846a701..dbeaaba0c 100644 --- a/crates/moon-ui-gpui/src/settings/connections/tab.rs +++ b/crates/moon-ui-gpui/src/settings/connections/tab.rs @@ -334,11 +334,11 @@ fn group_header_row( let n = nm_act.clone(); let _ = weak.update(cx, |this, ctx| { this.backend.update(ctx, |b, bcx| { - if let Some(p) = b.preview.as_mut() { - if let Some(gc) = p.groups.iter_mut().find(|g| g.name == n) { - gc.active = v; - bcx.notify(); - } + if let Some(p) = b.preview.as_mut() + && let Some(gc) = p.groups.iter_mut().find(|g| g.name == n) + { + gc.active = v; + bcx.notify(); } }); ctx.notify(); @@ -468,11 +468,11 @@ fn icon_picker_grid( let n = nm.clone(); let _ = weak_ico.update(cx, |this, ctx| { this.backend.update(ctx, |b, bcx| { - if let Some(p) = b.preview.as_mut() { - if let Some(g) = p.groups.iter_mut().find(|g| g.name == n) { - g.icon = id; - bcx.notify(); - } + if let Some(p) = b.preview.as_mut() + && let Some(g) = p.groups.iter_mut().find(|g| g.name == n) + { + g.icon = id; + bcx.notify(); } }); this.picking = None; @@ -787,7 +787,7 @@ impl SettingsView { }; let st = factory_status.get(core_id).cloned(); let built = server_row( - &view_ref, + view_ref, &factory_weak, row, *draft_index, diff --git a/crates/moon-ui-gpui/src/settings/connections/table.rs b/crates/moon-ui-gpui/src/settings/connections/table.rs index 01a46f169..29ba9b2a0 100644 --- a/crates/moon-ui-gpui/src/settings/connections/table.rs +++ b/crates/moon-ui-gpui/src/settings/connections/table.rs @@ -209,11 +209,11 @@ fn draft_checkbox_weak( let _ = weak.update(cx, |this, ctx| { let changed = this.backend.update(ctx, |b, bcx| { let mut changed = false; - if let Some(p) = b.preview.as_mut() { - if apply(p, v) { - bcx.notify(); - changed = true; - } + if let Some(p) = b.preview.as_mut() + && apply(p, v) + { + bcx.notify(); + changed = true; } changed }); @@ -260,11 +260,11 @@ fn srv_check( .unwrap_or(false) }; let mut checkbox = draft_checkbox_weak(weak, id, cur, move |p, v| { - if let Some(s) = p.servers.get_mut(i) { - if get(s) != v { - set(s, v); - return true; - } + if let Some(s) = p.servers.get_mut(i) + && get(s) != v + { + set(s, v); + return true; } false }); @@ -331,19 +331,19 @@ fn paste_key_affix( key_state.update(cx, |st, c| st.set_value(text.clone(), window, c)); let _ = weak.update(cx, |this, ctx| { this.backend.update(ctx, |b, bcx| { - if let Some(pv) = b.preview.as_mut() { - if let Some(s) = pv.servers.get_mut(i) { - s.transport = moon_core::config::seeded_transport(s.transport, &text); - s.key = Secret::new(text.clone()); - // A new key can point this row at a DIFFERENT Moonbot, and strategy ids - // are unique per host, not globally — so the pinned id would silently - // name whatever strategy inherited that number there. The NAME survives - // and re-pins itself against the new host's list. - if let Some(manual) = s.manual_strategy.as_mut() { - manual.id = 0; - } - bcx.notify(); + if let Some(pv) = b.preview.as_mut() + && let Some(s) = pv.servers.get_mut(i) + { + s.transport = moon_core::config::seeded_transport(s.transport, &text); + s.key = Secret::new(text.clone()); + // A new key can point this row at a DIFFERENT Moonbot, and strategy ids + // are unique per host, not globally — so the pinned id would silently + // name whatever strategy inherited that number there. The NAME survives + // and re-pins itself against the new host's list. + if let Some(manual) = s.manual_strategy.as_mut() { + manual.id = 0; } + bcx.notify(); } }); // A nonempty-to-nonempty paste must refresh every duplicate marker. @@ -502,12 +502,12 @@ impl SettingsView { /// Nothing; the method removes the row when it exists and clears row-owned transient state. pub(super) fn delete_server(&mut self, i: usize, window: &mut Window, cx: &mut Context) { self.backend.update(cx, |b, bcx| { - if let Some(p) = b.preview.as_mut() { - if i < p.servers.len() { - p.servers.remove(i); - sync_groups_from_servers(&p.servers, &mut p.groups); - bcx.notify(); - } + if let Some(p) = b.preview.as_mut() + && i < p.servers.len() + { + p.servers.remove(i); + sync_groups_from_servers(&p.servers, &mut p.groups); + bcx.notify(); } }); let rows = build_conn(&self.backend, window, cx); @@ -581,7 +581,7 @@ fn feed_popover( let feed = { let b = view.backend.read(cx); let s = b.preview.as_ref().unwrap_or(&b.config).servers.get(i); - s.map(|s| s.feed.clone()).unwrap_or_default() + s.map(|s| s.feed).unwrap_or_default() }; let on = FEED_FLAGS.iter().filter(|(_, g, _)| g(&feed)).count(); let tinted = on < FEED_FLAGS.len(); @@ -610,11 +610,11 @@ fn feed_popover( }) .on_click(move |_, _, cx| { backend.update(cx, |b, bcx| { - if let Some(p) = b.preview.as_mut() { - if let Some(s) = p.servers.get_mut(i) { - set(&mut s.feed, !cur); - bcx.notify(); - } + if let Some(p) = b.preview.as_mut() + && let Some(s) = p.servers.get_mut(i) + { + set(&mut s.feed, !cur); + bcx.notify(); } }); }), diff --git a/crates/moon-ui-gpui/src/settings/general.rs b/crates/moon-ui-gpui/src/settings/general.rs index e54417218..99ad083ae 100644 --- a/crates/moon-ui-gpui/src/settings/general.rs +++ b/crates/moon-ui-gpui/src/settings/general.rs @@ -157,14 +157,14 @@ impl SettingsView { fn adjust_idle(&mut self, delta: i32, cx: &mut Context) { let changed = self.backend.update(cx, |b, bcx| { let mut changed = false; - if let Some(p) = b.preview.as_mut() { - if p.main_idle_close_secs > 0 { - let v = (p.main_idle_close_secs as i32 + delta).clamp(5, 3600) as u32; - if p.main_idle_close_secs != v { - p.main_idle_close_secs = v; - bcx.notify(); - changed = true; - } + if let Some(p) = b.preview.as_mut() + && p.main_idle_close_secs > 0 + { + let v = (p.main_idle_close_secs as i32 + delta).clamp(5, 3600) as u32; + if p.main_idle_close_secs != v { + p.main_idle_close_secs = v; + bcx.notify(); + changed = true; } } changed diff --git a/crates/moon-ui-gpui/src/settings/hotkeys/tab.rs b/crates/moon-ui-gpui/src/settings/hotkeys/tab.rs index d65f4cd91..cdfdd178c 100644 --- a/crates/moon-ui-gpui/src/settings/hotkeys/tab.rs +++ b/crates/moon-ui-gpui/src/settings/hotkeys/tab.rs @@ -443,11 +443,11 @@ impl SettingsView { .on_hover(cx.listener(move |this, entered: &bool, _window, cx| { let next = entered.then(|| row_id.clone()); // A leave clears only this row: the next row's enter may already have landed. - if *entered || this.hotkeys_hover_row.as_deref() == Some(row_id.as_str()) { - if this.hotkeys_hover_row != next { - this.hotkeys_hover_row = next; - cx.notify(); - } + if (*entered || this.hotkeys_hover_row.as_deref() == Some(row_id.as_str())) + && this.hotkeys_hover_row != next + { + this.hotkeys_hover_row = next; + cx.notify(); } })) .w_full() diff --git a/crates/moon-ui-gpui/src/settings/import_preview.rs b/crates/moon-ui-gpui/src/settings/import_preview.rs index 4d00302ae..28d3e1fc3 100644 --- a/crates/moon-ui-gpui/src/settings/import_preview.rs +++ b/crates/moon-ui-gpui/src/settings/import_preview.rs @@ -34,6 +34,9 @@ pub(super) struct ImportReady { } /// State of an open import preview. +// `Ready` owns the import plan for the open dialog. Boxing it allocates on that cold path only +// after every match site changes, which is a wider edit than this lint. +#[allow(clippy::large_enum_variant)] pub(super) enum ImportState { /// The clipboard passed the MoonBot sniff and is being parsed and planned in the background. Loading, @@ -125,7 +128,7 @@ impl SettingsView { Ok::<_, moonbot_import::ImportError>(plan) }) .await; - let _ = cx.update(|cx| { + cx.update(|cx| { let _ = this.update(cx, |this, cx| { // Do not resurrect the preview if the settings window closed during loading. if !matches!(this.import, Some(ImportState::Loading)) { @@ -360,11 +363,11 @@ impl SettingsView { .checked(*on) .label(name.clone()) .on_change(cx.listener(move |this, ch: &bool, _, cx| { - if let Some(ImportState::Ready(st)) = this.import.as_mut() { - if let Some(c) = st.cores.get_mut(idx) { - c.2 = *ch; - cx.notify(); - } + if let Some(ImportState::Ready(st)) = this.import.as_mut() + && let Some(c) = st.cores.get_mut(idx) + { + c.2 = *ch; + cx.notify(); } })), ); diff --git a/crates/moon-ui-gpui/src/settings/mod.rs b/crates/moon-ui-gpui/src/settings/mod.rs index 326c0fda3..4ba38954c 100644 --- a/crates/moon-ui-gpui/src/settings/mod.rs +++ b/crates/moon-ui-gpui/src/settings/mod.rs @@ -317,11 +317,11 @@ impl SettingsView { let v = *ch; let changed = this.backend.update(cx, |b, bcx| { let mut changed = false; - if let Some(p) = b.preview.as_mut() { - if apply(p, v) { - bcx.notify(); - changed = true; - } + if let Some(p) = b.preview.as_mut() + && apply(p, v) + { + bcx.notify(); + changed = true; } changed }); @@ -936,13 +936,12 @@ pub fn open_on_tab( tab: Tab, cx: &mut App, ) { - if let Some(handle) = backend.read(cx).settings_window { - if handle + if let Some(handle) = backend.read(cx).settings_window + && handle .update(cx, |_, window, _| window.activate_window()) .is_ok() - { - return; - } + { + return; } if backend.read(cx).preview.is_some() { return; diff --git a/crates/moon-ui-gpui/src/settings/storage.rs b/crates/moon-ui-gpui/src/settings/storage.rs index c261907b2..fb17fd4d4 100644 --- a/crates/moon-ui-gpui/src/settings/storage.rs +++ b/crates/moon-ui-gpui/src/settings/storage.rs @@ -117,7 +117,7 @@ impl SettingsView { cx.spawn(async move |this, cx| { let executor = cx.update(|cx| cx.background_executor().clone()); let info = executor.spawn(async move { collect_info() }).await; - let _ = cx.update(|cx| { + cx.update(|cx| { let _ = this.update(cx, |this, cx| { this.storage.inflight = false; this.storage.info = Some(info); @@ -149,7 +149,7 @@ impl SettingsView { cx.spawn(async move |this, cx| { let executor = cx.update(|cx| cx.background_executor().clone()); let result = executor.spawn(async move { job() }).await; - let _ = cx.update(|cx| { + cx.update(|cx| { let _ = this.update(cx, |this, cx| { this.storage.busy = false; this.status = Some(match result { @@ -244,7 +244,7 @@ impl SettingsView { /// 60, seconds otherwise. 65 s is a step and must not read as "1 min", which is what 60 s /// already says. fn trades_margin_label(secs: u32) -> String { - if secs % 60 == 0 { + if secs.is_multiple_of(60) { t!("storage.trades_min", min = secs / 60).to_string() } else { t!("storage.trades_sec", s = secs).to_string() diff --git a/crates/moon-ui-gpui/src/settings/telegram.rs b/crates/moon-ui-gpui/src/settings/telegram.rs index e469f70e2..1db0e41f7 100644 --- a/crates/moon-ui-gpui/src/settings/telegram.rs +++ b/crates/moon-ui-gpui/src/settings/telegram.rs @@ -84,11 +84,11 @@ pub(super) fn build( if matches!(ev, MoonInputEvent::Change) { let val = emitter.read(cx).value().to_string(); this.backend.update(cx, |b, bcx| { - if let Some(p) = b.preview.as_mut() { - if p.telegram.token.expose() != val { - p.telegram.token = Secret::new(val); - bcx.notify(); - } + if let Some(p) = b.preview.as_mut() + && p.telegram.token.expose() != val + { + p.telegram.token = Secret::new(val); + bcx.notify(); } }); } diff --git a/crates/moon-ui-gpui/src/shell/actions.rs b/crates/moon-ui-gpui/src/shell/actions.rs index 16026e16b..caeae12a9 100644 --- a/crates/moon-ui-gpui/src/shell/actions.rs +++ b/crates/moon-ui-gpui/src/shell/actions.rs @@ -329,7 +329,8 @@ impl Shell { ) -> bool { use crate::hotkeys::HotkeyAction; let group = self.group.clone(); - let handled = match action { + + match action { HotkeyAction::SuperZoomIn | HotkeyAction::SuperZoomOut => { self.backend.update(cx, |b, bcx| { b.super_zoom_group = Some(group.clone()); @@ -452,8 +453,7 @@ impl Shell { crate::hotkeys::apply(other, b, bcx, &group, target, active_core) }) } - }; - handled + } } } diff --git a/crates/moon-ui-gpui/src/shell/core_settings_popup/widgets.rs b/crates/moon-ui-gpui/src/shell/core_settings_popup/widgets.rs index 1312606de..911e90a9b 100644 --- a/crates/moon-ui-gpui/src/shell/core_settings_popup/widgets.rs +++ b/crates/moon-ui-gpui/src/shell/core_settings_popup/widgets.rs @@ -143,10 +143,10 @@ pub(super) fn cs_checkbox( .on_change(move |ch: &bool, _w, app| { let on = *ch; let b = backend.read(app); - if let Some(core) = resolve_core_settings_write(seeded, b.active_trade_core(&group)) { - if let Err(e) = b.session.edit_client_settings(core, edit(on)) { - log::warn!("core settings edit failed: {e:#}"); - } + if let Some(core) = resolve_core_settings_write(seeded, b.active_trade_core(&group)) + && let Err(e) = b.session.edit_client_settings(core, edit(on)) + { + log::warn!("core settings edit failed: {e:#}"); } }) } diff --git a/crates/moon-ui-gpui/src/shell/init.rs b/crates/moon-ui-gpui/src/shell/init.rs index a1cb7c228..36a686572 100644 --- a/crates/moon-ui-gpui/src/shell/init.rs +++ b/crates/moon-ui-gpui/src/shell/init.rs @@ -357,14 +357,15 @@ impl Shell { let Some((group, ix)) = this.size_edit.as_ref() else { return; }; - if let Ok(v) = inp.read(cx).value().trim().replace(',', ".").parse::() { - if v > 0.0 && *ix < 6 { - this.backend.update(cx, |b, bcx| { - b.set_order_size_value(group, *ix, v); - b.order_size_rev = b.order_size_rev.wrapping_add(1); - bcx.notify(); - }); - } + if let Ok(v) = inp.read(cx).value().trim().replace(',', ".").parse::() + && v > 0.0 + && *ix < 6 + { + this.backend.update(cx, |b, bcx| { + b.set_order_size_value(group, *ix, v); + b.order_size_rev = b.order_size_rev.wrapping_add(1); + bcx.notify(); + }); } return; } @@ -375,18 +376,19 @@ impl Shell { return; }; let raw = inp.read(cx).value().to_string(); - if let Ok(v) = raw.trim().replace(',', ".").parse::() { - if v > 0.0 && ix < 6 { - this.backend.update(cx, |b, bcx| { - b.set_order_size_value(&group, ix, v); - if let Err(error) = b.config.save() { - log::warn!("save order size failed: {error}"); - } else { - b.config_dirty = false; - } - bcx.notify(); - }); - } + if let Ok(v) = raw.trim().replace(',', ".").parse::() + && v > 0.0 + && ix < 6 + { + this.backend.update(cx, |b, bcx| { + b.set_order_size_value(&group, ix, v); + if let Err(error) = b.config.save() { + log::warn!("save order size failed: {error}"); + } else { + b.config_dirty = false; + } + bcx.notify(); + }); } cx.notify(); }) @@ -405,20 +407,20 @@ impl Shell { }; if let Ok(v) = inp.read(cx).value().trim().replace(',', ".").parse::() && v.is_finite() + && v >= 0.0 + && ix < 6 { - if v >= 0.0 && ix < 6 { - this.backend.update(cx, |b, bcx| { - b.edit_group_exit( - &group, - ClientSettingsEdit::SetFixedSellPct { - slot: ix + 1, - pct: v, - }, - ); - b.order_size_rev = b.order_size_rev.wrapping_add(1); - bcx.notify(); - }); - } + this.backend.update(cx, |b, bcx| { + b.edit_group_exit( + &group, + ClientSettingsEdit::SetFixedSellPct { + slot: ix + 1, + pct: v, + }, + ); + b.order_size_rev = b.order_size_rev.wrapping_add(1); + bcx.notify(); + }); } cx.notify(); }) diff --git a/crates/moon-ui-gpui/src/shell/ticker.rs b/crates/moon-ui-gpui/src/shell/ticker.rs index 3bebb7edc..45ce1be23 100644 --- a/crates/moon-ui-gpui/src/shell/ticker.rs +++ b/crates/moon-ui-gpui/src/shell/ticker.rs @@ -76,7 +76,7 @@ impl Shell { let query = self.ticker_input.read(cx).value().to_string(); let results = { let b = self.backend.read(cx); - coin_search::search(&b, &self.group, None, &query) + coin_search::search(b, &self.group, None, &query) }; let backend = self.backend.clone(); let view = cx.entity(); diff --git a/crates/moon-ui-gpui/src/shell/workspace.rs b/crates/moon-ui-gpui/src/shell/workspace.rs index d221a8de3..39ac53b7c 100644 --- a/crates/moon-ui-gpui/src/shell/workspace.rs +++ b/crates/moon-ui-gpui/src/shell/workspace.rs @@ -9,7 +9,7 @@ use gpui::*; use moon_core::config::{ AUTO_WORKSPACE_RAIL_WIDTH_MAX, AUTO_WORKSPACE_RAIL_WIDTH_MIN, WorkspaceMode, }; -use moon_core::feed::{ConnStatus, CoreStartupStatus}; +use moon_core::feed::ConnStatus; use moon_core::session::CoreId; use moon_core::venue::CoreVenue; use moon_ui::{ @@ -103,11 +103,11 @@ fn auto_workspace_activation_fallback(activated: bool) -> Option<&'static str> { /// /// Returns: /// The eligible name to persist, or `None` for Classic, programmatic, or ineligible events. -pub(super) fn auto_workspace_tab_to_persist<'a>( +pub(super) fn auto_workspace_tab_to_persist( auto: bool, applying_topology: bool, - panel_name: &'a str, -) -> Option<&'a str> { + panel_name: &str, +) -> Option<&str> { (auto && !applying_topology && auto_workspace_tab_is_eligible(panel_name)).then_some(panel_name) } @@ -442,7 +442,9 @@ fn icon_workspace_summary(configured: usize) -> String { } /// One flattened virtual-list item in the all-core rail. +// `Exchange` holds the logo and the core list for one heading. The rail rebuilds these by value. #[derive(Clone)] +#[allow(clippy::large_enum_variant)] enum RailItem { /// Current group aggregate scope. Overview { selected: bool }, @@ -583,7 +585,7 @@ impl Shell { cx.spawn(async move |this, cx| { cx.background_spawn(async { crate::media::exchange_logos::prewarm() }) .await; - let _ = cx.update(|cx| { + cx.update(|cx| { let _ = this.update(cx, |this, cx| { this.exchange_logos_ready = true; cx.notify(); @@ -1088,9 +1090,7 @@ impl Shell { availability: backend.workspace_core_availability(&server.group, server.id), ready: core.is_some_and(|core| core.status == ConnStatus::Ready), connection: core.map(|core| core.status.clone()), - startup: core - .map(|core| core.startup) - .unwrap_or_else(CoreStartupStatus::default), + startup: core.map(|core| core.startup).unwrap_or_default(), fault: core.and_then(|core| core.fault.clone()), // Fed the WHOLE fleet, not the membership-filtered rows below: a hidden // core still connects, so a transport suggestion computed over a subset @@ -1467,7 +1467,7 @@ fn render_rail_item( let controls = (run.exchange_controls && density != WorkspaceRailDensity::Icon && !cores.is_empty()) - .then(|| RunScope { + .then_some(RunScope { key: RunKey::Section(section), cores, reserve: run.slots, diff --git a/crates/moon-ui-gpui/src/startup/boot.rs b/crates/moon-ui-gpui/src/startup/boot.rs index 4bf8930b3..308ff4a46 100644 --- a/crates/moon-ui-gpui/src/startup/boot.rs +++ b/crates/moon-ui-gpui/src/startup/boot.rs @@ -675,8 +675,8 @@ pub(super) fn boot(cfg: AppConfig, input: BootInput, cx: &mut App) { 20_000, ); cx.update(|cx| { - if let Some(guard) = &coord_instance { - if super::instance::poll_activation(guard) { + if let Some(guard) = &coord_instance + && super::instance::poll_activation(guard) { let handles: Vec<_> = coord_backend .read(cx) .group_windows @@ -687,7 +687,6 @@ pub(super) fn boot(cfg: AppConfig, input: BootInput, cx: &mut App) { let _ = handle.update(cx, |_, window, _| window.activate_window()); } } - } let mut edges = TickEdges::default(); consume_report_commit(coord_report_immediate_dirty.as_deref(), || { edges.immediate_report = true; @@ -703,11 +702,10 @@ pub(super) fn boot(cfg: AppConfig, input: BootInput, cx: &mut App) { }); let revision = report_revision_gate.observe(edges, Instant::now()); let (show_reqs, open_debug_10) = coord_backend.update(cx, |b, cx| { - if revision.wake_valuation { - if let Some(valuation) = &b.valuation { + if revision.wake_valuation + && let Some(valuation) = &b.valuation { valuation.wake(); } - } b.tick_telegram(cx); // The startup cleanup of the trade tape, then the tape autoload of the // tuner's Entry/Exit axis — in that order, the autoload waits for the diff --git a/crates/moon-ui-gpui/src/startup/strategy_filters_migration.rs b/crates/moon-ui-gpui/src/startup/strategy_filters_migration.rs index f72006ced..ea7196ce9 100644 --- a/crates/moon-ui-gpui/src/startup/strategy_filters_migration.rs +++ b/crates/moon-ui-gpui/src/startup/strategy_filters_migration.rs @@ -79,10 +79,10 @@ pub(super) fn migrate_strategy_filters( ); } } - if let Some(mut cfg) = layout.stored_chart_labels(ChartTabKind::AddTo).cloned() { - if append_if_missing(&mut cfg) == Some(true) { - layout.store_chart_labels(ChartTabKind::AddTo, cfg); - } + if let Some(mut cfg) = layout.stored_chart_labels(ChartTabKind::AddTo).cloned() + && append_if_missing(&mut cfg) == Some(true) + { + layout.store_chart_labels(ChartTabKind::AddTo, cfg); } for spec in specs.iter_mut() { let kind = crate::chart_tabs::apply_all::spec_kind(spec); diff --git a/crates/moon-ui-gpui/src/startup/tests.rs b/crates/moon-ui-gpui/src/startup/tests.rs index affa11ded..3cf3b3cfd 100644 --- a/crates/moon-ui-gpui/src/startup/tests.rs +++ b/crates/moon-ui-gpui/src/startup/tests.rs @@ -294,7 +294,7 @@ fn report_revision_decision_stays_wired_to_the_coordination_loop() { "consume_report_commit(coord_valuation_status_dirty.as_deref()", ); let observe = position(&source, "let revision = report_revision_gate.observe("); - let wake = position(&source, "if revision.wake_valuation {"); + let wake = position(&source, "if revision.wake_valuation"); let notify = position(&source, "if revision.notify {"); assert!(immediate < background); diff --git a/crates/moon-ui-gpui/src/strategies/actions.rs b/crates/moon-ui-gpui/src/strategies/actions.rs index c17071c84..9867dc7d5 100644 --- a/crates/moon-ui-gpui/src/strategies/actions.rs +++ b/crates/moon-ui-gpui/src/strategies/actions.rs @@ -202,10 +202,11 @@ impl StrategiesView { } // A removed strategy changes the captured identity just as surely as a hidden core. Check // every row before the first per-core command so no surviving subset can be sent. - if { + let res = { let store = self.backend.read(cx).session.store(); !strategy_targets_exist(&plan.targets, |(core, id)| row(store, core, id).is_some()) - } { + }; + if res { return; } let applied_cores: HashSet = plan.actions.iter().map(|(core, _)| *core).collect(); @@ -447,10 +448,11 @@ impl StrategiesView { { return; } - if { + let res = { let store = self.backend.read(cx).session.store(); !strategy_targets_exist(&plan.targets, |(core, id)| row(store, core, id).is_some()) - } { + }; + if res { return; } // Drafts the core would refuse are dropped HERE rather than in the captured plan: sending diff --git a/crates/moon-ui-gpui/src/strategies/fields.rs b/crates/moon-ui-gpui/src/strategies/fields.rs index 4e7ef3d15..6c81311fe 100644 --- a/crates/moon-ui-gpui/src/strategies/fields.rs +++ b/crates/moon-ui-gpui/src/strategies/fields.rs @@ -80,10 +80,10 @@ impl StrategiesView { cx: &mut Context, ) -> Entity { let rgb_val = parse_hex_rgb(hex); - if let Some((cached, state)) = self.field_colors.get(&id) { - if *cached == rgb_val { - return state.clone(); - } + if let Some((cached, state)) = self.field_colors.get(&id) + && *cached == rgb_val + { + return state.clone(); } let state = crate::controls::color_picker::shared_color_state(rgb_val, window, cx); let prefix = hex_alpha_prefix(hex); diff --git a/crates/moon-ui-gpui/src/strategies/full_params.rs b/crates/moon-ui-gpui/src/strategies/full_params.rs index c11a27dda..c1a6010e2 100644 --- a/crates/moon-ui-gpui/src/strategies/full_params.rs +++ b/crates/moon-ui-gpui/src/strategies/full_params.rs @@ -117,11 +117,11 @@ impl StrategiesView { _window: &mut Window, cx: &mut Context, ) -> AnyElement { - if let Some(section) = self.pending_param_scroll.take() { - if let Some(ix) = resolve_scroll_target(&flat, section) { - self.params_scroll - .scroll_to_item_strict(ix, ScrollStrategy::Top); - } + if let Some(section) = self.pending_param_scroll.take() + && let Some(ix) = resolve_scroll_target(&flat, section) + { + self.params_scroll + .scroll_to_item_strict(ix, ScrollStrategy::Top); } let frozen = self.viewing_version(); diff --git a/crates/moon-ui-gpui/src/strategies/param_entries.rs b/crates/moon-ui-gpui/src/strategies/param_entries.rs index d43fe8cae..ecf76f428 100644 --- a/crates/moon-ui-gpui/src/strategies/param_entries.rs +++ b/crates/moon-ui-gpui/src/strategies/param_entries.rs @@ -96,18 +96,18 @@ pub(super) fn flatten_params( if multi && lname == "strategyname" { continue; } - if let Some(c) = common { - if !c.contains(&lname) { - continue; - } + if let Some(c) = common + && !c.contains(&lname) + { + continue; } if differ && lname == "signaltype" { continue; } - if let Some(ch) = changed { - if !ch.contains_key(&lname) { - continue; - } + if let Some(ch) = changed + && !ch.contains_key(&lname) + { + continue; } fields.push(f.clone()); } diff --git a/crates/moon-ui-gpui/src/strategies/params.rs b/crates/moon-ui-gpui/src/strategies/params.rs index 86075dc75..4dd5deab7 100644 --- a/crates/moon-ui-gpui/src/strategies/params.rs +++ b/crates/moon-ui-gpui/src/strategies/params.rs @@ -1047,6 +1047,8 @@ fn compact_first_line(v: &str) -> String { } } +// `Content` owns the prepared parameter body. Boxing it allocates on every selection change. +#[allow(clippy::large_enum_variant)] pub(super) enum ParamsPanelModel { NoSelection, NoSchema, @@ -1344,16 +1346,16 @@ impl StrategiesView { // total: Apply and Revert both empty `field_edits` for it. Only `Staged` is retired this // way — `ClearedOnly` has already discarded stale drafts and `Identical` never had any, // so testing either here would vanish the note the very frame it is set. - if let Some((key, outcome, _)) = self.versions.staged_note { - if matches!(outcome, StagedOutcome::Staged(_)) { - let remaining = self - .field_edits - .keys() - .filter(|(core, id, _)| (*core, *id) == key) - .count(); - if remaining == 0 { - self.versions.staged_note = None; - } + if let Some((key, outcome, _)) = self.versions.staged_note + && matches!(outcome, StagedOutcome::Staged(_)) + { + let remaining = self + .field_edits + .keys() + .filter(|(core, id, _)| (*core, *id) == key) + .count(); + if remaining == 0 { + self.versions.staged_note = None; } } let p = MoonPalette::active(cx); @@ -1613,10 +1615,10 @@ impl StrategiesView { if multi && lname == "strategyname" { continue; } - if let Some(c) = &common { - if !c.contains(&lname) { - continue; - } + if let Some(c) = &common + && !c.contains(&lname) + { + continue; } if differ && lname == "signaltype" { continue; @@ -1660,10 +1662,10 @@ impl StrategiesView { // either doing visibly nothing, or, worse, silently staging into a retained state left // over from an earlier per-section visit that this pane is not displaying. Editing a // formula in full mode goes through the row's own edit-in-sections button, which switches back. - if !self.prefs.params_full { - if let Some(helper) = self.formula_helper(cx) { - pane_body = pane_body.child(helper); - } + if !self.prefs.params_full + && let Some(helper) = self.formula_helper(cx) + { + pane_body = pane_body.child(helper); } col = col.child(pane_body); col.into_any_element() @@ -2418,16 +2420,14 @@ impl StrategiesView { { let b = self.backend.read(cx); let store = b.session.store(); - if let Some(sections) = selected_sections(self, store) { - if let Some(f) = sections + if let Some(sections) = selected_sections(self, store) + && let Some(f) = sections .iter() .flat_map(|s| &s.fields) .find(|f| f.name == field) - { - if f.type_name != "String" || !matches!(f.ui, SchemaFieldUi::Edit) { - return None; - } - } + && (f.type_name != "String" || !matches!(f.ui, SchemaFieldUi::Edit)) + { + return None; } } let p = MoonPalette::active(cx); diff --git a/crates/moon-ui-gpui/src/strategies/sections.rs b/crates/moon-ui-gpui/src/strategies/sections.rs index e6fbbf497..aa1ff6e9b 100644 --- a/crates/moon-ui-gpui/src/strategies/sections.rs +++ b/crates/moon-ui-gpui/src/strategies/sections.rs @@ -184,15 +184,13 @@ impl StrategiesView { let label = section_label_key(§ion.title) .filter(|_| self.prefs.human_labels) .map_or(0.0, |key| { - design::ui_caption_text_width(cx, &t!(key).to_string(), 400.0) + design::ui_caption_text_width(cx, t!(key).as_ref(), 400.0) }); raw.max(label) }) .reduce(f32::max) }) - .unwrap_or_else(|| { - design::ui_body_text_width(cx, &t!("strat.sections").to_string(), 600.0) - }) + .unwrap_or_else(|| design::ui_body_text_width(cx, t!("strat.sections").as_ref(), 600.0)) } /// Render schema sections using dependency values shared with the parameters panel. diff --git a/crates/moon-ui-gpui/src/strategies/tree/dnd.rs b/crates/moon-ui-gpui/src/strategies/tree/dnd.rs index 14b9b2cff..43643f54f 100644 --- a/crates/moon-ui-gpui/src/strategies/tree/dnd.rs +++ b/crates/moon-ui-gpui/src/strategies/tree/dnd.rs @@ -187,9 +187,7 @@ impl StrategiesView { return Some(tree::ui::TreeNote::CoreNotCut); } self.copy_folders(folders, cx); - if self.clipboard.is_none() { - return None; - } + self.clipboard.as_ref()?; let marks: Vec<(CoreId, Vec)> = folders .iter() .map(|(core, path)| (*core, ops::split_path(path))) diff --git a/crates/moon-ui-gpui/src/strategies/tree/ops/tests.rs b/crates/moon-ui-gpui/src/strategies/tree/ops/tests.rs index e14dfeab4..e9f268bd4 100644 --- a/crates/moon-ui-gpui/src/strategies/tree/ops/tests.rs +++ b/crates/moon-ui-gpui/src/strategies/tree/ops/tests.rs @@ -295,7 +295,7 @@ fn the_text_clipboard_drops_the_source_anchor() { #[test] fn copy_rows_flattens_to_target() { // A multi-selection from different folders has empty relative paths, so paste puts all in target. - let rows = vec![ + let rows = [ row(1, "a", "grpA/p1", false), row(2, "b", "grpB/sub/p2", false), ]; @@ -429,7 +429,7 @@ fn tree_ops_move_folder(rows: &[StrategyRow], folder: &str, target: &str) -> Vec #[test] fn move_to_flattens_to_target() { // Moving a multi-selection from different folders puts every row directly in the target. - let rows = vec![ + let rows = [ row(1, "a", "src/p1", false), row(2, "b", "other/grp/p2", false), ]; diff --git a/crates/moon-ui-gpui/src/strategies/versions.rs b/crates/moon-ui-gpui/src/strategies/versions.rs index 6aee65274..a5bf10e80 100644 --- a/crates/moon-ui-gpui/src/strategies/versions.rs +++ b/crates/moon-ui-gpui/src/strategies/versions.rs @@ -169,7 +169,7 @@ impl StrategiesView { async move { moon_core::strat_db::stats::versions_with_stats(core, id as i64) }, ) .await; - let _ = cx.update(|cx| { + cx.update(|cx| { let _ = this.update(cx, |this, cx| { this.versions.inflight = false; if this.versions.key == Some((core, id)) { @@ -183,12 +183,11 @@ impl StrategiesView { } // A reveal that named a version wins over the deleted default above: it // is the more specific ask, and it arrived from the same click. - if let Some((key, vf)) = this.versions.pending_select.take() { - if key == (core, id) - && this.versions.list.iter().any(|v| v.valid_from == vf) - { - this.select_version(Some(vf), cx); - } + if let Some((key, vf)) = this.versions.pending_select.take() + && key == (core, id) + && this.versions.list.iter().any(|v| v.valid_from == vf) + { + this.select_version(Some(vf), cx); } cx.notify(); } @@ -212,7 +211,7 @@ impl StrategiesView { let heads = executor .spawn(async move { moon_core::strat_db::stats::deleted_heads() }) .await; - let _ = cx.update(|cx| { + cx.update(|cx| { let _ = this.update(cx, |this, cx| { this.deleted_inflight = false; this.deleted_loaded = true; @@ -269,7 +268,7 @@ impl StrategiesView { Some((view.fields, ignore)) }) .await; - let _ = cx.update(|cx| { + cx.update(|cx| { let _ = this.update(cx, |this, cx| { let Some((fields, ignore)) = payload else { return; @@ -476,7 +475,7 @@ impl StrategiesView { Some((head, fields)) }) .await; - let _ = cx.update(|cx| { + cx.update(|cx| { let Some((head, fields)) = payload else { log::warn!("восстановление {id}: нет head/версий в strat_db"); return; @@ -580,7 +579,7 @@ impl StrategiesView { ) }) .await; - let _ = cx.update(|cx| { + cx.update(|cx| { let _ = this.update(cx, |this, cx| { let Some(view) = view else { return }; if this.versions.sel != Some(vf) @@ -610,10 +609,9 @@ impl StrategiesView { }); if let Some(mut r) = base { if let Some((_, n)) = view.fields.iter().find(|(k, _)| k == "StrategyName") + && !n.is_empty() { - if !n.is_empty() { - r.name = n.clone(); - } + r.name = n.clone(); } r.fields = view.fields; this.versions.changed = view @@ -652,7 +650,9 @@ impl StrategiesView { if single { self.ensure_versions(cx); // Keep the count on the collapsed strip current. } - let count = single.then(|| self.versions.list.len()).filter(|n| *n > 0); + let count = single + .then_some(self.versions.list.len()) + .filter(|n| *n > 0); return v_flex() .id("versions-collapsed") .w(design::ui_px(cx, 22.0)) @@ -871,6 +871,8 @@ impl StrategiesView { *stamp_counts.entry(s.as_str()).or_insert(0) += 1; } let mut previous_taken = false; + // `i` indexes the version list and the bare-stamp list together. + #[allow(clippy::needless_range_loop)] for i in 0..n_versions { let v = &self.versions.list[i]; let slot = if v.valid_to.is_none() { @@ -998,8 +1000,7 @@ impl StrategiesView { // away (defect 5, and it must be unlosable). if let (Some(vf), true) = (self.versions.sel, live_exists) { let compact = - design::ui_body_text_width(cx, &t!("strat.version_restore").to_string(), 400.0) - + 40.0 + design::ui_body_text_width(cx, t!("strat.version_restore").as_ref(), 400.0) + 40.0 > self.panels.versions_w - design::ui_value(cx, VERSIONS_PANE_PADDING * 2.0); col = col.child( div() diff --git a/crates/moon-ui-gpui/src/trade_window/mod.rs b/crates/moon-ui-gpui/src/trade_window/mod.rs index e17c7ed4c..257aea1a3 100644 --- a/crates/moon-ui-gpui/src/trade_window/mod.rs +++ b/crates/moon-ui-gpui/src/trade_window/mod.rs @@ -732,7 +732,7 @@ impl TradeWindowView { let lookup = executor .spawn(async move { strategy::StrategyLookup::read(core, strategy_id, buy_utc_ms) }) .await; - let _ = cx.update(|cx| { + cx.update(|cx| { let _ = this.update(cx, |this, cx| { this.strategy_lookup = Some(lookup); cx.notify(); diff --git a/crates/moon-ui-gpui/src/window/group_window.rs b/crates/moon-ui-gpui/src/window/group_window.rs index e8ae706ee..2166c2150 100644 --- a/crates/moon-ui-gpui/src/window/group_window.rs +++ b/crates/moon-ui-gpui/src/window/group_window.rs @@ -126,13 +126,12 @@ fn spawn_group_window_inner( publish_workspace_change: bool, ) { // Focus an existing live window. handle.update returns an error for a window already closed. - if let Some(handle) = backend.read(cx).group_windows.get(&group).copied() { - if handle + if let Some(handle) = backend.read(cx).group_windows.get(&group).copied() + && handle .update(cx, |_, window, _| window.activate_window()) .is_ok() - { - return; - } + { + return; } // Do not open a Main market automatically at startup. Main starts empty until the user selects // one; the former behavior used server.market with BTCUSDT as fallback. diff --git a/crates/moon-ui-gpui/src/window/windowing.rs b/crates/moon-ui-gpui/src/window/windowing.rs index 18fbafde0..f08d5eadf 100644 --- a/crates/moon-ui-gpui/src/window/windowing.rs +++ b/crates/moon-ui-gpui/src/window/windowing.rs @@ -111,7 +111,7 @@ pub(crate) fn window_hwnd(window: &Window) -> Option { let RawWindowHandle::Win32(handle) = handle.as_raw() else { return None; }; - Some(handle.hwnd.get() as isize) + Some(handle.hwnd.get()) } #[cfg(not(target_os = "windows"))] @@ -526,16 +526,14 @@ pub(crate) fn saved_or_owner_display_id( if let Some(id) = saved_uuid.and_then(|saved| display_id_for_uuid(saved, cx)) { return Some(id); } - if WINDOW_COORDS_ARE_GLOBAL { - if let Some(origin) = saved_origin { - if let Some(d) = cx - .displays() - .into_iter() - .find(|d| d.bounds().contains(&origin)) - { - return Some(d.id()); - } - } + if WINDOW_COORDS_ARE_GLOBAL + && let Some(origin) = saved_origin + && let Some(d) = cx + .displays() + .into_iter() + .find(|d| d.bounds().contains(&origin)) + { + return Some(d.id()); } let owner_id = owner_display.or_else(|| owner_display_id(owner, cx)); owner_id.or_else(|| cx.primary_display().map(|display| display.id())) diff --git a/crates/moon-ui-gpui/src/workspace/tests.rs b/crates/moon-ui-gpui/src/workspace/tests.rs index c9ee58004..e575096de 100644 --- a/crates/moon-ui-gpui/src/workspace/tests.rs +++ b/crates/moon-ui-gpui/src/workspace/tests.rs @@ -307,7 +307,7 @@ fn repeated_activity_in_the_same_workspace_does_not_republish_owner() { /// singleton tools fall back to Classic when the first completed window publishes or renders. #[test] fn rebuild_keeps_an_owner_reopened_second_in_singleton_scope() { - let completed = vec!["alpha".to_string()]; + let completed = ["alpha".to_string()]; let opening = vec!["beta".to_string()]; let owner_registered = completed .iter() diff --git a/crates/moon-ui-gpui/tests/theme_contract/chart.rs b/crates/moon-ui-gpui/tests/theme_contract/chart.rs index f6034e29f..d2a62f41c 100644 --- a/crates/moon-ui-gpui/tests/theme_contract/chart.rs +++ b/crates/moon-ui-gpui/tests/theme_contract/chart.rs @@ -1266,7 +1266,7 @@ fn empty_chart_stack_keeps_its_localized_size_probed_hint() { &main_stack, "fn render(&mut self, window: &mut Window, cx: &mut Context) -> impl IntoElement", ); - let branch = braced_body(&render, "if self.charts.is_empty() {"); + let branch = braced_body(render, "if self.charts.is_empty() {"); assert!( branch.contains("empty::empty_screen("), "the empty Main stack branch must render the empty screen" diff --git a/crates/moon-ui-gpui/tests/theme_contract/shell.rs b/crates/moon-ui-gpui/tests/theme_contract/shell.rs index b89ffa83d..a0aaf783b 100644 --- a/crates/moon-ui-gpui/tests/theme_contract/shell.rs +++ b/crates/moon-ui-gpui/tests/theme_contract/shell.rs @@ -1687,7 +1687,7 @@ fn the_multi_select_hint_clips_instead_of_wrapping() { let coin_search = read_src("controls/coin_search.rs"); let popup = braced_body(&coin_search, "pub(crate) fn render_popup("); let hint = chain_between( - &popup, + popup, "if multi_select {", "chart.coin.multi_hint", "the multi-select hint", diff --git a/crates/moon-ui-gpui/tests/theme_contract/strategies.rs b/crates/moon-ui-gpui/tests/theme_contract/strategies.rs index 9e9bc8f33..66ac1d027 100644 --- a/crates/moon-ui-gpui/tests/theme_contract/strategies.rs +++ b/crates/moon-ui-gpui/tests/theme_contract/strategies.rs @@ -1203,7 +1203,7 @@ fn a_core_folder_row_counter_cluster_stays_passive() { /// Overview for the whole process lifetime — the reported bug, in full. #[test] fn strategies_window_seeds_expansion_from_the_auto_workspace() { - let ctor = code_only(&braced_body( + let ctor = code_only(braced_body( &read_src("strategies/state.rs"), "pub(super) fn new(", )); @@ -1292,19 +1292,19 @@ fn strategies_reopen_state_is_process_lifetime_only() { && read_src("strategies/versions.rs").contains("self.persist_session(cx)"), "Strategies mutation writers must share persist_session" ); - let observer = code_only(&braced_body(&state, "cx.observe(&workspace_revision,")); + let observer = code_only(braced_body(&state, "cx.observe(&workspace_revision,")); assert!( observer.contains("this.rail_seen_core = rail") && !observer.contains("this.expanded_cores"), "the live window's workspace observer must move only the rail overlay, never the persisted set" ); - let capture = code_only(&braced_body(&session, "pub(super) fn capture(")); + let capture = code_only(braced_body(&session, "pub(super) fn capture(")); assert!( capture.contains("expanded_cores: view.expanded_cores.clone()") && !capture.contains("rail"), "capture must snapshot the persisted set alone, never the rail overlay" ); - let session_struct = code_only(&braced_body( + let session_struct = code_only(braced_body( &session, "pub(crate) struct StrategiesSessionState", )); @@ -1331,7 +1331,7 @@ fn strategies_reopen_state_is_process_lifetime_only() { /// construction. A call only on the create path would not catch this breakage. #[test] fn strategies_open_re_seeds_the_auto_core_on_an_existing_handle() { - let open = code_only(&braced_body( + let open = code_only(braced_body( &read_src("strategies/window.rs"), "pub fn open(", )); @@ -1439,15 +1439,14 @@ fn strategy_field_label_lookup_and_dictionary_remain_bijective() { let key = trimmed.trim_end_matches(':').to_string(); dictionary.entry(key.clone()).or_default(); current = Some(key); - } else if let Some(key) = ¤t { - if let Some((locale, _)) = trimmed.split_once(':') { - if matches!(locale, "ru" | "en" | "es") { - dictionary - .get_mut(key) - .expect("current label key was inserted") - .insert(locale.to_string()); - } - } + } else if let Some(key) = ¤t + && let Some((locale, _)) = trimmed.split_once(':') + && matches!(locale, "ru" | "en" | "es") + { + dictionary + .get_mut(key) + .expect("current label key was inserted") + .insert(locale.to_string()); } } let dictionary_keys: BTreeSet = dictionary.keys().cloned().collect(); diff --git a/crates/moon-ui-gpui/tests/theme_contract/workspace.rs b/crates/moon-ui-gpui/tests/theme_contract/workspace.rs index fe4c242dc..feeb77594 100644 --- a/crates/moon-ui-gpui/tests/theme_contract/workspace.rs +++ b/crates/moon-ui-gpui/tests/theme_contract/workspace.rs @@ -369,7 +369,7 @@ fn main_open_requests_revalidate_live_group_before_signature_and_consume() { "pub(crate) fn pending_open_main_request_for_group(", )); assert!( - pending.contains("self.current_open_main_group().as_deref() == Some(group)") + pending.contains("self.current_open_main_group() == Some(group)") && pending.contains("self.open_main_request.pending_target()"), "read-phase routing must use the target core's current live group" ); @@ -378,7 +378,7 @@ fn main_open_requests_revalidate_live_group_before_signature_and_consume() { "pub(crate) fn pending_open_main_revision_for_group(", )); assert!( - signature.contains("self.current_open_main_group().as_deref() == Some(group)"), + signature.contains("self.current_open_main_group() == Some(group)"), "ChartTabs signatures must wake only the target core's current group" ); let take = code_only(braced_body( diff --git a/docs/AGENT_RULES.md b/docs/AGENT_RULES.md index a2e5bb449..2e1a429d7 100644 --- a/docs/AGENT_RULES.md +++ b/docs/AGENT_RULES.md @@ -34,7 +34,8 @@ A SessionStart hook injects this file, and `CONTRIBUTING.md` beside it, into eve ## Before you push 6. `cargo test --workspace` is green on your machine before the push, not just on CI afterwards. -7. Run `cargo clippy` — CI does not (→ `CONTRIBUTING.md` § Commits and PRs). +7. `cargo clippy --workspace --all-targets -- -D warnings` must pass; CI runs that command + (→ `CONTRIBUTING.md` § Commits and PRs and § Build and checks). 8. The tree is rustfmt-clean: run `cargo fmt --all` (or `make fmt`) before you push (→ `CONTRIBUTING.md` § Commits and PRs). 9. Read `git status` before every push; never `git add -f` — fix the over-broad ignore instead.