Skip to main content

vcs_modify_guard/
lib.rs

1//! Help CLI tools decide whether it is safe to modify files in a VCS
2//! working tree.
3//!
4//! `vcs-modify-guard` helps CLI tools enforce `--allow-dirty`,
5//! `--allow-staged`, and `--allow-no-vcs` style checks before they modify
6//! files.
7//!
8//! Currently, this crate supports Git repositories. Backend selection is
9//! controlled by Cargo features; see [Feature flags](#feature-flags).
10//!
11//! # API overview
12//!
13//! This crate provides two layers of API:
14//!
15//! - [`AllowOptions`] is the main entry point. It implements `cargo fix`-style
16//!   safe-to-modify checks and returns a [`ModificationSafety`] describing whether
17//!   modification is safe. By default, checks are scoped to the queried path.
18//! - [`repository::Repository`] is a lower-level API for tools that need to
19//!   discover a repository and inspect whether files are dirty and/or staged to
20//!   implement their own policy. Dirty files include modified tracked files and
21//!   untracked files.
22//!
23//! Most users should start with [`AllowOptions`]. Reach for
24//! [`repository::Repository`] only when you need custom behavior beyond the
25//! built-in `--allow-*` semantics.
26//!
27//! # Feature flags
28//!
29//! This crate currently supports Git repositories via selectable Git
30//! backends.
31//!
32//! ## Backend selection features
33//!
34//! - `git-default` (enabled by default) enables the default Git backend.
35//!   Currently, this enables `git-gix`.
36//! - `git-gix` enables the `gix` backend.
37//! - `git-libgit2` enables the `libgit2` backend.
38//! - `git-cli` enables the Git CLI backend.
39//!
40//! To opt out of the default backend, disable default features and enable the
41//! desired backend feature(s) explicitly:
42//!
43//! ```toml
44//! [dependencies]
45//! vcs-modify-guard = {
46//!   version = "0.1.0",
47//!   default-features = false,
48//!   features = ["git-libgit2"]
49//! }
50//! ```
51//!
52//! If multiple backends are enabled, they are tried in this fixed priority
53//! order: `gix`, then `libgit2`, then the Git CLI.
54//!
55//! If no backend selection features are enabled, repository discovery reports
56//! that no supported repository was found.
57//!
58//! ## Backend configuration features
59//!
60//! - `vendored-libgit2` forwards to `git2`'s `vendored-libgit2` feature when
61//!   `git-libgit2` is enabled.
62//!
63//! # Example
64//!
65//! The following example shows how to validate whether a target path is safe
66//! to modify before performing an operation that may modify files.
67//!
68//! ```no_run
69//! use std::path::{Path, PathBuf};
70//!
71//! use clap::Parser;
72//! use vcs_modify_guard::{AllowOptions, ModificationSafety, UnsafeModificationReason};
73//!
74//! #[derive(Debug, Parser)]
75//! struct Args {
76//!     /// Process code even if a VCS was not detected.
77//!     #[arg(long)]
78//!     allow_no_vcs: bool,
79//!     /// Process code even if the target path has modified, staged, or
80//!     /// untracked files under it.
81//!     #[arg(long)]
82//!     allow_dirty: bool,
83//!     /// Process code even if the target path has staged changes under it.
84//!     #[arg(long)]
85//!     allow_staged: bool,
86//!     /// Target path to process. Defaults to the current working directory.
87//!     target: Option<PathBuf>,
88//! }
89//!
90//! fn main() -> Result<(), Box<dyn std::error::Error>> {
91//!     let args = Args::parse();
92//!
93//!     let target = args.target.as_deref().unwrap_or_else(|| Path::new("."));
94//!     let safety = AllowOptions::new()
95//!         .allow_no_vcs(args.allow_no_vcs)
96//!         .allow_dirty(args.allow_dirty)
97//!         .allow_staged(args.allow_staged)
98//!         .check_safe_to_modify(target)?;
99//!
100//!     match safety {
101//!         ModificationSafety::Safe => {}
102//!         ModificationSafety::Unsafe(reason) => match reason {
103//!             UnsafeModificationReason::NoVcs => {
104//!                 return Err("blocked by no VCS".into());
105//!             }
106//!             UnsafeModificationReason::Dirty { .. } => {
107//!                 return Err("blocked by dirty files".into());
108//!             }
109//!             UnsafeModificationReason::Staged { .. } => {
110//!                 return Err("blocked by staged changes".into());
111//!             }
112//!             _ => {
113//!                 return Err("blocked by unsafe modifications".into());
114//!             }
115//!         },
116//!     }
117//!
118//!     eprintln!("Proceeding...");
119//!
120//!     Ok(())
121//! }
122//! ```
123//!
124//! See the `allow_options` example for a complete command-line application.
125//!
126//! If you need custom policy logic instead of the built-in `--allow-*`
127//! behavior, see the [`repository`] module for direct repository discovery and
128//! change query APIs.
129
130#![cfg_attr(docsrs, feature(doc_cfg))]
131#![doc(html_root_url = "https://docs.rs/vcs-modify-guard/0.1.0")]
132
133#[cfg_attr(
134    not(vcs_backend_enabled),
135    expect(
136        unused_imports,
137        unreachable_pub,
138        reason = "when no VCS backend is enabled, `vcs::*` re-exports nothing from the crate root"
139    )
140)]
141pub use self::vcs::*;
142pub use self::{allow_options::*, error::*};
143
144mod allow_options;
145mod error;
146pub mod repository;
147#[cfg(test)]
148mod testing;
149mod util;
150mod vcs;