Skip to main content

vcs_runner/
lib.rs

1//! VCS-specific helpers built on [`procpilot`]. Adds jj/git shorthand
2//! wrappers, repo detection, and output parsers.
3//!
4//! For generic subprocess execution (stdin, retry, timeout, custom envs),
5//! use [`procpilot::Cmd`] directly — it's re-exported here for convenience.
6
7mod detect;
8mod error;
9#[cfg(feature = "git-parse")]
10mod parse_git;
11#[cfg(feature = "jj-parse")]
12mod parse_jj;
13mod runner;
14mod types;
15
16pub use detect::{VcsBackend, detect_vcs};
17pub use error::RunError;
18#[cfg(feature = "git-parse")]
19pub use parse_git::parse_git_diff_name_status;
20#[cfg(feature = "jj-parse")]
21pub use parse_jj::{
22    BOOKMARK_TEMPLATE, LOG_TEMPLATE, BookmarkParseResult, LogParseResult, parse_bookmark_output,
23    parse_diff_summary, parse_log_output, parse_remote_list,
24};
25pub use runner::{
26    git_merge_base, is_transient_error, jj_merge_base, run_git, run_git_cancellable, run_git_utf8,
27    run_git_utf8_cancellable, run_git_utf8_with_retry, run_git_utf8_with_retry_cancellable,
28    run_git_utf8_with_timeout, run_git_with_retry, run_git_with_retry_cancellable,
29    run_git_with_timeout, run_jj, run_jj_cancellable, run_jj_utf8, run_jj_utf8_cancellable,
30    run_jj_utf8_with_retry, run_jj_utf8_with_retry_cancellable, run_jj_utf8_with_timeout,
31    run_jj_with_retry, run_jj_with_retry_cancellable, run_jj_with_timeout,
32};
33
34// Re-export procpilot's generic subprocess API so vcs-runner consumers have
35// one dependency. Prefer these for anything non-VCS-specific.
36pub use procpilot::{
37    Cmd, CmdDisplay, DefaultRunner, Redirection, RetryPolicy, RunOutput, Runner,
38    STREAM_SUFFIX_SIZE, SpawnedProcess, StdinData, binary_available, binary_version,
39    default_transient,
40};
41
42#[cfg(any(feature = "jj-parse", feature = "git-parse"))]
43pub use types::{FileChange, FileChangeKind};
44#[cfg(feature = "jj-parse")]
45pub use types::{Bookmark, ConflictState, ContentState, GitRemote, LogEntry, RemoteStatus, WorkingCopy};
46
47/// Common types and helpers for everyday VCS subprocess work.
48///
49/// `use vcs_runner::prelude::*;` brings in procpilot's standard set
50/// (`Cmd`, `RunError`, `RunOutput`, `Redirection`, `RetryPolicy`,
51/// `StdinData`, `SpawnedProcess`) plus the VCS-specific helpers
52/// (`run_jj`, `run_git`, retry/timeout variants, `jj_merge_base`,
53/// `git_merge_base`, `is_transient_error`, and the binary-availability
54/// checks).
55///
56/// Parser types and constants (`LogEntry`, `BOOKMARK_TEMPLATE`, …) stay
57/// out of the prelude — those callers know they need them and can
58/// import explicitly.
59pub mod prelude {
60    pub use procpilot::prelude::*;
61
62    pub use crate::{
63        VcsBackend, detect_vcs, git_available, git_merge_base, git_version, is_transient_error,
64        jj_available, jj_merge_base, jj_version, run_git, run_git_cancellable, run_git_utf8,
65        run_git_utf8_cancellable, run_git_utf8_with_retry, run_git_utf8_with_retry_cancellable,
66        run_git_utf8_with_timeout, run_git_with_retry, run_git_with_retry_cancellable,
67        run_git_with_timeout, run_jj, run_jj_cancellable, run_jj_utf8, run_jj_utf8_cancellable,
68        run_jj_utf8_with_retry, run_jj_utf8_with_retry_cancellable, run_jj_utf8_with_timeout,
69        run_jj_with_retry, run_jj_with_retry_cancellable, run_jj_with_timeout,
70    };
71}
72
73/// Check whether the `jj` binary is available on PATH.
74pub fn jj_available() -> bool {
75    binary_available("jj")
76}
77
78/// Get the jj version string, if available.
79pub fn jj_version() -> Option<String> {
80    binary_version("jj")
81}
82
83/// Check whether the `git` binary is available on PATH.
84pub fn git_available() -> bool {
85    binary_available("git")
86}
87
88/// Get the git version string, if available.
89pub fn git_version() -> Option<String> {
90    binary_version("git")
91}
92
93#[cfg(test)]
94mod tests {
95    use super::*;
96
97    #[test]
98    fn jj_available_returns_bool() {
99        let _ = jj_available();
100    }
101
102    #[test]
103    fn jj_version_matches_availability() {
104        if jj_available() {
105            let v = jj_version().expect("jj is installed");
106            assert!(v.contains("jj"));
107        } else {
108            assert!(jj_version().is_none());
109        }
110    }
111
112    #[test]
113    fn git_available_returns_bool() {
114        let _ = git_available();
115    }
116
117    #[test]
118    fn git_version_matches_availability() {
119        if git_available() {
120            let v = git_version().expect("git is installed");
121            assert!(v.contains("git"));
122        } else {
123            assert!(git_version().is_none());
124        }
125    }
126}