use serde::{Deserialize, Serialize};
#[derive(Debug, Default, Deserialize, Serialize)]
pub struct PoiTrackerConfig {
#[serde(default)]
pub bugzilla: BugzillaConfig,
#[serde(default, rename = "dist-git")]
pub dist_git: DistGitConfig,
}
#[derive(Debug, Default, Deserialize, Serialize)]
pub struct DistGitConfig {
#[serde(default)]
pub api_token: String,
}
#[derive(Debug, Default, Deserialize, Serialize)]
pub struct BugzillaConfig {
#[serde(default)]
pub api_key: String,
#[serde(default)]
pub email: String,
#[serde(default)]
pub url: String,
}
impl BugzillaConfig {
pub const DEFAULT_URL: &'static str = "https://bugzilla.redhat.com";
}
pub fn resolve_api_key(cli_key: Option<&str>) -> Result<String, String> {
if let Some(key) = cli_key
&& !key.is_empty()
{
return Ok(key.to_string());
}
if let Ok(key) = std::env::var("BUGZILLA_API_KEY")
&& !key.is_empty()
{
return Ok(key);
}
if let Ok(config) =
sandogasa_config::ConfigFile::for_tool("poi-tracker").load::<PoiTrackerConfig>()
&& !config.bugzilla.api_key.is_empty()
{
return Ok(config.bugzilla.api_key);
}
Err("Bugzilla API key not found.\n\
Set it up with: poi-tracker config\n\
Or pass --api-key or set BUGZILLA_API_KEY."
.to_string())
}
pub fn resolve_distgit_token(cli_token: Option<&str>) -> Result<String, String> {
if let Some(token) = cli_token
&& !token.is_empty()
{
return Ok(token.to_string());
}
if let Ok(token) = std::env::var("PAGURE_API_TOKEN")
&& !token.is_empty()
{
return Ok(token);
}
if let Ok(config) =
sandogasa_config::ConfigFile::for_tool("poi-tracker").load::<PoiTrackerConfig>()
&& !config.dist_git.api_token.is_empty()
{
return Ok(config.dist_git.api_token);
}
Err("dist-git API token not found.\n\
Generate one at https://src.fedoraproject.org/settings/token/new\n\
with the \"Modify an existing project\" ACL, then set it up\n\
with: poi-tracker config\n\
Or pass --api-token or set PAGURE_API_TOKEN."
.to_string())
}
pub fn resolve_url() -> String {
sandogasa_config::ConfigFile::for_tool("poi-tracker")
.load::<PoiTrackerConfig>()
.ok()
.filter(|c| !c.bugzilla.url.is_empty())
.map(|c| c.bugzilla.url)
.unwrap_or_else(|| BugzillaConfig::DEFAULT_URL.to_string())
}
pub fn resolve_email() -> Option<String> {
sandogasa_config::ConfigFile::for_tool("poi-tracker")
.load::<PoiTrackerConfig>()
.ok()
.map(|c| c.bugzilla.email)
.filter(|e| !e.is_empty())
}
pub async fn cmd_config() -> Result<(), String> {
let cf = sandogasa_config::ConfigFile::for_tool("poi-tracker");
let mut config: PoiTrackerConfig = cf.load_user().unwrap_or_default();
println!("poi-tracker configuration\n");
println!("Config file: {}\n", cf.path().display());
if config.bugzilla.url.is_empty() {
config.bugzilla.url = BugzillaConfig::DEFAULT_URL.to_string();
}
println!("Bugzilla URL: {}", config.bugzilla.url);
if config.bugzilla.api_key.is_empty() {
println!(
"\nGenerate an API key at:\n \
https://bugzilla.redhat.com/userprefs.cgi?tab=apikey\n"
);
let key = sandogasa_config::prompt_field("Bugzilla", "API key", true, None)
.map_err(|e| format!("failed to read API key: {e}"))?;
config.bugzilla.api_key = key;
} else {
println!("Bugzilla API key: (set)");
}
let current = if config.bugzilla.email.is_empty() {
"<unset>"
} else {
config.bugzilla.email.as_str()
};
print!("Bugzilla email [{current}] (for --claim; blank to keep): ");
use std::io::{BufRead, Write};
std::io::stdout()
.flush()
.map_err(|e| format!("flush: {e}"))?;
let mut line = String::new();
std::io::stdin()
.lock()
.read_line(&mut line)
.map_err(|e| format!("read: {e}"))?;
let trimmed = line.trim();
if !trimmed.is_empty() {
config.bugzilla.email = trimmed.to_string();
}
if config.dist_git.api_token.is_empty() {
println!(
"\nOptional: a dist-git API token lets `adopt` take orphaned\n\
packages. Generate one at\n \
https://src.fedoraproject.org/settings/token/new\n\
with the \"Modify an existing project\" ACL."
);
print!("dist-git API token (blank to skip): ");
std::io::stdout()
.flush()
.map_err(|e| format!("flush: {e}"))?;
let token = rpassword::read_password().map_err(|e| format!("read token: {e}"))?;
let token = token.trim();
if !token.is_empty() {
config.dist_git.api_token = token.to_string();
}
} else {
println!("dist-git API token: (set)");
}
let url = config.bugzilla.url.clone();
config.bugzilla.api_key = sandogasa_cli::credential::validated_key(
"Bugzilla",
"API key",
config.bugzilla.api_key.clone(),
move |key| {
let url = url.clone();
Box::pin(async move {
use sandogasa_cli::credential::KeyVerdict;
let bz = match sandogasa_bugzilla::BzClient::new(&url).with_api_key(key) {
Ok(bz) => bz,
Err(e) => return KeyVerdict::Unknown(format!("could not check: {e}")),
};
match bz.api_key_accepted().await {
Ok(true) => KeyVerdict::Accepted("accepted.".to_string()),
Ok(false) => KeyVerdict::Refused("refused.".to_string()),
Err(e) => KeyVerdict::Unknown(format!("could not check: {e}")),
}
})
},
)
.await
.key;
cf.save(&config)
.map_err(|e| format!("failed to save config: {e}"))?;
println!("\nConfig saved to {}", cf.path().display());
Ok(())
}