use crate::config::PackageConfig;
use crate::publishers::{PublishCtx, PublishState, publisher_for};
#[derive(Debug, Clone)]
pub enum PublishOutcome {
NotConfigured { path: String },
AlreadyPublished { path: String, publisher: String },
Succeeded { path: String, publisher: String },
Failed {
path: String,
publisher: String,
message: String,
},
}
impl PublishOutcome {
pub fn path(&self) -> &str {
match self {
Self::NotConfigured { path }
| Self::AlreadyPublished { path, .. }
| Self::Succeeded { path, .. }
| Self::Failed { path, .. } => path,
}
}
pub fn is_failure(&self) -> bool {
matches!(self, Self::Failed { .. })
}
}
pub fn run_package_publish(
package: &PackageConfig,
version: &str,
tag: &str,
dry_run: bool,
env: &[(&str, &str)],
) -> PublishOutcome {
let Some(cfg) = package.publish.as_ref() else {
return PublishOutcome::NotConfigured {
path: package.path.clone(),
};
};
let publisher = publisher_for(cfg);
let pub_name = publisher.name().to_string();
let ctx = PublishCtx {
package,
version,
tag,
dry_run,
env,
};
match publisher.check(&ctx) {
Ok(PublishState::Completed) => {
eprintln!(
"{pub_name} ({}): already at {version} — skipping",
package.path
);
return PublishOutcome::AlreadyPublished {
path: package.path.clone(),
publisher: pub_name,
};
}
Ok(PublishState::Needed) => {}
Ok(PublishState::Unknown(reason)) => {
eprintln!(
"{pub_name} ({}): state check inconclusive ({reason}) — attempting publish",
package.path
);
}
Err(e) => {
return PublishOutcome::Failed {
path: package.path.clone(),
publisher: pub_name,
message: format!("check failed: {e}"),
};
}
}
match publisher.run(&ctx) {
Ok(()) => PublishOutcome::Succeeded {
path: package.path.clone(),
publisher: pub_name,
},
Err(e) => PublishOutcome::Failed {
path: package.path.clone(),
publisher: pub_name,
message: e.to_string(),
},
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::config::{PackageConfig, PublishConfig};
#[test]
fn not_configured_when_publish_is_none() {
let pkg = PackageConfig {
path: ".".into(),
..Default::default()
};
let outcome = run_package_publish(&pkg, "1.0.0", "v1.0.0", false, &[]);
assert!(matches!(outcome, PublishOutcome::NotConfigured { .. }));
}
#[test]
fn custom_run_succeeds() {
let pkg = PackageConfig {
path: ".".into(),
publish: Some(PublishConfig::Custom {
command: "true".into(),
check: None,
cwd: Some(".".into()),
}),
..Default::default()
};
let outcome = run_package_publish(&pkg, "1.0.0", "v1.0.0", false, &[]);
assert!(matches!(outcome, PublishOutcome::Succeeded { .. }));
}
#[test]
fn custom_run_failure_captured() {
let pkg = PackageConfig {
path: ".".into(),
publish: Some(PublishConfig::Custom {
command: "false".into(),
check: None,
cwd: Some(".".into()),
}),
..Default::default()
};
let outcome = run_package_publish(&pkg, "1.0.0", "v1.0.0", false, &[]);
assert!(matches!(outcome, PublishOutcome::Failed { .. }));
}
#[test]
fn custom_check_completed_short_circuits_run() {
let pkg = PackageConfig {
path: ".".into(),
publish: Some(PublishConfig::Custom {
command: "false".into(),
check: Some("true".into()),
cwd: Some(".".into()),
}),
..Default::default()
};
let outcome = run_package_publish(&pkg, "1.0.0", "v1.0.0", false, &[]);
assert!(matches!(outcome, PublishOutcome::AlreadyPublished { .. }));
}
#[test]
fn go_always_completed() {
let pkg = PackageConfig {
path: ".".into(),
publish: Some(PublishConfig::Go),
..Default::default()
};
let outcome = run_package_publish(&pkg, "1.0.0", "v1.0.0", false, &[]);
assert!(matches!(outcome, PublishOutcome::AlreadyPublished { .. }));
}
#[test]
fn unknown_check_still_runs() {
let pkg = PackageConfig {
path: ".".into(),
publish: Some(PublishConfig::Custom {
command: "true".into(),
check: None,
cwd: Some(".".into()),
}),
..Default::default()
};
let outcome = run_package_publish(&pkg, "1.0.0", "v1.0.0", false, &[]);
assert!(
matches!(outcome, PublishOutcome::Succeeded { .. }),
"Unknown state should proceed to run, got {outcome:?}"
);
}
#[test]
fn dry_run_skips_execution() {
let pkg = PackageConfig {
path: ".".into(),
publish: Some(PublishConfig::Custom {
command: "false".into(),
check: None,
cwd: Some(".".into()),
}),
..Default::default()
};
let outcome = run_package_publish(&pkg, "1.0.0", "v1.0.0", true, &[]);
assert!(matches!(outcome, PublishOutcome::Succeeded { .. }));
}
}