1use serde::{Deserialize, Serialize};
7
8#[derive(Debug, Clone, Serialize, Deserialize)]
16pub struct Project {
17 pub id: String,
19
20 pub project_path: String,
22
23 pub name: String,
25
26 pub description: Option<String>,
28
29 pub issue_prefix: Option<String>,
31
32 #[serde(default = "default_one")]
34 pub next_issue_number: i32,
35
36 pub plan_prefix: Option<String>,
38
39 #[serde(default = "default_one")]
41 pub next_plan_number: i32,
42
43 pub created_at: i64,
45
46 pub updated_at: i64,
48}
49
50fn default_one() -> i32 {
51 1
52}
53
54impl Project {
55 pub fn new(project_path: String, name: String) -> Self {
57 let now = chrono::Utc::now().timestamp_millis();
58 let id = format!("proj_{}", &uuid::Uuid::new_v4().to_string()[..12]);
59
60 let issue_prefix = name
62 .chars()
63 .filter(|c| c.is_alphanumeric())
64 .take(4)
65 .collect::<String>()
66 .to_uppercase();
67
68 Self {
69 id,
70 project_path,
71 name,
72 description: None,
73 issue_prefix: Some(issue_prefix),
74 next_issue_number: 1,
75 plan_prefix: None,
76 next_plan_number: 1,
77 created_at: now,
78 updated_at: now,
79 }
80 }
81
82 pub fn next_issue_short_id(&self) -> String {
84 let prefix = self.issue_prefix.as_deref().unwrap_or("SC");
85 format!("{}-{}", prefix, self.next_issue_number)
86 }
87}
88
89#[cfg(test)]
90mod tests {
91 use super::*;
92
93 #[test]
94 fn test_new_project() {
95 let project = Project::new(
96 "/home/user/myproject".to_string(),
97 "My Project".to_string(),
98 );
99
100 assert!(project.id.starts_with("proj_"));
101 assert_eq!(project.project_path, "/home/user/myproject");
102 assert_eq!(project.name, "My Project");
103 assert_eq!(project.issue_prefix, Some("MYPR".to_string()));
104 assert_eq!(project.next_issue_number, 1);
105 }
106
107 #[test]
108 fn test_next_issue_short_id() {
109 let mut project = Project::new(
110 "/test".to_string(),
111 "Test".to_string(),
112 );
113 project.issue_prefix = Some("TEST".to_string());
114 project.next_issue_number = 42;
115
116 assert_eq!(project.next_issue_short_id(), "TEST-42");
117 }
118}