use std::collections::BTreeMap;
use percent_encoding as pe;
const BADGE_BRANCH_DEFAULT: &str = "master";
const BADGE_SERVICE_DEFAULT: &str = "github";
const BADGE_WORKFLOW_DEFAULT: &str = "main";
type Attrs = BTreeMap<String, String>;
pub struct BadgeInfo {
pub key: &'static str,
pub official: bool,
pub required: &'static [&'static str],
pub optional: &'static [&'static str],
}
pub const SUPPORTED_BADGES: &[BadgeInfo] = &[
BadgeInfo {
key: "crates-io",
official: false,
required: &[],
optional: &["crate"],
},
BadgeInfo {
key: "appveyor",
official: true,
required: &["repository"],
optional: &["branch", "service"],
},
BadgeInfo {
key: "circle-ci",
official: true,
required: &["repository"],
optional: &["branch", "service"],
},
BadgeInfo {
key: "gitlab",
official: true,
required: &["repository"],
optional: &["branch"],
},
BadgeInfo {
key: "travis-ci",
official: true,
required: &["repository"],
optional: &["branch"],
},
BadgeInfo {
key: "github",
official: false,
required: &["repository"],
optional: &["workflow"],
},
BadgeInfo {
key: "codecov",
official: true,
required: &["repository"],
optional: &["branch", "service"],
},
BadgeInfo {
key: "coveralls",
official: true,
required: &["repository"],
optional: &["branch", "service"],
},
BadgeInfo {
key: "is-it-maintained-issue-resolution",
official: true,
required: &["repository"],
optional: &[],
},
BadgeInfo {
key: "is-it-maintained-open-issues",
official: true,
required: &["repository"],
optional: &[],
},
BadgeInfo {
key: "maintenance",
official: true,
required: &["status"],
optional: &[],
},
];
pub fn appveyor(attrs: Attrs) -> Result<String, String> {
let repo = required(&attrs, "appveyor", "repository")?;
let branch = attrs
.get("branch")
.map(|i| i.as_ref())
.unwrap_or(BADGE_BRANCH_DEFAULT);
let service = attrs
.get("service")
.map(|i| i.as_ref())
.unwrap_or(BADGE_SERVICE_DEFAULT);
Ok(format!(
"[]\
(https://ci.appveyor.com/project/{repo}/branch/{branch})",
repo=repo, branch=branch, service=service
))
}
pub fn circle_ci(attrs: Attrs) -> Result<String, String> {
let repo = required(&attrs, "circle-ci", "repository")?;
let branch = attrs
.get("branch")
.map(|i| i.as_ref())
.unwrap_or(BADGE_BRANCH_DEFAULT);
let service = badge_service_short_name(
attrs
.get("service")
.map(|i| i.as_ref())
.unwrap_or(BADGE_SERVICE_DEFAULT),
);
Ok(format!(
"[]\
(https://circleci.com/{service}/{repo}/tree/{branch})",
repo = repo,
service = service,
branch = percent_encode(branch)
))
}
pub fn gitlab(attrs: Attrs) -> Result<String, String> {
let repo = required(&attrs, "gitlab", "repository")?;
let branch = attrs
.get("branch")
.map(|i| i.as_ref())
.unwrap_or(BADGE_BRANCH_DEFAULT);
Ok(format!(
"[]\
(https://gitlab.com/{repo}/commits/master)",
repo = repo,
branch = percent_encode(branch)
))
}
pub fn travis_ci(attrs: Attrs) -> Result<String, String> {
let repo = required(&attrs, "travis-ci", "repository")?;
let branch = attrs
.get("branch")
.map(|i| i.as_ref())
.unwrap_or(BADGE_BRANCH_DEFAULT);
Ok(format!(
"[]\
(https://travis-ci.org/{repo})",
repo = repo,
branch = percent_encode(branch)
))
}
pub fn github(attrs: Attrs) -> Result<String, String> {
let repo = required(&attrs, "github", "repository")?;
let workflow = attrs
.get("workflow")
.map(|i| i.as_ref())
.unwrap_or(BADGE_WORKFLOW_DEFAULT);
Ok(format!(
"[]\
(https://github.com/{repo}/actions?query=workflow%3A%22{workflow_plus}%22)",
repo = repo,
workflow = percent_encode(workflow),
workflow_plus = percent_encode(&str::replace(workflow, " ", "+"))
))
}
pub fn codecov(attrs: Attrs) -> Result<String, String> {
let repo = required(&attrs, "codecov", "repository")?;
let branch = attrs
.get("branch")
.map(|i| i.as_ref())
.unwrap_or(BADGE_BRANCH_DEFAULT);
let service = badge_service_short_name(
attrs
.get("service")
.map(|i| i.as_ref())
.unwrap_or(BADGE_SERVICE_DEFAULT),
);
Ok(format!(
"[]\
(https://codecov.io/{service}/{repo})",
repo = repo,
branch = percent_encode(branch),
service = service
))
}
pub fn coveralls(attrs: Attrs) -> Result<String, String> {
let repo = required(&attrs, "coveralls", "repository")?;
let branch = attrs
.get("branch")
.map(|i| i.as_ref())
.unwrap_or(BADGE_BRANCH_DEFAULT);
let service = attrs
.get("service")
.map(|i| i.as_ref())
.unwrap_or(BADGE_SERVICE_DEFAULT);
Ok(format!(
"[]\
(https://coveralls.io/{service}/{repo}?branch={branch})",
repo = repo,
branch = percent_encode(branch),
service = service
))
}
pub fn is_it_maintained_issue_resolution(attrs: Attrs) -> Result<String, String> {
let repo = required(&attrs, "is-it-maintained-issue-resolution", "repository")?;
Ok(format!(
"[]\
(https://isitmaintained.com/project/{repo} \"Average time to resolve an issue\")",
repo=repo
))
}
pub fn is_it_maintained_open_issues(attrs: Attrs) -> Result<String, String> {
let repo = required(&attrs, "is-it-maintained-open-issues", "repository")?;
Ok(format!(
"[]\
(https://isitmaintained.com/project/{repo} \"Percentage of issues still open\")",
repo = repo
))
}
pub fn maintenance(attrs: Attrs) -> Result<String, String> {
let status = required(&attrs, "maintenance", "status")?;
let status_with_color = match status {
"actively-developed" => "actively--developed-brightgreen",
"passively-maintained" => "passively--maintained-yellowgreen",
"as-is" => "as--is-yellow",
"none" => "maintenance-none-lightgrey", "experimental" => "experimental-blue",
"looking-for-maintainer" => "looking--for--maintainer-darkblue", "deprecated" => "deprecated-red",
_ => "unknown-black",
};
Ok(format!(
"",
status = status_with_color
))
}
pub fn crates_io(attrs: Attrs, crate_name: &str) -> Result<String, String> {
let name = attrs.get("crate").map(|s| s.as_ref()).unwrap_or(crate_name);
Ok(format!(
"[](https://crates.io/crates/{name})",
name = name
))
}
fn required<'a>(attrs: &'a Attrs, badge: &str, key: &str) -> Result<&'a str, String> {
attrs
.get(key)
.map(String::as_str)
.ok_or_else(|| format!("badge `{badge}` is missing required attribute `{key}`"))
}
fn percent_encode(input: &str) -> pe::PercentEncode<'_> {
pe::utf8_percent_encode(input, pe::NON_ALPHANUMERIC)
}
fn badge_service_short_name(service: &str) -> &'static str {
match service {
"github" => "gh",
"bitbucket" => "bb",
"gitlab" => "gl",
_ => "gh",
}
}