use badgeland::{icon_exists, Badge, BadgeData, Color, Icon, Size, Style};
use clap::{ArgGroup, Parser};
use std::{convert::TryFrom, error::Error, fs::File, io::prelude::*, path::PathBuf, str::FromStr};
#[derive(Debug, PartialEq, Clone)]
enum Content {
Text(String),
Data(BadgeData),
}
impl FromStr for Content {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
BadgeData::from_str(s)
.map(|d| Content::Data(d))
.or(Ok(Content::Text(s.to_string())))
}
}
#[derive(Parser, Debug)]
#[clap(group = ArgGroup::new("style").required(false))]
struct StyleArg {
#[clap(short, long, group = "style", action)]
flat: bool,
#[clap(short, long, group = "style", action)]
classic: bool,
}
impl From<StyleArg> for Style {
fn from(s: StyleArg) -> Self {
if s.flat {
Self::Flat
} else {
Self::Classic
}
}
}
#[derive(Parser, Debug)]
#[clap(group = ArgGroup::new("size").required(false))]
struct SizeArg {
#[clap(short = 'x', long, group = "size", action)]
small: bool,
#[clap(short, long, group = "size", action)]
medium: bool,
#[clap(short, long, group = "size", action)]
large: bool,
}
impl From<SizeArg> for Size {
fn from(s: SizeArg) -> Self {
match (s.large, s.medium, s.small) {
(true, _, _) => Self::Large,
(_, true, _) => Self::Medium,
_ => Self::Small,
}
}
}
#[derive(Debug, Parser)]
struct Opt {
#[clap(short, long, value_parser)]
subject: Option<String>,
#[clap(flatten)]
style: StyleArg,
#[clap(flatten)]
size: SizeArg,
#[clap(long, value_parser)]
color: Option<Color>,
#[clap(long, value_parser)]
icon: Option<String>,
#[clap(long, value_parser)]
icon_color: Option<Color>,
#[clap(short, long, value_parser)]
out: Option<PathBuf>,
#[clap(value_parser)]
content: Content,
}
#[derive(Debug, Parser)]
#[clap(name = "cargo-badge", bin_name = "cargo")]
enum CargoCmd {
#[clap(name = "badge")]
Badge(Opt),
}
fn main() -> Result<(), Box<dyn Error>> {
let badge_cmd = CargoCmd::parse();
let CargoCmd::Badge(opt) = badge_cmd;
if matches!(&opt.icon, Some(icon) if !icon_exists(icon)) {
return Err("Icon does not exists. Try using a fontawesome icon name".into());
}
let mut badge = Badge::new();
if let Some(sub) = &opt.subject {
badge.subject(sub);
}
if let Some(col) = opt.color {
badge.color(col);
}
badge.style(opt.style.into());
badge.size(opt.size.into());
if let Some(icon) = &opt.icon {
let icon = Icon::try_from(icon.as_str());
if let Ok(i) = icon {
badge.icon(i);
}
if let Some(c) = opt.icon_color {
badge.icon_color(c);
}
}
let svg = match opt.content {
Content::Data(d) => badge.data(d.as_ref()).to_string(),
Content::Text(t) => badge.text(&t).to_string(),
};
if let Some(out_file) = opt.out {
let mut file = File::create(&out_file).unwrap();
file.write_all(svg.as_bytes()).unwrap();
} else {
println!("{}", svg);
}
Ok(())
}