update_version/parsers/
mod.rs1use anyhow::Result;
2use log::{debug, info};
3use semver::Version;
4use std::path::{Path, PathBuf};
5use thiserror::Error;
6
7pub mod package_json_parser;
8pub mod tauri_config_parser;
9pub mod toml_parser;
10
11#[derive(Debug, Error)]
12#[non_exhaustive]
13pub enum ParsingError {
14 #[error("No versions found in directory: {0}")]
15 NoVersionFoundError(String),
16}
17
18#[derive(Debug, Clone, Default)]
20pub struct WalkOptions {
21 pub no_ignore: bool,
24}
25
26pub trait Parser {
27 fn update_version(
28 path: impl AsRef<Path>,
29 version: &Version,
30 options: &WalkOptions,
31 ) -> Result<Vec<PathBuf>> {
32 info!("Updating version to {}", version);
33 let files = Self::get_matching_files(path, options)?;
34 let version_regex = Self::version_match_regex()?;
35 for file in &files {
36 debug!("Checking file: '{}'", file.display());
37 let contents = std::fs::read_to_string(file)?;
38 let new_contents = version_regex
39 .replace(contents.as_str(), Self::version_line_format(version)?)
40 .to_string();
41 std::fs::write(file, new_contents)?;
42 }
43 Ok(files)
44 }
45 fn increment_version(path: impl AsRef<Path>, options: &WalkOptions) -> Result<Vec<PathBuf>> {
46 let path = path.as_ref();
47 let current_version = Self::get_current_version(path, options)?;
48 let mut new_version = current_version.clone();
49 if current_version.pre.is_empty() {
50 new_version.patch += 1;
51 } else {
52 new_version.pre = semver::Prerelease::EMPTY;
53 }
54 new_version.build = semver::BuildMetadata::EMPTY;
55 debug!(
56 "Incrementing version from {} -> {}",
57 current_version, new_version
58 );
59 Self::update_version(path, &new_version, options)
60 }
61 fn get_current_version(path: impl AsRef<Path>, options: &WalkOptions) -> Result<Version> {
62 let path = path.as_ref();
63 let files = Self::get_matching_files(path, options)?;
64 let version_regex = Self::version_match_regex()?;
65
66 for file in files {
67 let contents = std::fs::read_to_string(file)?;
68 if let Some(captures) = version_regex.captures(contents.as_str())
69 && let Some(version) = captures.get(2)
70 {
71 let version = version.as_str();
72 debug!("Found current version: {}", version);
73 return Ok(Version::parse(version)?);
74 }
75 }
76
77 Err(ParsingError::NoVersionFoundError(path.to_string_lossy().to_string()).into())
78 }
79
80 fn get_matching_files(path: impl AsRef<Path>, options: &WalkOptions) -> Result<Vec<PathBuf>> {
81 debug!("Checking matching files");
82 let mut files: Vec<PathBuf> = vec![];
83 let path = path.as_ref();
84 let filename_regex = Self::filename_match_regex()?;
85
86 let mut builder = ignore::WalkBuilder::new(path);
87
88 if options.no_ignore {
89 builder.git_ignore(false);
92 builder.git_global(false);
93 builder.git_exclude(false);
94 } else {
95 builder.add_custom_ignore_filename(".uvignore");
96 }
97
98 for item in builder.build() {
99 let item = item?;
100 let path = item.path();
101 if filename_regex.is_match(path.to_string_lossy().as_ref()) {
102 files.push(path.to_path_buf());
103 }
104 }
105
106 files.sort_by(|a, b| {
108 a.components().count().cmp(&b.components().count())
109 .then_with(|| a.cmp(b))
110 });
111
112 debug!("Found files: {:?}", files);
113 Ok(files)
114 }
115
116 fn version_match_regex() -> Result<regex::Regex>;
117 fn filename_match_regex() -> Result<regex::Regex>;
118 fn version_line_format(version: &Version) -> Result<String>;
119}