Skip to main content

abbaye/version_extractors/
mod.rs

1use chrono::{DateTime, Utc};
2use miette::{Result, bail};
3use schemars::JsonSchema;
4use serde::{Deserialize, Serialize};
5
6pub mod cargo;
7pub mod git;
8
9use cargo::{CargoVersion, CargoVersionConfig};
10use git::{GitVersion, GitVersionConfig};
11
12/// A version string paired with an optional release date.
13///
14/// Returned by [`VersionExtractor::get_last_version`] and
15/// [`VersionExtractor::get_all_versions`] so that callers can propagate
16/// date information (e.g. to Atom feed entries) without a second round-trip.
17#[derive(Debug, Clone)]
18pub struct VersionInfo {
19    /// The human-readable version string (e.g. `"1.2.3"`).
20    pub version: String,
21    /// The UTC date/time at which this version was released, if known.
22    pub date: Option<DateTime<Utc>>,
23}
24
25#[allow(async_fn_in_trait)]
26pub trait VersionExtractor {
27    type ConfigType: Default + for<'de> Deserialize<'de> + Clone;
28
29    async fn get_last_version(&self, config: Self::ConfigType) -> Result<VersionInfo>;
30
31    async fn get_all_versions(&self, _config: Self::ConfigType) -> Result<Vec<VersionInfo>> {
32        bail!("get_all_versions is not supported by this version extractor")
33    }
34}
35
36#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema)]
37#[serde(tag = "type", rename_all = "snake_case")]
38pub enum AnyVersionExtractor {
39    /// Reads the version from the `version` field in the `[package]` section
40    /// of a `Cargo.toml` file.
41    ///
42    /// ```toml
43    /// [version_extractor]
44    /// type = "cargo"
45    /// manifest_path = "Cargo.toml" # optional, defaults to ./Cargo.toml
46    /// ```
47    Cargo(CargoVersionConfig),
48
49    /// Derives the version from the most recent Git tag using
50    /// `git describe --tags --always`.
51    /// Supports stripping a tag prefix (e.g. `"v"`) and customising the
52    /// suffix appended when the working tree has uncommitted changes.
53    ///
54    /// ```toml
55    /// [version_extractor]
56    /// type = "git"
57    /// tag_prefix = "v"      # optional, strips leading "v"
58    /// dirty_suffix = "-dev" # optional, defaults to "-dirty"
59    /// ```
60    Git(GitVersionConfig),
61}
62
63impl AnyVersionExtractor {
64    pub async fn extract(&self) -> Result<VersionInfo> {
65        match self {
66            Self::Cargo(config) => CargoVersion.get_last_version(config.clone()).await,
67            Self::Git(config) => GitVersion.get_last_version(config.clone()).await,
68        }
69    }
70
71    pub async fn extract_all(&self) -> Result<Vec<VersionInfo>> {
72        match self {
73            Self::Cargo(config) => CargoVersion.get_all_versions(config.clone()).await,
74            Self::Git(config) => GitVersion.get_all_versions(config.clone()).await,
75        }
76    }
77
78    /// Returns the full VCS tag name for the given version string.
79    ///
80    /// For Git extractors this re-applies the configured `tag_prefix` (e.g.
81    /// `"v"` → `"v1.2.3"`).  For other extractors the version is returned
82    /// unchanged.
83    pub fn tag_name(&self, version: &str) -> String {
84        match self {
85            Self::Git(config) => match &config.tag_prefix {
86                Some(prefix) => format!("{prefix}{version}"),
87                None => version.to_owned(),
88            },
89            Self::Cargo(_) => version.to_owned(),
90        }
91    }
92}