#![warn(missing_docs)]
#![forbid(unsafe_code)]
use std::borrow::Cow;
use error::BadgeError;
pub mod error;
#[cfg(feature = "font-shape")]
mod font;
mod template;
#[derive(Clone, Debug, PartialEq, Eq)]
#[allow(dead_code)]
pub enum BadgeColor {
Green,
LightGreen,
Yellow,
Red,
LightGrey,
Grey,
CustomRgb(u8, u8, u8),
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "cli", derive(clap::ValueEnum))]
pub enum BadgeStyle {
Flat,
FlatSquare,
}
impl BadgeColor {
pub fn as_hex_str(&self) -> Cow<'static, str> {
match self {
Self::Green => "#4c1".into(),
Self::LightGreen => "#a3c51c".into(),
Self::Yellow => "#dfb317".into(),
Self::Red => "#e05d44".into(),
Self::LightGrey => "#9f9f9f".into(),
Self::Grey => "#555".into(),
Self::CustomRgb(r, g, b) => format!("#{:02x}{:02x}{:02x}", r, g, b).into(),
}
}
fn parse_hex_color(color: &str) -> Result<u8, ()> {
let byte = u8::from_str_radix(color, 16).map_err(|_| ())?;
if color.len() == 1 {
Ok(byte * 16 + byte)
} else if color.len() == 2 {
Ok(byte)
} else {
Err(())
}
}
}
impl<'a> TryFrom<&'a str> for BadgeColor {
type Error = BadgeError;
fn try_from(value: &'a str) -> Result<Self, Self::Error> {
match value {
"grey" | "gray" => Ok(Self::Grey),
"light-grey" | "light-gray" => Ok(Self::LightGrey),
"red" => Ok(Self::Red),
"yellow" => Ok(Self::Yellow),
"green" => Ok(Self::Green),
"light-green" => Ok(Self::LightGreen),
_ => {
let Some(hex_color) = value.strip_prefix('#') else {
return Err(BadgeError::InvalidColor(value.to_string()))
};
let (r, g, b) = match hex_color.len() {
3 => {
let r = Self::parse_hex_color(&hex_color[0..=0])
.map_err(|_| BadgeError::invalid_color(value))?;
let g = Self::parse_hex_color(&hex_color[1..=1])
.map_err(|_| BadgeError::invalid_color(value))?;
let b = Self::parse_hex_color(&hex_color[2..=2])
.map_err(|_| BadgeError::invalid_color(value))?;
(r, g, b)
}
6 => {
let r = Self::parse_hex_color(&hex_color[0..=1])
.map_err(|_| BadgeError::invalid_color(value))?;
let g = Self::parse_hex_color(&hex_color[2..=3])
.map_err(|_| BadgeError::invalid_color(value))?;
let b = Self::parse_hex_color(&hex_color[4..=5])
.map_err(|_| BadgeError::invalid_color(value))?;
(r, g, b)
}
_ => return Err(BadgeError::InvalidColor(value.to_string())),
};
Ok(Self::CustomRgb(r, g, b))
}
}
}
}
pub struct BadgeBuilder<'a> {
style: BadgeStyle,
label: &'a str,
label_color: BadgeColor,
message: &'a str,
message_color: BadgeColor,
}
impl<'a> Default for BadgeBuilder<'a> {
fn default() -> Self {
Self::new()
}
}
impl<'a> BadgeBuilder<'a> {
pub fn new() -> Self {
Self {
style: BadgeStyle::Flat,
label: "badge",
label_color: BadgeColor::Grey,
message: "example",
message_color: BadgeColor::LightGrey,
}
}
pub fn style(mut self, style: BadgeStyle) -> Self {
self.style = style;
self
}
pub fn label(mut self, text: &'a str) -> Self {
self.label = text;
self
}
pub fn label_color(mut self, color: BadgeColor) -> Self {
self.label_color = color;
self
}
pub fn message(mut self, text: &'a str) -> Self {
self.message = text;
self
}
pub fn message_color(mut self, color: BadgeColor) -> Self {
self.message_color = color;
self
}
pub fn render(self) -> Result<String, BadgeError> {
use askama::Template;
let badge = template::BadgeTemplate::new(
self.style,
self.label,
self.label_color,
self.message,
self.message_color,
);
Ok(badge.render()?)
}
}
#[cfg(test)]
mod test {
use super::*;
use pretty_assertions::assert_eq;
#[test]
fn test_parse() {
assert_eq!(
BadgeColor::Green,
BadgeColor::try_from("green").expect("failed to parse")
);
assert_eq!(
BadgeColor::LightGreen,
BadgeColor::try_from("light-green").expect("failed to parse")
);
assert_eq!(
BadgeColor::Yellow,
BadgeColor::try_from("yellow").expect("failed to parse")
);
assert_eq!(
BadgeColor::Red,
BadgeColor::try_from("red").expect("failed to parse")
);
assert_eq!(
BadgeColor::Grey,
BadgeColor::try_from("grey").expect("failed to parse")
);
assert_eq!(
BadgeColor::Grey,
BadgeColor::try_from("gray").expect("failed to parse")
);
BadgeColor::try_from("").expect_err("parsing should have failed");
BadgeColor::try_from("abcdefgh").expect_err("parsing should have failed");
BadgeColor::try_from("123").expect_err("parsing should have failed");
BadgeColor::try_from("aaa").expect_err("parsing should have failed");
BadgeColor::try_from("#a").expect_err("parsing should have failed");
BadgeColor::try_from("#aag").expect_err("parsing should have failed");
BadgeColor::try_from("#aaaaa").expect_err("parsing should have failed");
BadgeColor::try_from("#aaaaag").expect_err("parsing should have failed");
assert_eq!(
BadgeColor::CustomRgb(170, 170, 170),
BadgeColor::try_from("#aaaaaa").expect("failed to parse")
);
assert_eq!(
BadgeColor::CustomRgb(170, 170, 170),
BadgeColor::try_from("#aaa").expect("failed to parse")
);
assert_eq!(
BadgeColor::CustomRgb(171, 205, 239),
BadgeColor::try_from("#abcdef").expect("failed to parse")
);
}
#[test]
fn test_as_str() {
assert_eq!(
"#aaaaaa",
BadgeColor::try_from("#aaaaaa")
.expect("failed to parse")
.as_hex_str(),
);
assert_eq!(
"#aaaaaa",
BadgeColor::try_from("#AAA")
.expect("failed to parse")
.as_hex_str(),
);
assert_eq!(
"#010203",
BadgeColor::try_from("#010203")
.expect("failed to parse")
.as_hex_str(),
);
}
}