Abbaye

at bb4e6ed

use std::path::PathBuf;

use miette::{IntoDiagnostic, Result, miette};
use serde::Deserialize;

use super::VersionExtractor;

/// Configuration for [`CargoVersion`].
#[derive(Debug, Default, Clone, Deserialize)]
pub struct CargoVersionConfig {
    /// Path to the `Cargo.toml` to read the version from.
    /// Defaults to `Cargo.toml` in the current working directory.
    pub manifest_path: Option<PathBuf>,
}

/// Extracts the package version by reading it directly from `Cargo.toml`.
pub struct CargoVersion;

// Internal types used only for TOML parsing.
#[derive(Deserialize)]
struct CargoManifest {
    package: Option<CargoPackage>,
}

#[derive(Deserialize)]
struct CargoPackage {
    version: Option<String>,
}

impl VersionExtractor for CargoVersion {
    type ConfigType = CargoVersionConfig;

    async fn get_last_version(&self, config: Self::ConfigType) -> Result<String> {
        let path = config
            .manifest_path
            .unwrap_or_else(|| PathBuf::from("Cargo.toml"));

        let content = tokio::fs::read_to_string(&path).await.into_diagnostic()?;

        let manifest: CargoManifest = toml::from_str(&content).into_diagnostic()?;

        manifest
            .package
            .ok_or_else(|| miette!("{} has no [package] section", path.display()))?
            .version
            .ok_or_else(|| miette!("no version field in [package] in {}", path.display()))
    }
}