Skip to main content

pmpx_engine/discovery/
stop.rs

1//! Why walking up stopped.
2//!
3//! Every stop reason is one sentence `pmpx info` shows: "why was no project found" is most often
4//! answered by having hit one of these, so each one has to say something the user can act on.
5
6/// Why walking up stopped.
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8pub enum StopReason {
9    /// Reached the filesystem root.
10    FilesystemRoot,
11    /// Hit `$HOME` — anything above it is no longer the user's project.
12    Home,
13    /// Hit `.git`.
14    GitRoot,
15    /// Reached the `max_depth` limit.
16    MaxDepth,
17    /// The caller explicitly asked not to walk up (`--no-walk-up` or `[discovery] walk_up = false`).
18    WalkUpDisabled,
19}
20
21impl StopReason {
22    /// Human-readable explanation, for `pmpx info`.
23    pub fn describe(self, max_depth: usize) -> String {
24        match self {
25            StopReason::FilesystemRoot => "reached the filesystem root".into(),
26            StopReason::Home => "reached $HOME".into(),
27            StopReason::GitRoot => "reached .git".into(),
28            StopReason::MaxDepth => format!("reached the max_depth limit ({})", dirs(max_depth)),
29            StopReason::WalkUpDisabled => "--no-walk-up / [discovery] walk_up = false".into(),
30        }
31    }
32}
33
34/// `1 directory` / `6 directories`.
35///
36/// Every count around here is routinely 1, and "Walked up 1 directories" is exactly the kind of
37/// thing users report as a bug.
38pub fn dirs(n: usize) -> String {
39    format!("{n} director{}", if n == 1 { "y" } else { "ies" })
40}
41
42#[cfg(test)]
43mod tests {
44    use super::*;
45
46    /// 1 must not come out as "1 directories" -- these counts are routinely 1, and it is exactly
47    /// the kind of thing users report as a bug.
48    #[test]
49    fn a_single_directory_is_not_plural() {
50        assert_eq!(dirs(1), "1 directory");
51        assert_eq!(dirs(0), "0 directories");
52        assert_eq!(dirs(6), "6 directories");
53        assert_eq!(
54            StopReason::MaxDepth.describe(1),
55            "reached the max_depth limit (1 directory)"
56        );
57    }
58
59    #[test]
60    fn every_stop_reason_has_a_description() {
61        for (reason, max) in [
62            (StopReason::FilesystemRoot, 8),
63            (StopReason::Home, 8),
64            (StopReason::GitRoot, 8),
65            (StopReason::MaxDepth, 8),
66            (StopReason::WalkUpDisabled, 8),
67        ] {
68            assert!(!reason.describe(max).is_empty());
69        }
70    }
71}