Abbaye

at 2f4eb91

use std::path::PathBuf;

use figment::{
    Figment,
    providers::{Format, Toml},
};
use miette::{IntoDiagnostic, Result};
use serde::{Deserialize, Serialize};

use schemars::JsonSchema;

use crate::{
    builders::BuilderEntry, changelog::ChangelogConfig, version_extractors::AnyVersionExtractor,
};

/// General website metadata.
#[derive(Debug, Default, Clone, Deserialize, Serialize, JsonSchema)]
pub struct SiteConfig {
    /// Display name of the project, used in page titles and headings.
    pub name: String,
    /// Where to output generated files. Defaults to `public`.
    #[serde(default = "abbaye_output_dir")]
    pub output_dir: PathBuf,
    /// Path to the README file rendered on each version page.
    /// Defaults to `README.md` in the current working directory.
    pub readme: Option<PathBuf>,
    /// Canonical base URL of the published site (e.g. `"https://example.com"`).
    /// When set, the generated `releases.atom` feed will include absolute links
    /// and proper entry IDs.  Trailing slashes are stripped automatically.
    pub base_url: Option<String>,
    /// URL of the project's repository (e.g. `"https://git.sr.ht/~ololduck/abbaye"`).
    /// When set, we will show the repository link in the generated website.
    pub repo_url: Option<String>,
    /// An optional language code for the website (e.g. `"en"` or `"fr"`).
    /// When set, the generated HTML will include a `lang` attribute on the `<html>` tag. Defaults to `"en"`.
    pub lang: Option<String>,
    /// If you have a Fediverse account, you can set this to your username to enable Fediverse integration. (don't forget the starting '@' !)
    pub fediverse_creator: Option<String>,
    /// OpenGraph configuration for the website.
    pub opengraph: Option<OpenGraphConfig>,
}

/// OpenGraph configuration for the website.
#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema)]
pub struct OpenGraphConfig {
    /// OpenGraph type of the website. It will be used as the `og:type` meta tag.
    /// Note: by default, the value "website" will be enforced for the version listing page and "article" for the release notes page.
    pub r#type: Option<String>,
    /// URL of the website's image.
    pub image: String,
    /// Alt text for the website's image.
    pub image_alt_text: Option<String>,
    /// URL of the website. If not set, the `base_url` will be used instead. If `base_url` is not set either, the world explodes. Think of the kittens.
    pub url: Option<String>,
    /// Author of the website. Used on release notes pages to indicate the author.
    pub author: Option<String>,
}

/// Configuration for the git repository web UI.
#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema)]
pub struct GitUiConfig {
    #[serde(default = "default_branch")]
    pub default_branch: String,
    /// Maximum number of commits to show in the log page. Defaults to 200.
    #[serde(default = "default_max_commits")]
    pub max_commits: usize,
    /// Path to the git repository to read. Defaults to `.` (the current directory).
    pub repo_path: Option<PathBuf>,
    /// Explicit clone URL displayed in the UI (e.g. `"https://example.com/repository.git"`).
    /// When absent, derived from `site.base_url` by appending `/repository.git`.
    pub clone_url: Option<String>,
}

fn default_branch() -> String {
    "main".to_string()
}

fn default_max_commits() -> usize {
    200
}

/// A full configuration for the Abbaye site generator.
///
/// Here's a sample configuration that works well as a starting point for rust projects:
///
/// ```toml
/// [site]
/// name = "Abbaye"
///
/// [version_extractor]
/// type = "cargo" # extract version from Cargo.toml
///
/// [changelog]
/// # let's use the default changelog extractor
///
/// [[builders]]
/// type = "cargo" # calls `cargo build --release` for each target
/// targets = ["x86_64-unknown-linux-gnu", "x86_64-unknown-linux-musl"]
/// [[builders]]
/// type = "cargo_doc" # generates documentation using `cargo doc`
/// no_deps = true
///
/// [[builders]]
/// type = "archive" # creates a compressed tarball of the source code
/// ```
///
/// You can learn more about each builder type in the [builders module documentation](crate::builders).
///
#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema)]
pub struct AbbayeConfig {
    /// Metadata about the site.
    pub site: SiteConfig,
    /// which version extractor to use to extract the version(s) of the project
    pub version_extractor: AnyVersionExtractor,
    /// Configuration for the changelog extractor.
    pub changelog: ChangelogConfig,
    /// Builders to run during the build process.
    pub builders: Vec<BuilderEntry>,
    /// Optional git repository web UI. When present, abbaye generates browsable HTML
    /// pages at `<output>/repository/` and a clonable bare repository at
    /// `<output>/repository.git/`.
    #[serde(default)]
    pub git_ui: Option<GitUiConfig>,
}

fn abbaye_output_dir() -> PathBuf {
    PathBuf::from("public")
}

/// Load the Abbaye2 configuration from the current working directory.
///
/// Looks for `.abbaye.toml` first, then `abbaye.toml`; when both are present
/// `abbaye.toml` takes precedence (last merge wins).
pub fn load_config() -> Result<AbbayeConfig> {
    let cwd = std::env::current_dir().into_diagnostic()?;
    Figment::new()
        .merge(Toml::file(cwd.join(".abbaye.toml")))
        .merge(Toml::file(cwd.join("abbaye.toml")))
        .merge(Toml::file(cwd.join(".abbaye").join("abbaye.toml")))
        .extract()
        .into_diagnostic()
}