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;