diff --git a/cargo-dist/src/backend/ci/github.rs b/cargo-dist/src/backend/ci/github.rs index 3d19c1dea..d20f049ff 100644 --- a/cargo-dist/src/backend/ci/github.rs +++ b/cargo-dist/src/backend/ci/github.rs @@ -302,7 +302,7 @@ impl GithubCiInfo { } let mut publish_jobs = vec![]; - if let Some(PublisherConfig { homebrew, npm }) = &dist.global_publishers { + if let Some(PublisherConfig { homebrew, npm, .. }) = &dist.global_publishers { if homebrew.is_some() { publish_jobs.push(PublishStyle::Homebrew.to_string()); } diff --git a/cargo-dist/src/config/v0_to_v1.rs b/cargo-dist/src/config/v0_to_v1.rs index 58859cd5f..2958078d2 100644 --- a/cargo-dist/src/config/v0_to_v1.rs +++ b/cargo-dist/src/config/v0_to_v1.rs @@ -339,8 +339,12 @@ impl DistMetadata { list_to_bool_layer(is_global, &publish_jobs, PublishStyle::Homebrew, || None); let npm_publisher_layer = list_to_bool_layer(is_global, &publish_jobs, PublishStyle::Npm, || None); + let user_layer = list_to_bool_layer_predicate(is_global, &publish_jobs, |job| { + matches!(job, PublishStyle::User(_)) + }); let needs_publisher_layer = homebrew_publisher_layer.is_some() || npm_publisher_layer.is_some() + || user_layer.is_some() || publish_prereleases.is_some(); let publisher_layer = needs_publisher_layer.then_some(PublisherLayer { common: CommonPublisherLayer { @@ -348,6 +352,7 @@ impl DistMetadata { }, homebrew: homebrew_publisher_layer, npm: npm_publisher_layer, + user: user_layer, }); // done! @@ -396,3 +401,25 @@ where Some(BoolOr::Bool(is_in_list)) } } + +fn list_to_bool_layer_predicate( + is_global: bool, + list: &Option>, + predicate: impl FnMut(&&I) -> bool, +) -> Option> +where + I: Eq, +{ + // If the list doesn't exist, don't mention it + let list = list.as_ref()?; + // Otherwise treat "is in the list" as a simple boolean + let is_in_list = list.iter().find(predicate).is_some(); + if is_global && !is_in_list { + // ... with the exception of an omitted value in the global list. + // here None and Some(false) are the same, so Some(false) is Noise + // we want to hide. + None + } else { + Some(BoolOr::Bool(is_in_list)) + } +} diff --git a/cargo-dist/src/config/v1/publishers/mod.rs b/cargo-dist/src/config/v1/publishers/mod.rs index 6ad4abf0d..19d22fc6f 100644 --- a/cargo-dist/src/config/v1/publishers/mod.rs +++ b/cargo-dist/src/config/v1/publishers/mod.rs @@ -2,11 +2,13 @@ pub mod homebrew; pub mod npm; +pub mod user; use super::*; use homebrew::*; use npm::*; +use user::*; /// the final publisher config #[derive(Debug, Default, Clone, PartialEq, Eq)] @@ -15,6 +17,8 @@ pub struct PublisherConfig { pub homebrew: Option, /// npm publisher pub npm: Option, + /// user specified publisher + pub user: Option, } /// the publisher config @@ -28,6 +32,8 @@ pub struct PublisherConfigInheritable { pub homebrew: Option, /// npm publisher pub npm: Option, + /// user specified publisher + pub user: Option, } /// "raw" publisher config from presum @@ -40,6 +46,8 @@ pub struct PublisherLayer { pub homebrew: Option>, /// npm publisher pub npm: Option>, + /// user-specified publisher + pub user: Option>, } impl PublisherConfigInheritable { /// get the defaults for a given package @@ -48,6 +56,7 @@ impl PublisherConfigInheritable { common: CommonPublisherConfig::defaults_for_package(workspaces, pkg_idx), homebrew: None, npm: None, + user: None, } } /// fold the inherited fields in to get the final publisher config @@ -60,6 +69,7 @@ impl PublisherConfigInheritable { common, homebrew, npm, + user, } = self; let homebrew = homebrew.map(|homebrew| { let mut default = @@ -73,7 +83,17 @@ impl PublisherConfigInheritable { default.apply_layer(npm); default }); - PublisherConfig { homebrew, npm } + let user = user.map(|user| { + let mut default = + UserPublisherConfig::defaults_for_package(workspaces, pkg_idx, &common); + default.apply_layer(user); + default + }); + PublisherConfig { + homebrew, + npm, + user, + } } } impl ApplyLayer for PublisherConfigInheritable { @@ -84,11 +104,13 @@ impl ApplyLayer for PublisherConfigInheritable { common, homebrew, npm, + user, }: Self::Layer, ) { self.common.apply_layer(common); self.homebrew.apply_bool_layer(homebrew); self.npm.apply_bool_layer(npm); + self.user.apply_bool_layer(user); } } diff --git a/cargo-dist/src/config/v1/publishers/user.rs b/cargo-dist/src/config/v1/publishers/user.rs new file mode 100644 index 000000000..afb0a3371 --- /dev/null +++ b/cargo-dist/src/config/v1/publishers/user.rs @@ -0,0 +1,49 @@ +//! user specified publisher config + +use super::*; + +/// Options for user specified publishes +#[derive(Debug, Default, Clone, Serialize, Deserialize)] +pub struct UserPublisherLayer { + /// Common options + pub common: CommonPublisherLayer, +} +/// Options for user specified publishes +#[derive(Debug, Default, Clone, PartialEq, Eq)] +pub struct UserPublisherConfig { + /// Common options + pub common: CommonPublisherConfig, +} + +impl UserPublisherConfig { + /// Get defaults for the given package + pub fn defaults_for_package( + _workspaces: &WorkspaceGraph, + _pkg_idx: PackageIdx, + common: &CommonPublisherConfig, + ) -> Self { + Self { + common: common.clone(), + } + } +} + +impl ApplyLayer for UserPublisherConfig { + type Layer = UserPublisherLayer; + fn apply_layer(&mut self, Self::Layer { common }: Self::Layer) { + self.common.apply_layer(common); + } +} +impl ApplyLayer for UserPublisherLayer { + type Layer = UserPublisherLayer; + fn apply_layer(&mut self, Self::Layer { common }: Self::Layer) { + self.common.apply_layer(common); + } +} + +impl std::ops::Deref for UserPublisherConfig { + type Target = CommonPublisherConfig; + fn deref(&self) -> &Self::Target { + &self.common + } +} diff --git a/cargo-dist/src/tasks/mod.rs b/cargo-dist/src/tasks/mod.rs index fe3f19897..7177dd0bf 100644 --- a/cargo-dist/src/tasks/mod.rs +++ b/cargo-dist/src/tasks/mod.rs @@ -1140,10 +1140,15 @@ impl<'pkg_graph> DistGraphBuilder<'pkg_graph> { .as_ref() .map(|p| { // until we have `dist publish` we need to enforce everyone agreeing on `prereleases` - let PublisherConfig { homebrew, npm } = p; + let PublisherConfig { + homebrew, + npm, + user, + } = p; let h_pre = homebrew.as_ref().map(|p| p.prereleases); let npm_pre = npm.as_ref().map(|p| p.prereleases); - let choices = [h_pre, npm_pre]; + let user_pre = user.as_ref().map(|p| p.prereleases); + let choices = [h_pre, npm_pre, user_pre]; let mut global_choice = None; #[allow(clippy::manual_flatten)] for choice in choices {