xcom_rs/bookmarks/
commands.rs1use anyhow::Result;
2use serde::{Deserialize, Serialize};
3
4use crate::tweets::models::Tweet;
5
6#[derive(Debug, Clone)]
8pub struct BookmarkArgs {
9 pub tweet_id: String,
10}
11
12#[derive(Debug, Clone)]
14pub struct BookmarkListArgs {
15 pub limit: Option<usize>,
16 pub cursor: Option<String>,
17}
18
19#[derive(Debug, Clone, Serialize, Deserialize)]
21pub struct BookmarkResult {
22 pub tweet_id: String,
23 pub success: bool,
24}
25
26#[derive(Debug, Clone, Serialize, Deserialize)]
28pub struct BookmarkPaginationMeta {
29 #[serde(skip_serializing_if = "Option::is_none")]
30 pub next_token: Option<String>,
31}
32
33#[derive(Debug, Clone, Serialize, Deserialize)]
35pub struct BookmarkListMeta {
36 pub pagination: BookmarkPaginationMeta,
37}
38
39#[derive(Debug, Clone, Serialize, Deserialize)]
41pub struct BookmarkListResult {
42 pub tweets: Vec<Tweet>,
43 #[serde(skip_serializing_if = "Option::is_none")]
44 pub meta: Option<BookmarkListMeta>,
45}
46
47pub struct BookmarkCommand;
49
50impl BookmarkCommand {
51 pub fn new() -> Self {
53 Self
54 }
55
56 pub fn add(&self, args: BookmarkArgs) -> Result<BookmarkResult> {
59 Ok(BookmarkResult {
60 tweet_id: args.tweet_id,
61 success: true,
62 })
63 }
64
65 pub fn remove(&self, args: BookmarkArgs) -> Result<BookmarkResult> {
68 Ok(BookmarkResult {
69 tweet_id: args.tweet_id,
70 success: true,
71 })
72 }
73
74 pub fn list(&self, args: BookmarkListArgs) -> Result<BookmarkListResult> {
77 let limit = args.limit.unwrap_or(10);
78
79 let offset = if let Some(cursor) = &args.cursor {
81 cursor
83 .strip_prefix("bookmark_cursor_")
84 .and_then(|s| s.parse::<usize>().ok())
85 .unwrap_or(0)
86 } else {
87 0
88 };
89
90 let mut tweets = Vec::new();
92 for i in offset..(offset + limit) {
93 let mut tweet = Tweet::new(format!("bookmark_tweet_{}", i));
94 tweet.text = Some(format!("Bookmarked tweet text {}", i));
95 tweet.author_id = Some(format!("user_{}", i));
96 tweet.created_at = Some("2024-01-01T00:00:00Z".to_string());
97 tweets.push(tweet);
98 }
99
100 let next_token = if tweets.len() == limit {
102 Some(format!("bookmark_cursor_{}", offset + limit))
103 } else {
104 None
105 };
106
107 let meta = Some(BookmarkListMeta {
108 pagination: BookmarkPaginationMeta { next_token },
109 });
110
111 Ok(BookmarkListResult { tweets, meta })
112 }
113}
114
115impl Default for BookmarkCommand {
116 fn default() -> Self {
117 Self::new()
118 }
119}
120
121#[cfg(test)]
122mod tests {
123 use super::*;
124
125 #[test]
126 fn test_bookmark_add() {
127 let cmd = BookmarkCommand::new();
128 let args = BookmarkArgs {
129 tweet_id: "tweet123".to_string(),
130 };
131
132 let result = cmd.add(args).unwrap();
133 assert_eq!(result.tweet_id, "tweet123");
134 assert!(result.success);
135 }
136
137 #[test]
138 fn test_bookmark_remove() {
139 let cmd = BookmarkCommand::new();
140 let args = BookmarkArgs {
141 tweet_id: "tweet456".to_string(),
142 };
143
144 let result = cmd.remove(args).unwrap();
145 assert_eq!(result.tweet_id, "tweet456");
146 assert!(result.success);
147 }
148
149 #[test]
150 fn test_bookmark_list_default() {
151 let cmd = BookmarkCommand::new();
152 let args = BookmarkListArgs {
153 limit: None,
154 cursor: None,
155 };
156
157 let result = cmd.list(args).unwrap();
158 assert_eq!(result.tweets.len(), 10);
159 assert!(result.meta.is_some());
160 let meta = result.meta.unwrap();
161 assert!(meta.pagination.next_token.is_some());
162 assert_eq!(
163 meta.pagination.next_token,
164 Some("bookmark_cursor_10".to_string())
165 );
166 }
167
168 #[test]
169 fn test_bookmark_list_with_limit() {
170 let cmd = BookmarkCommand::new();
171 let args = BookmarkListArgs {
172 limit: Some(5),
173 cursor: None,
174 };
175
176 let result = cmd.list(args).unwrap();
177 assert_eq!(result.tweets.len(), 5);
178 assert!(result.meta.is_some());
179 let meta = result.meta.unwrap();
180 assert_eq!(
181 meta.pagination.next_token,
182 Some("bookmark_cursor_5".to_string())
183 );
184 }
185
186 #[test]
187 fn test_bookmark_list_with_cursor() {
188 let cmd = BookmarkCommand::new();
189 let args = BookmarkListArgs {
190 limit: Some(5),
191 cursor: Some("bookmark_cursor_5".to_string()),
192 };
193
194 let result = cmd.list(args).unwrap();
195 assert_eq!(result.tweets.len(), 5);
196 assert_eq!(result.tweets[0].id, "bookmark_tweet_5");
198 }
199}