Skip to main content

max / goingson

2.0 KB · 71 lines History Blame Raw
1 //! Monthly review domain types.
2
3 use chrono::{DateTime, Utc};
4 use serde::{Deserialize, Serialize};
5 use crate::id_types::{MonthlyGoalId, MonthlyReflectionId, UserId};
6
7 // ============ Monthly Goal ============
8
9 /// Status of a monthly goal.
10 #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
11 #[serde(rename_all = "lowercase")]
12 pub enum MonthlyGoalStatus {
13 Active,
14 Done,
15 Abandoned,
16 }
17
18 impl MonthlyGoalStatus {
19 pub fn as_str(&self) -> &str {
20 match self {
21 Self::Active => "active",
22 Self::Done => "done",
23 Self::Abandoned => "abandoned",
24 }
25 }
26 }
27
28 impl std::str::FromStr for MonthlyGoalStatus {
29 type Err = crate::CoreError;
30
31 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
32 match s {
33 "active" => Ok(Self::Active),
34 "done" => Ok(Self::Done),
35 "abandoned" => Ok(Self::Abandoned),
36 _ => Err(crate::CoreError::parse(format!("Invalid goal status: {s}"))),
37 }
38 }
39 }
40
41 /// A monthly goal — free-text item set at the start of each month.
42 #[derive(Debug, Clone, Serialize, Deserialize)]
43 #[serde(rename_all = "camelCase")]
44 pub struct MonthlyGoal {
45 pub id: MonthlyGoalId,
46 pub user_id: UserId,
47 /// Month in YYYY-MM format.
48 pub month: String,
49 pub text: String,
50 pub status: MonthlyGoalStatus,
51 /// Position 1-3.
52 pub position: i32,
53 pub created_at: DateTime<Utc>,
54 pub updated_at: DateTime<Utc>,
55 }
56
57 // ============ Monthly Reflection ============
58
59 /// A monthly reflection capturing highlights and areas for improvement.
60 #[derive(Debug, Clone, Serialize, Deserialize)]
61 #[serde(rename_all = "camelCase")]
62 pub struct MonthlyReflection {
63 pub id: MonthlyReflectionId,
64 pub user_id: UserId,
65 /// Month in YYYY-MM format.
66 pub month: String,
67 pub highlight_text: String,
68 pub change_text: String,
69 pub completed_at: DateTime<Utc>,
70 }
71