use std::path::PathBuf;
use anyhow::{anyhow, Context};
use chrono::{Date, NaiveDate, Utc};
use release_plz_core::{ChangelogRequest, GitHub, UpdateRequest, CARGO_TOML};
use secrecy::SecretString;
use url::Url;
#[derive(clap::Parser, Debug)]
#[clap(about, version, author)]
pub struct CliArgs {
#[clap(subcommand)]
pub command: Command,
}
#[derive(clap::Subcommand, Debug)]
pub enum Command {
Update(Update),
ReleasePr(ReleasePr),
}
#[derive(clap::Parser, Debug)]
pub struct Update {
#[clap(long)]
project_manifest: Option<PathBuf>,
#[clap(long)]
registry_project_manifest: Option<PathBuf>,
#[clap(short, long)]
package: Option<String>,
#[clap(long, conflicts_with("release-date"))]
no_changelog: bool,
#[clap(long, conflicts_with("no-changelog"))]
release_date: Option<String>,
#[clap(long, conflicts_with("registry-project-manifest"))]
registry: Option<String>,
}
#[derive(clap::Parser, Debug)]
pub struct ReleasePr {
#[clap(flatten)]
pub update: Update,
#[clap(long)]
pub github_token: SecretString,
#[clap(long)]
pub repo_url: Url,
}
impl Update {
pub fn update_request(&self) -> anyhow::Result<UpdateRequest> {
let mut update = UpdateRequest::new(self.local_manifest()).with_context(|| {
format!("cannot find {CARGO_TOML} file. Make sure you are inside a rust project")
})?;
if let Some(registry_project_manifest) = &self.registry_project_manifest {
update = update
.with_registry_project_manifest(registry_project_manifest.clone())
.with_context(|| {
format!("cannot find project manifest {registry_project_manifest:?}")
})?;
}
if !self.no_changelog {
let release_date = self
.release_date
.as_ref()
.map(|date| {
NaiveDate::parse_from_str(date, "%Y-%m-%d")
.context("cannot parse release_date to y-m-d format")
})
.transpose()?
.map(|date| Date::<Utc>::from_utc(date, Utc));
update = update.with_changelog(ChangelogRequest { release_date });
}
if let Some(package) = &self.package {
update = update.with_single_package(package.clone());
}
if let Some(registry) = &self.registry {
update = update.with_registry(registry.clone());
}
Ok(update)
}
fn local_manifest(&self) -> PathBuf {
match &self.project_manifest {
Some(manifest) => manifest.clone(),
None => std::env::current_dir()
.expect("cannot retrieve current directory")
.join(CARGO_TOML),
}
}
}
impl ReleasePr {
pub fn github(&self) -> anyhow::Result<GitHub> {
let segments = self
.repo_url
.path_segments()
.map(|c| c.collect::<Vec<_>>())
.ok_or_else(|| {
anyhow!(
"cannot find github owner and repo from url {}",
self.repo_url
)
})?;
let owner = segments
.get(0)
.ok_or_else(|| anyhow!("cannot find github owner from url {}", self.repo_url))?
.to_string();
let repo = segments
.get(1)
.ok_or_else(|| anyhow!("cannot find github repo from url {}", self.repo_url))?
.to_string();
Ok(GitHub::new(owner, repo, self.github_token.clone()))
}
}