use release_plz_core::UpdateRequest;
use serde::{Deserialize, Serialize};
use std::{collections::HashMap, path::PathBuf};
use url::Url;
#[derive(Serialize, Deserialize, Default, PartialEq, Eq, Debug)]
#[serde(deny_unknown_fields)]
pub struct Config {
#[serde(default)]
pub workspace: Workspace,
#[serde(default)]
pub package: HashMap<String, PackageSpecificConfig>,
}
impl Config {
pub fn fill_update_config(
&self,
is_changelog_update_disabled: bool,
update_request: UpdateRequest,
) -> UpdateRequest {
let mut default_update_config = self.workspace.packages_defaults.update.clone();
if is_changelog_update_disabled {
default_update_config.update_changelog = false.into();
}
let mut update_request =
update_request.with_default_package_config(default_update_config.into());
for (package, config) in &self.package {
let mut update_config = config.clone();
if is_changelog_update_disabled {
update_config.update.update_changelog = false.into();
}
update_request = update_request.with_package_config(package, update_config.into());
}
update_request
}
}
#[derive(Serialize, Deserialize, Default, PartialEq, Eq, Debug)]
pub struct Workspace {
#[serde(flatten)]
pub update: UpdateConfig,
#[serde(flatten)]
pub packages_defaults: PackageConfig,
}
#[derive(Serialize, Deserialize, Default, PartialEq, Eq, Debug)]
#[serde(deny_unknown_fields)]
pub struct UpdateConfig {
#[serde(default)]
pub update_dependencies: bool,
#[serde(default)]
pub changelog_config: Option<PathBuf>,
#[serde(default)]
pub allow_dirty: bool,
#[serde(default)]
pub repo_url: Option<Url>,
}
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Default, Clone)]
pub struct PackageSpecificConfig {
#[serde(flatten)]
update: PackageUpdateConfig,
#[serde(flatten)]
release: PackageReleaseConfig,
changelog_path: Option<PathBuf>,
}
impl From<PackageSpecificConfig> for release_plz_core::PackageReleaseConfig {
fn from(config: PackageSpecificConfig) -> Self {
Self {
generic: release_plz_core::ReleaseConfig {},
changelog_path: config.changelog_path,
}
}
}
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Default, Clone)]
pub struct PackageConfig {
#[serde(flatten)]
update: PackageUpdateConfig,
#[serde(flatten)]
release: PackageReleaseConfig,
}
impl From<PackageUpdateConfig> for release_plz_core::UpdateConfig {
fn from(config: PackageUpdateConfig) -> Self {
Self {
semver_check: config.semver_check.into(),
update_changelog: config.update_changelog.into(),
}
}
}
impl From<PackageSpecificConfig> for release_plz_core::PackageUpdateConfig {
fn from(config: PackageSpecificConfig) -> Self {
Self {
generic: config.update.into(),
changelog_path: config.changelog_path,
}
}
}
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Default, Clone)]
#[serde(deny_unknown_fields)]
pub struct PackageUpdateConfig {
#[serde(default)]
pub semver_check: SemverCheck,
#[serde(default)]
update_changelog: BoolDefaultingTrue,
}
impl PackageUpdateConfig {
pub fn update_changelog(&self) -> bool {
self.update_changelog.into()
}
}
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone, Copy)]
struct BoolDefaultingTrue(bool);
impl Default for BoolDefaultingTrue {
fn default() -> Self {
Self(true)
}
}
impl From<BoolDefaultingTrue> for bool {
fn from(config: BoolDefaultingTrue) -> Self {
config.0
}
}
impl From<bool> for BoolDefaultingTrue {
fn from(config: bool) -> Self {
Self(config)
}
}
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Default, Clone)]
#[serde(deny_unknown_fields)]
pub struct PackageReleaseConfig {
#[serde(default)]
pub git_release: GitReleaseConfig,
}
#[derive(Serialize, Deserialize, Default, PartialEq, Eq, Debug, Clone, Copy)]
#[serde(rename_all = "snake_case")]
pub enum SemverCheck {
#[default]
Lib,
Yes,
No,
}
impl From<SemverCheck> for release_plz_core::RunSemverCheck {
fn from(config: SemverCheck) -> Self {
match config {
SemverCheck::Lib => Self::Lib,
SemverCheck::Yes => Self::Yes,
SemverCheck::No => Self::No,
}
}
}
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone, Default)]
#[serde(deny_unknown_fields)]
pub struct GitReleaseConfig {
#[serde(default)]
enable: BoolDefaultingTrue,
#[serde(default)]
pub release_type: ReleaseType,
#[serde(default)]
pub draft: bool,
}
#[derive(Serialize, Deserialize, Default, PartialEq, Eq, Debug, Clone, Copy)]
#[serde(rename_all = "snake_case")]
pub enum ReleaseType {
#[default]
Prod,
Pre,
Auto,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn config_without_update_config_is_deserialized() {
let config = r#"
[workspace]
update_dependencies = false
changelog_config = "../git-cliff.toml"
allow_dirty = false
repo_url = "https://github.com/MarcoIeni/release-plz"
[workspace.git_release]
enable = true
release_type = "prod"
draft = false
"#;
let expected_config = Config {
workspace: Workspace {
update: UpdateConfig {
update_dependencies: false,
changelog_config: Some("../git-cliff.toml".into()),
allow_dirty: false,
repo_url: Some("https://github.com/MarcoIeni/release-plz".parse().unwrap()),
},
packages_defaults: PackageConfig {
update: PackageUpdateConfig {
semver_check: SemverCheck::Lib,
update_changelog: true.into(),
},
release: PackageReleaseConfig {
git_release: GitReleaseConfig {
enable: true.into(),
release_type: ReleaseType::Prod,
draft: false,
},
},
},
},
package: [].into(),
};
let config: Config = toml::from_str(config).unwrap();
assert_eq!(config, expected_config)
}
#[test]
fn config_is_deserialized() {
let config = r#"
[workspace]
update_dependencies = false
changelog_config = "../git-cliff.toml"
allow_dirty = false
repo_url = "https://github.com/MarcoIeni/release-plz"
semver_check = "lib"
update_changelog = true
[workspace.git_release]
enable = true
release_type = "prod"
draft = false
"#;
let expected_config = Config {
workspace: Workspace {
update: UpdateConfig {
update_dependencies: false,
changelog_config: Some("../git-cliff.toml".into()),
allow_dirty: false,
repo_url: Some("https://github.com/MarcoIeni/release-plz".parse().unwrap()),
},
packages_defaults: PackageConfig {
update: PackageUpdateConfig {
semver_check: SemverCheck::Lib,
update_changelog: true.into(),
},
release: PackageReleaseConfig {
git_release: GitReleaseConfig {
enable: true.into(),
release_type: ReleaseType::Prod,
draft: false,
},
},
},
},
package: [].into(),
};
let config: Config = toml::from_str(config).unwrap();
assert_eq!(config, expected_config)
}
#[test]
fn config_is_serialized() {
let config = Config {
workspace: Workspace {
update: UpdateConfig {
update_dependencies: false,
changelog_config: Some("../git-cliff.toml".into()),
allow_dirty: false,
repo_url: Some("https://github.com/MarcoIeni/release-plz".parse().unwrap()),
},
packages_defaults: PackageConfig {
update: PackageUpdateConfig {
semver_check: SemverCheck::Lib,
update_changelog: true.into(),
},
release: PackageReleaseConfig {
git_release: GitReleaseConfig {
enable: true.into(),
release_type: ReleaseType::Prod,
draft: false,
},
},
},
},
package: [(
"crate1".to_string(),
PackageSpecificConfig {
update: PackageUpdateConfig {
semver_check: SemverCheck::No,
update_changelog: true.into(),
},
release: PackageReleaseConfig {
git_release: GitReleaseConfig {
enable: true.into(),
release_type: ReleaseType::Prod,
draft: false,
},
},
changelog_path: Some("./CHANGELOG.md".into()),
},
)]
.into(),
};
expect_test::expect![[r#"
[workspace]
update_dependencies = false
changelog_config = "../git-cliff.toml"
allow_dirty = false
repo_url = "https://github.com/MarcoIeni/release-plz"
semver_check = "lib"
update_changelog = true
[workspace.git_release]
enable = true
release_type = "prod"
draft = false
[package.crate1]
semver_check = "no"
update_changelog = true
changelog_path = "./CHANGELOG.md"
[package.crate1.git_release]
enable = true
release_type = "prod"
draft = false
"#]]
.assert_eq(&toml::to_string(&config).unwrap());
}
}