ricecoder_tui/
prompt_context.rs1use std::path::PathBuf;
13
14#[derive(Debug, Clone)]
27pub struct PromptContext {
28 pub text: String,
30 pub images: Vec<PathBuf>,
32 pub ready: bool,
34 pub created_at: std::time::SystemTime,
36}
37
38impl PromptContext {
39 pub fn new() -> Self {
41 Self {
42 text: String::new(),
43 images: Vec::new(),
44 ready: false,
45 created_at: std::time::SystemTime::now(),
46 }
47 }
48
49 pub fn with_text(text: impl Into<String>) -> Self {
51 Self {
52 text: text.into(),
53 images: Vec::new(),
54 ready: false,
55 created_at: std::time::SystemTime::now(),
56 }
57 }
58
59 pub fn with_text_and_images(text: impl Into<String>, images: Vec<PathBuf>) -> Self {
61 Self {
62 text: text.into(),
63 images,
64 ready: false,
65 created_at: std::time::SystemTime::now(),
66 }
67 }
68
69 pub fn set_text(&mut self, text: impl Into<String>) {
75 self.text = text.into();
76 }
77
78 pub fn get_text(&self) -> &str {
80 &self.text
81 }
82
83 pub fn add_image(&mut self, path: PathBuf) {
93 if !self.images.contains(&path) {
94 self.images.push(path);
95 }
96 }
97
98 pub fn add_images(&mut self, paths: Vec<PathBuf>) {
108 for path in paths {
109 self.add_image(path);
110 }
111 }
112
113 pub fn remove_image(&mut self, path: &PathBuf) -> bool {
123 if let Some(pos) = self.images.iter().position(|p| p == path) {
124 self.images.remove(pos);
125 true
126 } else {
127 false
128 }
129 }
130
131 pub fn clear_images(&mut self) {
133 self.images.clear();
134 }
135
136 pub fn get_images(&self) -> &[PathBuf] {
138 &self.images
139 }
140
141 pub fn image_count(&self) -> usize {
143 self.images.len()
144 }
145
146 pub fn has_images(&self) -> bool {
148 !self.images.is_empty()
149 }
150
151 pub fn has_text(&self) -> bool {
153 !self.text.is_empty()
154 }
155
156 pub fn is_complete(&self) -> bool {
158 self.has_text() || self.has_images()
159 }
160
161 pub fn mark_ready(&mut self) {
163 self.ready = true;
164 }
165
166 pub fn mark_not_ready(&mut self) {
168 self.ready = false;
169 }
170
171 pub fn is_ready(&self) -> bool {
173 self.ready && self.is_complete()
174 }
175
176 pub fn clear(&mut self) {
178 self.text.clear();
179 self.images.clear();
180 self.ready = false;
181 self.created_at = std::time::SystemTime::now();
182 }
183
184 pub fn summary(&self) -> String {
186 let mut parts = Vec::new();
187
188 if self.has_text() {
189 parts.push(format!("Text: {} chars", self.text.len()));
190 }
191
192 if self.has_images() {
193 parts.push(format!("Images: {}", self.image_count()));
194 }
195
196 if parts.is_empty() {
197 "Empty context".to_string()
198 } else {
199 parts.join(", ")
200 }
201 }
202}
203
204impl Default for PromptContext {
205 fn default() -> Self {
206 Self::new()
207 }
208}
209
210#[cfg(test)]
211mod tests {
212 use super::*;
213
214 #[test]
215 fn test_prompt_context_creation() {
216 let context = PromptContext::new();
217 assert_eq!(context.text, "");
218 assert_eq!(context.images.len(), 0);
219 assert!(!context.ready);
220 }
221
222 #[test]
223 fn test_prompt_context_with_text() {
224 let context = PromptContext::with_text("Hello, world!");
225 assert_eq!(context.text, "Hello, world!");
226 assert_eq!(context.images.len(), 0);
227 }
228
229 #[test]
230 fn test_prompt_context_with_text_and_images() {
231 let images = vec![
232 PathBuf::from("/path/to/image1.png"),
233 PathBuf::from("/path/to/image2.jpg"),
234 ];
235 let context = PromptContext::with_text_and_images("Hello", images.clone());
236
237 assert_eq!(context.text, "Hello");
238 assert_eq!(context.images.len(), 2);
239 assert_eq!(context.images, images);
240 }
241
242 #[test]
243 fn test_set_text() {
244 let mut context = PromptContext::new();
245 context.set_text("New text");
246 assert_eq!(context.get_text(), "New text");
247 }
248
249 #[test]
250 fn test_add_image() {
251 let mut context = PromptContext::new();
252 let path = PathBuf::from("/path/to/image.png");
253
254 context.add_image(path.clone());
255 assert_eq!(context.image_count(), 1);
256 assert!(context.has_images());
257 assert_eq!(context.get_images()[0], path);
258 }
259
260 #[test]
261 fn test_add_duplicate_image() {
262 let mut context = PromptContext::new();
263 let path = PathBuf::from("/path/to/image.png");
264
265 context.add_image(path.clone());
266 context.add_image(path.clone());
267
268 assert_eq!(context.image_count(), 1);
270 }
271
272 #[test]
273 fn test_add_multiple_images() {
274 let mut context = PromptContext::new();
275 let paths = vec![
276 PathBuf::from("/path/to/image1.png"),
277 PathBuf::from("/path/to/image2.jpg"),
278 ];
279
280 context.add_images(paths.clone());
281 assert_eq!(context.image_count(), 2);
282 }
283
284 #[test]
285 fn test_remove_image() {
286 let mut context = PromptContext::new();
287 let path = PathBuf::from("/path/to/image.png");
288
289 context.add_image(path.clone());
290 assert_eq!(context.image_count(), 1);
291
292 let removed = context.remove_image(&path);
293 assert!(removed);
294 assert_eq!(context.image_count(), 0);
295 }
296
297 #[test]
298 fn test_remove_image_not_found() {
299 let mut context = PromptContext::new();
300 let path = PathBuf::from("/path/to/image.png");
301
302 let removed = context.remove_image(&path);
303 assert!(!removed);
304 }
305
306 #[test]
307 fn test_clear_images() {
308 let mut context = PromptContext::new();
309 context.add_images(vec![
310 PathBuf::from("/path/to/image1.png"),
311 PathBuf::from("/path/to/image2.jpg"),
312 ]);
313
314 assert_eq!(context.image_count(), 2);
315 context.clear_images();
316 assert_eq!(context.image_count(), 0);
317 }
318
319 #[test]
320 fn test_has_text() {
321 let mut context = PromptContext::new();
322 assert!(!context.has_text());
323
324 context.set_text("Some text");
325 assert!(context.has_text());
326 }
327
328 #[test]
329 fn test_has_images() {
330 let mut context = PromptContext::new();
331 assert!(!context.has_images());
332
333 context.add_image(PathBuf::from("/path/to/image.png"));
334 assert!(context.has_images());
335 }
336
337 #[test]
338 fn test_is_complete() {
339 let mut context = PromptContext::new();
340 assert!(!context.is_complete());
341
342 context.set_text("Some text");
343 assert!(context.is_complete());
344
345 context.clear();
346 assert!(!context.is_complete());
347
348 context.add_image(PathBuf::from("/path/to/image.png"));
349 assert!(context.is_complete());
350 }
351
352 #[test]
353 fn test_ready_state() {
354 let mut context = PromptContext::new();
355 assert!(!context.is_ready());
356
357 context.mark_ready();
358 assert!(!context.is_ready()); context.set_text("Some text");
361 assert!(context.is_ready());
362
363 context.mark_not_ready();
364 assert!(!context.is_ready());
365 }
366
367 #[test]
368 fn test_clear() {
369 let mut context = PromptContext::new();
370 context.set_text("Some text");
371 context.add_image(PathBuf::from("/path/to/image.png"));
372 context.mark_ready();
373
374 assert!(context.has_text());
375 assert!(context.has_images());
376 assert!(context.ready);
377
378 context.clear();
379
380 assert!(!context.has_text());
381 assert!(!context.has_images());
382 assert!(!context.ready);
383 }
384
385 #[test]
386 fn test_summary() {
387 let mut context = PromptContext::new();
388 assert_eq!(context.summary(), "Empty context");
389
390 context.set_text("Hello, world!");
391 assert!(context.summary().contains("Text"));
392
393 context.add_image(PathBuf::from("/path/to/image.png"));
394 let summary = context.summary();
395 assert!(summary.contains("Text"));
396 assert!(summary.contains("Images"));
397 }
398
399 #[test]
400 fn test_image_count() {
401 let mut context = PromptContext::new();
402 assert_eq!(context.image_count(), 0);
403
404 context.add_images(vec![
405 PathBuf::from("/path/to/image1.png"),
406 PathBuf::from("/path/to/image2.jpg"),
407 PathBuf::from("/path/to/image3.gif"),
408 ]);
409
410 assert_eq!(context.image_count(), 3);
411 }
412
413 #[test]
414 fn test_get_images() {
415 let mut context = PromptContext::new();
416 let paths = vec![
417 PathBuf::from("/path/to/image1.png"),
418 PathBuf::from("/path/to/image2.jpg"),
419 ];
420
421 context.add_images(paths.clone());
422 assert_eq!(context.get_images(), paths.as_slice());
423 }
424}