use std::fmt::{self, Display};
use serde::{Deserialize, Serialize};
use time::OffsetDateTime;
use url::Url;
pub const DEFAULT_URL: &str = "https://gitmoji.dev/api/gitmojis";
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum EmojiFormat {
UseCode,
UseEmoji,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(default)]
pub struct GitmojiConfig {
auto_add: bool,
format: EmojiFormat,
signed: bool,
scope: bool,
update_url: Url,
#[serde(with = "time::serde::iso8601::option")]
last_update: Option<OffsetDateTime>,
gitmojis: Vec<Gitmoji>,
}
impl GitmojiConfig {
#[must_use]
pub const fn new(
auto_add: bool,
format: EmojiFormat,
signed: bool,
scope: bool,
update_url: Url,
) -> Self {
Self {
auto_add,
format,
signed,
scope,
update_url,
last_update: None,
gitmojis: vec![],
}
}
pub fn merge(&mut self, local_config: &LocalGitmojiConfig) {
if let Some(auto_add) = local_config.auto_add() {
self.auto_add = auto_add;
}
if let Some(format) = local_config.format() {
self.format = format;
}
if let Some(signed) = local_config.signed() {
self.signed = signed;
}
if let Some(gitmojis) = local_config.gitmojis() {
self.gitmojis = gitmojis.to_vec();
}
}
#[must_use]
pub const fn auto_add(&self) -> bool {
self.auto_add
}
#[must_use]
pub const fn format(&self) -> &EmojiFormat {
&self.format
}
#[must_use]
pub const fn signed(&self) -> bool {
self.signed
}
#[must_use]
pub const fn scope(&self) -> bool {
self.scope
}
#[must_use]
pub fn update_url(&self) -> &str {
self.update_url.as_ref()
}
pub fn set_update_url(&mut self, update_url: Url) {
self.update_url = update_url;
}
#[must_use]
pub const fn last_update(&self) -> Option<OffsetDateTime> {
self.last_update
}
#[must_use]
pub fn gitmojis(&self) -> &[Gitmoji] {
self.gitmojis.as_ref()
}
pub fn set_gitmojis(&mut self, gitmojis: Vec<Gitmoji>) {
self.last_update = Some(OffsetDateTime::now_utc());
self.gitmojis = gitmojis;
}
}
impl Default for GitmojiConfig {
fn default() -> Self {
Self {
auto_add: false,
format: EmojiFormat::UseCode,
signed: false,
scope: false,
update_url: DEFAULT_URL.parse().expect("It's a valid URL"),
last_update: None,
gitmojis: vec![],
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
pub struct LocalGitmojiConfig {
auto_add: Option<bool>,
format: Option<EmojiFormat>,
signed: Option<bool>,
scope: Option<bool>,
gitmojis: Option<Vec<Gitmoji>>,
}
impl LocalGitmojiConfig {
#[must_use]
pub fn auto_add(&self) -> Option<bool> {
self.auto_add
}
#[must_use]
pub fn format(&self) -> Option<EmojiFormat> {
self.format
}
#[must_use]
pub fn signed(&self) -> Option<bool> {
self.signed
}
#[must_use]
pub fn scope(&self) -> Option<bool> {
self.scope
}
#[must_use]
pub fn gitmojis(&self) -> Option<&[Gitmoji]> {
self.gitmojis.as_deref()
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Gitmoji {
emoji: String,
code: String,
name: Option<String>,
description: Option<String>,
}
impl Gitmoji {
#[must_use]
pub fn new(
emoji: String,
code: String,
name: Option<String>,
description: Option<String>,
) -> Self {
Self {
emoji,
code,
name,
description,
}
}
#[must_use]
pub fn emoji(&self) -> &str {
self.emoji.as_ref()
}
#[must_use]
pub fn code(&self) -> &str {
self.code.as_ref()
}
#[must_use]
pub fn name(&self) -> Option<&str> {
self.name.as_deref()
}
#[must_use]
pub fn description(&self) -> Option<&str> {
self.description.as_deref()
}
}
impl Display for Gitmoji {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Gitmoji {
emoji,
code,
name,
description,
..
} = self;
write!(
f,
"{emoji} {code} {} - {}",
name.as_deref().unwrap_or_default(),
description.as_deref().unwrap_or_default()
)
}
}
#[cfg(test)]
mod tests {
use assert2::*;
use super::*;
#[test]
fn should_serde_gitmoji() {
let gitmoji = Gitmoji {
emoji: String::from("🚀"),
code: String::from("rocket"),
name: Some(String::from("Initialize")),
description: Some(String::from("Bla bla")),
};
let toml = toml::to_string(&gitmoji);
let_assert!(Ok(toml) = toml);
let result = toml::from_str::<Gitmoji>(&toml);
let_assert!(Ok(result) = result);
check!(result == gitmoji);
}
#[test]
fn should_serde_config() {
let mut config = GitmojiConfig::default();
config.gitmojis.push(Gitmoji {
emoji: String::from("🚀"),
code: String::from("rocket"),
name: Some(String::from("Initialize")),
description: Some(String::from("Bla bla")),
});
let toml = toml::to_string(&config);
let_assert!(Ok(toml) = toml);
let result = toml::from_str::<GitmojiConfig>(&toml);
let_assert!(Ok(result) = result);
check!(result == config);
}
}