1use chrono::{DateTime, Utc};
2use serde::{Deserialize, Serialize};
3
4#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
5pub struct Tag {
6 pub id: Option<i64>,
7 pub name: String,
8 pub color: Option<String>,
9 pub description: Option<String>,
10 pub created_at: DateTime<Utc>,
11}
12
13impl Tag {
14 pub fn new(name: String) -> Self {
15 Self {
16 id: None,
17 name: name.trim().to_lowercase(),
18 color: None,
19 description: None,
20 created_at: Utc::now(),
21 }
22 }
23
24 pub fn with_color(mut self, color: String) -> Self {
25 self.color = Some(color);
26 self
27 }
28
29 pub fn with_description(mut self, description: String) -> Self {
30 self.description = Some(description);
31 self
32 }
33
34 pub fn validate(&self) -> anyhow::Result<()> {
35 if self.name.is_empty() {
36 return Err(anyhow::anyhow!("Tag name cannot be empty"));
37 }
38
39 if self.name != self.name.trim().to_lowercase() {
40 return Err(anyhow::anyhow!("Tag name must be lowercase and trimmed"));
41 }
42
43 if self.name.contains(char::is_whitespace) {
44 return Err(anyhow::anyhow!("Tag name cannot contain whitespace"));
45 }
46
47 Ok(())
48 }
49}