Skip to main content

cmx_core/
platform_iter.rs

1//! Shared iterators for cross-platform traversal.
2//!
3//! The `for platform in Platform::ALL { if !platform.supports(kind) { continue }
4//! let pv = ctx.paths.with_platform(platform); ... }` pattern appears throughout
5//! the codebase. This module centralises it so every call site reads as a
6//! one-liner filter-map rather than a three-line hand-rolled guard.
7//!
8//! # Usage
9//!
10//! ```no_run
11//! # use cmx_core::platform_iter;
12//! # use cmx_core::types::ArtifactKind;
13//! # use cmx_core::context::AppContext;
14//! // Iterate every platform that supports `kind`:
15//! // for view in platform_iter::views_for(ctx, platform_iter::all(), kind) { ... }
16//! //
17//! // Put the active platform first:
18//! // for view in platform_iter::views_for(ctx, platform_iter::active_first(active), kind) { ... }
19//! ```
20
21use std::iter;
22
23use crate::paths::ConfigPaths;
24use crate::platform::Platform;
25use crate::types::ArtifactKind;
26
27/// A platform together with the `ConfigPaths` view scoped to it.
28///
29/// Callers use `view.paths` wherever a platform-specific `ConfigPaths` is
30/// needed, and `view.platform` for display or comparisons.
31pub struct PlatformView {
32    pub platform: Platform,
33    pub paths: ConfigPaths,
34}
35
36/// Filter `platforms` to those that support `kind` and map each to a
37/// [`PlatformView`] that bundles the platform with its scoped [`ConfigPaths`].
38///
39/// The base paths (`home_dir`, `config_dir`) are cloned from `base_paths` into
40/// each view via [`ConfigPaths::with_platform`]; only the active `platform`
41/// field changes.
42pub fn views_for(
43    base_paths: &ConfigPaths,
44    platforms: impl IntoIterator<Item = Platform>,
45    kind: ArtifactKind,
46) -> impl Iterator<Item = PlatformView> {
47    // Pre-clone both path roots so the closure owns them and does not hold a
48    // reference to `base_paths` across the iterator's lifetime.
49    let config_dir = base_paths.config_dir.clone();
50    let home_dir = base_paths.home_dir.clone();
51    platforms
52        .into_iter()
53        .filter(move |p| p.supports(kind))
54        .map(move |platform| PlatformView {
55            paths: ConfigPaths {
56                config_dir: config_dir.clone(),
57                home_dir: home_dir.clone(),
58                platform,
59            },
60            platform,
61        })
62}
63
64/// Iterate every platform in [`Platform::ALL`] in canonical order.
65pub fn all() -> impl Iterator<Item = Platform> {
66    Platform::ALL.iter().copied()
67}
68
69/// Iterate `active` first, then every other platform in canonical order.
70///
71/// Mirrors the pattern in `info/mod.rs`: the active platform is searched first
72/// so a locally-installed artifact is found without scanning every tool.
73pub fn active_first(active: Platform) -> impl Iterator<Item = Platform> {
74    iter::once(active).chain(Platform::ALL.iter().copied().filter(move |&p| p != active))
75}