gitmoji_rs/
model.rs

1use std::fmt::{self, Display};
2
3use serde::{Deserialize, Serialize};
4use url::Url;
5
6/// The default URL used for update
7pub const DEFAULT_URL: &str = "https://gitmoji.dev/api/gitmojis";
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
10/// The emoji format
11pub enum EmojiFormat {
12    /// Use the code mode, like ':smile:'
13    UseCode,
14    /// Use the emoji mode, like '😄'
15    UseEmoji,
16}
17
18#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
19#[serde(default)]
20/// The Gitmojis configuration
21pub struct GitmojiConfig {
22    auto_add: bool,
23    format: EmojiFormat,
24    signed: bool,
25    scope: bool,
26    update_url: Url,
27    gitmojis: Vec<Gitmoji>,
28}
29
30impl GitmojiConfig {
31    /// Create a new `GitmojiConfig`
32    #[must_use]
33    pub const fn new(
34        auto_add: bool,
35        format: EmojiFormat,
36        signed: bool,
37        scope: bool,
38        update_url: Url,
39    ) -> Self {
40        Self {
41            auto_add,
42            format,
43            signed,
44            scope,
45            update_url,
46            gitmojis: vec![],
47        }
48    }
49
50    /// Merge with a local configuration
51    pub fn merge(&mut self, local_config: &LocalGitmojiConfig) {
52        if let Some(auto_add) = local_config.auto_add() {
53            self.auto_add = auto_add;
54        }
55        if let Some(format) = local_config.format() {
56            self.format = format;
57        }
58        if let Some(signed) = local_config.signed() {
59            self.signed = signed;
60        }
61        if let Some(gitmojis) = local_config.gitmojis() {
62            self.gitmojis = gitmojis.to_vec();
63        }
64    }
65
66    /// If the "--all" is added to commit command
67    #[must_use]
68    pub const fn auto_add(&self) -> bool {
69        self.auto_add
70    }
71
72    /// The format of gitmoji (code or emoji)
73    #[must_use]
74    pub const fn format(&self) -> &EmojiFormat {
75        &self.format
76    }
77
78    /// If the signed commits is enabled
79    #[must_use]
80    pub const fn signed(&self) -> bool {
81        self.signed
82    }
83
84    /// If we add a scope
85    #[must_use]
86    pub const fn scope(&self) -> bool {
87        self.scope
88    }
89
90    /// The URL used for update
91    #[must_use]
92    pub fn update_url(&self) -> &str {
93        self.update_url.as_ref()
94    }
95
96    /// Set the URL used for update
97    pub fn set_update_url(&mut self, update_url: Url) {
98        self.update_url = update_url;
99    }
100
101    /// The gitmoji list
102    #[must_use]
103    pub fn gitmojis(&self) -> &[Gitmoji] {
104        self.gitmojis.as_ref()
105    }
106
107    /// Set the gitmojis list
108    pub fn set_gitmojis(&mut self, gitmojis: Vec<Gitmoji>) {
109        self.gitmojis = gitmojis;
110    }
111}
112
113impl Default for GitmojiConfig {
114    fn default() -> Self {
115        Self {
116            auto_add: false,
117            format: EmojiFormat::UseCode,
118            signed: false,
119            scope: false,
120            update_url: DEFAULT_URL.parse().expect("It's a valid URL"),
121            gitmojis: vec![],
122        }
123    }
124}
125
126#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
127/// The local gitmoji configuration
128pub struct LocalGitmojiConfig {
129    auto_add: Option<bool>,
130    format: Option<EmojiFormat>,
131    signed: Option<bool>,
132    scope: Option<bool>,
133    gitmojis: Option<Vec<Gitmoji>>,
134}
135
136impl LocalGitmojiConfig {
137    /// If the "--all" is added to commit command
138    #[must_use]
139    pub fn auto_add(&self) -> Option<bool> {
140        self.auto_add
141    }
142
143    /// The format of gitmoji (code or emoji)
144    #[must_use]
145    pub fn format(&self) -> Option<EmojiFormat> {
146        self.format
147    }
148
149    /// If the signed commits is enabled
150    #[must_use]
151    pub fn signed(&self) -> Option<bool> {
152        self.signed
153    }
154
155    /// If we add a scope
156    #[must_use]
157    pub fn scope(&self) -> Option<bool> {
158        self.scope
159    }
160
161    /// The gitmoji list
162    #[must_use]
163    pub fn gitmojis(&self) -> Option<&[Gitmoji]> {
164        self.gitmojis.as_deref()
165    }
166}
167
168#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
169/// A Gitmoji
170pub struct Gitmoji {
171    emoji: String,
172    code: String,
173    name: Option<String>,
174    description: Option<String>,
175}
176
177impl Gitmoji {
178    /// Create a gitmoji
179    #[must_use]
180    pub fn new(
181        emoji: String,
182        code: String,
183        name: Option<String>,
184        description: Option<String>,
185    ) -> Self {
186        Self {
187            emoji,
188            code,
189            name,
190            description,
191        }
192    }
193
194    /// The emoji
195    #[must_use]
196    pub fn emoji(&self) -> &str {
197        self.emoji.as_ref()
198    }
199
200    /// The associated code
201    #[must_use]
202    pub fn code(&self) -> &str {
203        self.code.as_ref()
204    }
205
206    /// The name
207    #[must_use]
208    pub fn name(&self) -> Option<&str> {
209        self.name.as_deref()
210    }
211
212    /// The description
213    #[must_use]
214    pub fn description(&self) -> Option<&str> {
215        self.description.as_deref()
216    }
217}
218
219impl Display for Gitmoji {
220    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
221        let Gitmoji {
222            emoji,
223            code,
224            name,
225            description,
226            ..
227        } = self;
228        write!(
229            f,
230            "{emoji} {code} {} - {}",
231            name.as_deref().unwrap_or_default(),
232            description.as_deref().unwrap_or_default()
233        )
234    }
235}
236
237#[cfg(test)]
238#[allow(clippy::ignored_unit_patterns)]
239mod tests {
240    use assert2::*;
241
242    use super::*;
243
244    #[test]
245    fn should_serde_gitmoji() {
246        let gitmoji = Gitmoji {
247            emoji: String::from("🚀"),
248            code: String::from("rocket"),
249            name: Some(String::from("Initialize")),
250            description: Some(String::from("Bla bla")),
251        };
252
253        // Serialize
254        let toml = toml_edit::ser::to_string(&gitmoji);
255        let_assert!(Ok(toml) = toml);
256
257        // Deserialize
258        let result = toml_edit::de::from_str::<Gitmoji>(&toml);
259        let_assert!(Ok(result) = result);
260
261        check!(result == gitmoji);
262    }
263
264    #[test]
265    fn should_serde_config() {
266        let mut config = GitmojiConfig::default();
267        config.gitmojis.push(Gitmoji {
268            emoji: String::from("🚀"),
269            code: String::from("rocket"),
270            name: Some(String::from("Initialize")),
271            description: Some(String::from("Bla bla")),
272        });
273
274        // Serialize
275        let toml = toml_edit::ser::to_string(&config);
276        let_assert!(Ok(toml) = toml);
277
278        // Deserialize
279        let result = toml_edit::de::from_str::<GitmojiConfig>(&toml);
280        let_assert!(Ok(result) = result);
281
282        check!(result == config);
283    }
284}