Skip to main content

fido_types/
models.rs

1use chrono::{DateTime, Utc};
2use serde::{Deserialize, Serialize};
3use uuid::Uuid;
4
5use crate::enums::{ColorScheme, SortOrder, VoteDirection};
6
7// Custom serde module for DateTime to ensure RFC3339 string format
8mod datetime_format {
9    use chrono::{DateTime, Utc};
10    use serde::{self, Deserialize, Deserializer, Serializer};
11
12    pub fn serialize<S>(date: &DateTime<Utc>, serializer: S) -> Result<S::Ok, S::Error>
13    where
14        S: Serializer,
15    {
16        let s = date.to_rfc3339();
17        serializer.serialize_str(&s)
18    }
19
20    pub fn deserialize<'de, D>(deserializer: D) -> Result<DateTime<Utc>, D::Error>
21    where
22        D: Deserializer<'de>,
23    {
24        let s = String::deserialize(deserializer)?;
25        s.parse::<DateTime<Utc>>().map_err(serde::de::Error::custom)
26    }
27}
28
29#[derive(Debug, Clone, Serialize, Deserialize)]
30pub struct User {
31    pub id: Uuid,
32    pub username: String,
33    pub bio: Option<String>,
34    #[serde(with = "datetime_format")]
35    pub join_date: DateTime<Utc>,
36    pub is_test_user: bool,
37    #[serde(default)]
38    pub is_admin: bool,
39}
40
41#[derive(Debug, Clone, Serialize, Deserialize)]
42pub struct Post {
43    pub id: Uuid,
44    pub author_id: Uuid,
45    pub author_username: String,
46    pub content: String,
47    #[serde(with = "datetime_format")]
48    pub created_at: DateTime<Utc>,
49    pub upvotes: i32,
50    pub downvotes: i32,
51    pub hashtags: Vec<String>,
52    /// User's vote on this post (if authenticated)
53    #[serde(default)]
54    pub user_vote: Option<String>,
55    /// Parent post ID for replies (None for top-level posts)
56    #[serde(default)]
57    pub parent_post_id: Option<Uuid>,
58    /// Number of replies to this post
59    #[serde(default)]
60    pub reply_count: i32,
61    /// User ID being replied to (for @mentions in replies)
62    #[serde(default)]
63    pub reply_to_user_id: Option<Uuid>,
64    /// Username being replied to (for display purposes)
65    #[serde(default)]
66    pub reply_to_username: Option<String>,
67}
68
69#[derive(Debug, Clone, Serialize, Deserialize)]
70pub struct Vote {
71    pub user_id: Uuid,
72    pub post_id: Uuid,
73    pub direction: VoteDirection,
74    #[serde(with = "datetime_format")]
75    pub created_at: DateTime<Utc>,
76}
77
78#[derive(Debug, Clone, Serialize, Deserialize)]
79pub struct DirectMessage {
80    pub id: Uuid,
81    pub from_user_id: Uuid,
82    pub to_user_id: Uuid,
83    #[serde(default)]
84    pub from_username: String,
85    #[serde(default)]
86    pub to_username: String,
87    pub content: String,
88    #[serde(with = "datetime_format")]
89    pub created_at: DateTime<Utc>,
90    pub is_read: bool,
91}
92
93#[derive(Debug, Clone, Serialize, Deserialize)]
94pub struct UserProfile {
95    pub user_id: Uuid,
96    pub username: String,
97    pub bio: Option<String>,
98    pub karma: i32,
99    pub post_count: i32,
100    #[serde(with = "datetime_format")]
101    pub join_date: DateTime<Utc>,
102    pub recent_hashtags: Vec<String>,
103}
104
105#[derive(Debug, Clone, Serialize, Deserialize)]
106pub struct UserProfileView {
107    pub id: String,
108    pub username: String,
109    pub bio: Option<String>,
110    pub join_date: String,
111    pub follower_count: usize,
112    pub following_count: usize,
113    pub post_count: usize,
114    pub relationship: RelationshipStatus,
115}
116
117#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
118#[serde(tag = "type", rename_all = "snake_case")]
119pub enum RelationshipStatus {
120    #[serde(rename = "self")]
121    Self_,
122    MutualFriends,
123    Following,
124    FollowsYou,
125    None,
126}
127
128#[derive(Debug, Clone, Serialize, Deserialize)]
129pub struct UserConfig {
130    pub user_id: Uuid,
131    pub color_scheme: ColorScheme,
132    pub sort_order: SortOrder,
133    pub max_posts_display: i32,
134    pub emoji_enabled: bool,
135}
136
137impl Default for UserConfig {
138    fn default() -> Self {
139        Self {
140            user_id: Uuid::nil(),
141            color_scheme: ColorScheme::default(),
142            sort_order: SortOrder::default(),
143            max_posts_display: 25,
144            emoji_enabled: true,
145        }
146    }
147}
148
149// Request/Response types for API
150#[derive(Debug, Serialize, Deserialize)]
151pub struct CreatePostRequest {
152    pub content: String,
153}
154
155#[derive(Debug, Serialize, Deserialize)]
156pub struct CreateReplyRequest {
157    pub content: String,
158}
159
160#[derive(Debug, Serialize, Deserialize)]
161pub struct UpdatePostRequest {
162    pub content: String,
163}
164
165#[derive(Debug, Serialize, Deserialize)]
166pub struct VoteRequest {
167    pub direction: String,
168}
169
170#[derive(Debug, Serialize, Deserialize)]
171pub struct SendMessageRequest {
172    pub to_username: String,
173    pub content: String,
174}
175
176#[derive(Debug, Serialize, Deserialize)]
177pub struct UpdateBioRequest {
178    pub bio: String,
179}
180
181#[derive(Debug, Serialize, Deserialize)]
182pub struct UpdateConfigRequest {
183    pub color_scheme: Option<String>,
184    pub sort_order: Option<String>,
185    pub max_posts_display: Option<i32>,
186    pub emoji_enabled: Option<bool>,
187}
188
189#[derive(Debug, Serialize, Deserialize)]
190pub struct LoginRequest {
191    pub username: String,
192}
193
194#[derive(Debug, Serialize, Deserialize)]
195pub struct LoginResponse {
196    pub user: User,
197    pub session_token: String,
198}
199
200#[derive(Debug, Serialize, Deserialize)]
201pub struct ErrorResponse {
202    pub error: String,
203    pub details: Option<String>,
204}