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}
38
39#[derive(Debug, Clone, Serialize, Deserialize)]
40pub struct Post {
41    pub id: Uuid,
42    pub author_id: Uuid,
43    pub author_username: String,
44    pub content: String,
45    #[serde(with = "datetime_format")]
46    pub created_at: DateTime<Utc>,
47    pub upvotes: i32,
48    pub downvotes: i32,
49    pub hashtags: Vec<String>,
50    /// User's vote on this post (if authenticated)
51    #[serde(default)]
52    pub user_vote: Option<String>,
53    /// Parent post ID for replies (None for top-level posts)
54    #[serde(default)]
55    pub parent_post_id: Option<Uuid>,
56    /// Number of replies to this post
57    #[serde(default)]
58    pub reply_count: i32,
59    /// User ID being replied to (for @mentions in replies)
60    #[serde(default)]
61    pub reply_to_user_id: Option<Uuid>,
62    /// Username being replied to (for display purposes)
63    #[serde(default)]
64    pub reply_to_username: Option<String>,
65}
66
67#[derive(Debug, Clone, Serialize, Deserialize)]
68pub struct Vote {
69    pub user_id: Uuid,
70    pub post_id: Uuid,
71    pub direction: VoteDirection,
72    #[serde(with = "datetime_format")]
73    pub created_at: DateTime<Utc>,
74}
75
76#[derive(Debug, Clone, Serialize, Deserialize)]
77pub struct DirectMessage {
78    pub id: Uuid,
79    pub from_user_id: Uuid,
80    pub to_user_id: Uuid,
81    #[serde(default)]
82    pub from_username: String,
83    #[serde(default)]
84    pub to_username: String,
85    pub content: String,
86    #[serde(with = "datetime_format")]
87    pub created_at: DateTime<Utc>,
88    pub is_read: bool,
89}
90
91#[derive(Debug, Clone, Serialize, Deserialize)]
92pub struct UserProfile {
93    pub user_id: Uuid,
94    pub username: String,
95    pub bio: Option<String>,
96    pub karma: i32,
97    pub post_count: i32,
98    #[serde(with = "datetime_format")]
99    pub join_date: DateTime<Utc>,
100    pub recent_hashtags: Vec<String>,
101}
102
103#[derive(Debug, Clone, Serialize, Deserialize)]
104pub struct UserProfileView {
105    pub id: String,
106    pub username: String,
107    pub bio: Option<String>,
108    pub join_date: String,
109    pub follower_count: usize,
110    pub following_count: usize,
111    pub post_count: usize,
112    pub relationship: RelationshipStatus,
113}
114
115#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
116#[serde(tag = "type", rename_all = "snake_case")]
117pub enum RelationshipStatus {
118    #[serde(rename = "self")]
119    Self_,
120    MutualFriends,
121    Following,
122    FollowsYou,
123    None,
124}
125
126#[derive(Debug, Clone, Serialize, Deserialize)]
127pub struct UserConfig {
128    pub user_id: Uuid,
129    pub color_scheme: ColorScheme,
130    pub sort_order: SortOrder,
131    pub max_posts_display: i32,
132    pub emoji_enabled: bool,
133}
134
135impl Default for UserConfig {
136    fn default() -> Self {
137        Self {
138            user_id: Uuid::nil(),
139            color_scheme: ColorScheme::default(),
140            sort_order: SortOrder::default(),
141            max_posts_display: 25,
142            emoji_enabled: true,
143        }
144    }
145}
146
147// Request/Response types for API
148#[derive(Debug, Serialize, Deserialize)]
149pub struct CreatePostRequest {
150    pub content: String,
151}
152
153#[derive(Debug, Serialize, Deserialize)]
154pub struct CreateReplyRequest {
155    pub content: String,
156}
157
158#[derive(Debug, Serialize, Deserialize)]
159pub struct UpdatePostRequest {
160    pub content: String,
161}
162
163#[derive(Debug, Serialize, Deserialize)]
164pub struct VoteRequest {
165    pub direction: String,
166}
167
168#[derive(Debug, Serialize, Deserialize)]
169pub struct SendMessageRequest {
170    pub to_username: String,
171    pub content: String,
172}
173
174#[derive(Debug, Serialize, Deserialize)]
175pub struct UpdateBioRequest {
176    pub bio: String,
177}
178
179#[derive(Debug, Serialize, Deserialize)]
180pub struct UpdateConfigRequest {
181    pub color_scheme: Option<String>,
182    pub sort_order: Option<String>,
183    pub max_posts_display: Option<i32>,
184    pub emoji_enabled: Option<bool>,
185}
186
187#[derive(Debug, Serialize, Deserialize)]
188pub struct LoginRequest {
189    pub username: String,
190}
191
192#[derive(Debug, Serialize, Deserialize)]
193pub struct LoginResponse {
194    pub user: User,
195    pub session_token: String,
196}
197
198#[derive(Debug, Serialize, Deserialize)]
199pub struct ErrorResponse {
200    pub error: String,
201    pub details: Option<String>,
202}