standard_version/version_file.rs
1//! Version file detection and updating.
2//!
3//! Provides the [`VersionFile`] trait for ecosystem-specific version file
4//! engines, and the [`update_version_files`] / [`detect_version_files`]
5//! functions (in the [`scan`](crate::scan) module) that discover and
6//! update version files at a repository root.
7
8use std::path::PathBuf;
9
10// ---------------------------------------------------------------------------
11// Error
12// ---------------------------------------------------------------------------
13
14/// Errors that can occur when reading or writing version files.
15#[derive(Debug, thiserror::Error)]
16#[non_exhaustive]
17pub enum VersionFileError {
18 /// The expected file was not found on disk.
19 #[error("file not found: {}", .0.display())]
20 FileNotFound(PathBuf),
21 /// The file does not contain a version field this engine can handle.
22 #[error("no version field found")]
23 NoVersionField,
24 /// Writing the updated content back to disk failed.
25 #[error("write failed: {0}")]
26 WriteFailed(#[source] std::io::Error),
27 /// Reading the file from disk failed.
28 #[error("read failed: {0}")]
29 ReadFailed(#[source] std::io::Error),
30 /// A user-supplied regex pattern is invalid or has no capture groups.
31 #[error("invalid regex: {0}")]
32 InvalidRegex(String),
33}
34
35// ---------------------------------------------------------------------------
36// Trait
37// ---------------------------------------------------------------------------
38
39/// A version file engine that can detect, read, and write a version field
40/// inside a specific file format (e.g. `Cargo.toml`, `package.json`).
41pub trait VersionFile {
42 /// Human-readable name (e.g. `"Cargo.toml"`).
43 fn name(&self) -> &str;
44
45 /// Filenames to look for at the repository root.
46 fn filenames(&self) -> &[&str];
47
48 /// Check if `content` contains a version field this engine handles.
49 fn detect(&self, content: &str) -> bool;
50
51 /// Extract the current version string from file content.
52 fn read_version(&self, content: &str) -> Option<String>;
53
54 /// Return updated file content with `new_version` replacing the old value.
55 fn write_version(&self, content: &str, new_version: &str) -> Result<String, VersionFileError>;
56
57 /// Compare old and new file content and return optional extra information
58 /// about side-effects (e.g. `VERSION_CODE` increment in gradle).
59 ///
60 /// The default implementation returns `None`.
61 fn extra_info(&self, _old_content: &str, _new_content: &str) -> Option<String> {
62 None
63 }
64}
65
66// ---------------------------------------------------------------------------
67// UpdateResult
68// ---------------------------------------------------------------------------
69
70/// The outcome of updating a single version file.
71#[derive(Debug, Clone, PartialEq, Eq)]
72pub struct UpdateResult {
73 /// Absolute path to the file that was updated.
74 pub path: PathBuf,
75 /// Human-readable engine name (e.g. `"Cargo.toml"`).
76 pub name: String,
77 /// Version string before the update.
78 pub old_version: String,
79 /// Version string after the update.
80 pub new_version: String,
81 /// Optional extra info (e.g. `"VERSION_CODE: 42 → 43"`).
82 pub extra: Option<String>,
83}
84
85// ---------------------------------------------------------------------------
86// DetectedFile
87// ---------------------------------------------------------------------------
88
89/// Information about a detected version file (no writes performed).
90#[derive(Debug, Clone, PartialEq, Eq)]
91pub struct DetectedFile {
92 /// Absolute path to the file.
93 pub path: PathBuf,
94 /// Human-readable engine name (e.g. `"Cargo.toml"`).
95 pub name: String,
96 /// Current version string in the file.
97 pub old_version: String,
98}
99
100// ---------------------------------------------------------------------------
101// CustomVersionFile
102// ---------------------------------------------------------------------------
103
104/// A user-defined version file matched by path and regex.
105///
106/// Processed by [`RegexVersionFile`](crate::regex_engine::RegexVersionFile)
107/// during [`update_version_files`](crate::scan::update_version_files).
108#[derive(Debug, Clone)]
109pub struct CustomVersionFile {
110 /// Path to the file, relative to the repository root.
111 pub path: PathBuf,
112 /// Regex pattern whose first capture group contains the version string.
113 pub pattern: String,
114}
115
116// Re-export scan functions so existing `use version_file::update_version_files`
117// paths continue to work.
118pub use crate::scan::{detect_version_files, update_version_files};