ricecoder_images/
session_integration.rs1use crate::models::{ImageAnalysisResult, ImageMetadata};
10use crate::error::{ImageError, ImageResult};
11use serde::{Deserialize, Serialize};
12use std::collections::HashMap;
13
14#[derive(Debug, Clone, Serialize, Deserialize)]
16pub struct MessageImageMetadata {
17 pub hash: String,
19 pub metadata: ImageMetadata,
21 pub analysis: Option<ImageAnalysisResult>,
23 pub was_cached: bool,
25}
26
27impl MessageImageMetadata {
28 pub fn new(
30 hash: String,
31 metadata: ImageMetadata,
32 analysis: Option<ImageAnalysisResult>,
33 was_cached: bool,
34 ) -> Self {
35 Self {
36 hash,
37 metadata,
38 analysis,
39 was_cached,
40 }
41 }
42}
43
44#[derive(Debug, Clone, Default, Serialize, Deserialize)]
46pub struct MessageImages {
47 images: HashMap<String, MessageImageMetadata>,
49}
50
51impl MessageImages {
52 pub fn new() -> Self {
54 Self {
55 images: HashMap::new(),
56 }
57 }
58
59 pub fn add_image(&mut self, image_meta: MessageImageMetadata) -> ImageResult<()> {
69 if self.images.contains_key(&image_meta.hash) {
70 return Err(ImageError::InvalidFile(
71 format!("Image with hash {} already exists in message", image_meta.hash),
72 ));
73 }
74
75 self.images.insert(image_meta.hash.clone(), image_meta);
76 Ok(())
77 }
78
79 pub fn get_image(&self, hash: &str) -> Option<&MessageImageMetadata> {
81 self.images.get(hash)
82 }
83
84 pub fn get_all_images(&self) -> Vec<&MessageImageMetadata> {
86 self.images.values().collect()
87 }
88
89 pub fn image_count(&self) -> usize {
91 self.images.len()
92 }
93
94 pub fn has_images(&self) -> bool {
96 !self.images.is_empty()
97 }
98
99 pub fn remove_image(&mut self, hash: &str) -> Option<MessageImageMetadata> {
101 self.images.remove(hash)
102 }
103
104 pub fn get_image_hashes(&self) -> Vec<String> {
106 self.images.keys().cloned().collect()
107 }
108
109 pub fn to_vec(&self) -> Vec<MessageImageMetadata> {
111 self.images.values().cloned().collect()
112 }
113
114 pub fn clear(&mut self) {
116 self.images.clear();
117 }
118}
119
120#[derive(Debug, Clone, Default, Serialize, Deserialize)]
122pub struct SessionImageContext {
123 pub current_images: Vec<String>,
125 pub all_images: Vec<String>,
127 pub image_metadata: HashMap<String, ImageMetadata>,
129}
130
131impl SessionImageContext {
132 pub fn new() -> Self {
134 Self {
135 current_images: Vec::new(),
136 all_images: Vec::new(),
137 image_metadata: HashMap::new(),
138 }
139 }
140
141 pub fn add_image(&mut self, hash: String, metadata: ImageMetadata) {
148 if !self.current_images.contains(&hash) {
149 self.current_images.push(hash.clone());
150 }
151
152 if !self.all_images.contains(&hash) {
153 self.all_images.push(hash.clone());
154 }
155
156 self.image_metadata.insert(hash, metadata);
157 }
158
159 pub fn remove_image(&mut self, hash: &str) {
163 self.current_images.retain(|h| h != hash);
164 }
165
166 pub fn get_current_images(&self) -> Vec<&ImageMetadata> {
168 self.current_images
169 .iter()
170 .filter_map(|hash| self.image_metadata.get(hash))
171 .collect()
172 }
173
174 pub fn get_all_images(&self) -> Vec<&ImageMetadata> {
176 self.all_images
177 .iter()
178 .filter_map(|hash| self.image_metadata.get(hash))
179 .collect()
180 }
181
182 pub fn get_image_metadata(&self, hash: &str) -> Option<&ImageMetadata> {
184 self.image_metadata.get(hash)
185 }
186
187 pub fn has_image(&self, hash: &str) -> bool {
189 self.current_images.iter().any(|h| h == hash)
190 }
191
192 pub fn current_image_count(&self) -> usize {
194 self.current_images.len()
195 }
196
197 pub fn total_image_count(&self) -> usize {
199 self.all_images.len()
200 }
201
202 pub fn clear_current(&mut self) {
204 self.current_images.clear();
205 }
206
207 pub fn clear_all(&mut self) {
209 self.current_images.clear();
210 self.all_images.clear();
211 self.image_metadata.clear();
212 }
213}
214
215#[cfg(test)]
216mod tests {
217 use super::*;
218 use std::path::PathBuf;
219
220 fn create_test_image_metadata() -> ImageMetadata {
221 ImageMetadata::new(
222 PathBuf::from("/path/to/image.png"),
223 crate::formats::ImageFormat::Png,
224 1024 * 1024,
225 800,
226 600,
227 "test_hash_123".to_string(),
228 )
229 }
230
231 fn create_test_message_image_metadata() -> MessageImageMetadata {
232 MessageImageMetadata::new(
233 "test_hash_123".to_string(),
234 create_test_image_metadata(),
235 None,
236 false,
237 )
238 }
239
240 #[test]
241 fn test_message_images_add() {
242 let mut msg_images = MessageImages::new();
243 let image_meta = create_test_message_image_metadata();
244
245 assert!(msg_images.add_image(image_meta.clone()).is_ok());
246 assert_eq!(msg_images.image_count(), 1);
247 assert!(msg_images.has_images());
248 }
249
250 #[test]
251 fn test_message_images_duplicate() {
252 let mut msg_images = MessageImages::new();
253 let image_meta = create_test_message_image_metadata();
254
255 assert!(msg_images.add_image(image_meta.clone()).is_ok());
256 assert!(msg_images.add_image(image_meta).is_err());
257 assert_eq!(msg_images.image_count(), 1);
258 }
259
260 #[test]
261 fn test_message_images_get() {
262 let mut msg_images = MessageImages::new();
263 let image_meta = create_test_message_image_metadata();
264
265 msg_images.add_image(image_meta.clone()).unwrap();
266
267 let retrieved = msg_images.get_image("test_hash_123");
268 assert!(retrieved.is_some());
269 assert_eq!(retrieved.unwrap().hash, "test_hash_123");
270 }
271
272 #[test]
273 fn test_message_images_remove() {
274 let mut msg_images = MessageImages::new();
275 let image_meta = create_test_message_image_metadata();
276
277 msg_images.add_image(image_meta).unwrap();
278 assert_eq!(msg_images.image_count(), 1);
279
280 let removed = msg_images.remove_image("test_hash_123");
281 assert!(removed.is_some());
282 assert_eq!(msg_images.image_count(), 0);
283 }
284
285 #[test]
286 fn test_message_images_get_all() {
287 let mut msg_images = MessageImages::new();
288
289 for i in 0..3 {
290 let mut image_meta = create_test_message_image_metadata();
291 image_meta.hash = format!("hash_{}", i);
292 msg_images.add_image(image_meta).unwrap();
293 }
294
295 let all = msg_images.get_all_images();
296 assert_eq!(all.len(), 3);
297 }
298
299 #[test]
300 fn test_session_image_context_add() {
301 let mut ctx = SessionImageContext::new();
302 let metadata = create_test_image_metadata();
303
304 ctx.add_image("hash1".to_string(), metadata);
305
306 assert_eq!(ctx.current_image_count(), 1);
307 assert_eq!(ctx.total_image_count(), 1);
308 assert!(ctx.has_image("hash1"));
309 }
310
311 #[test]
312 fn test_session_image_context_remove() {
313 let mut ctx = SessionImageContext::new();
314 let metadata = create_test_image_metadata();
315
316 ctx.add_image("hash1".to_string(), metadata);
317 assert_eq!(ctx.current_image_count(), 1);
318
319 ctx.remove_image("hash1");
320 assert_eq!(ctx.current_image_count(), 0);
321 assert_eq!(ctx.total_image_count(), 1); }
323
324 #[test]
325 fn test_session_image_context_history() {
326 let mut ctx = SessionImageContext::new();
327 let metadata = create_test_image_metadata();
328
329 ctx.add_image("hash1".to_string(), metadata.clone());
330 ctx.add_image("hash2".to_string(), metadata.clone());
331
332 ctx.remove_image("hash1");
333
334 assert_eq!(ctx.current_image_count(), 1);
335 assert_eq!(ctx.total_image_count(), 2);
336 assert!(!ctx.has_image("hash1"));
337 assert!(ctx.has_image("hash2"));
338 }
339
340 #[test]
341 fn test_session_image_context_clear_current() {
342 let mut ctx = SessionImageContext::new();
343 let metadata = create_test_image_metadata();
344
345 ctx.add_image("hash1".to_string(), metadata);
346 ctx.clear_current();
347
348 assert_eq!(ctx.current_image_count(), 0);
349 assert_eq!(ctx.total_image_count(), 1);
350 }
351
352 #[test]
353 fn test_session_image_context_clear_all() {
354 let mut ctx = SessionImageContext::new();
355 let metadata = create_test_image_metadata();
356
357 ctx.add_image("hash1".to_string(), metadata);
358 ctx.clear_all();
359
360 assert_eq!(ctx.current_image_count(), 0);
361 assert_eq!(ctx.total_image_count(), 0);
362 }
363
364 #[test]
365 fn test_session_image_context_get_metadata() {
366 let mut ctx = SessionImageContext::new();
367 let metadata = create_test_image_metadata();
368
369 ctx.add_image("hash1".to_string(), metadata.clone());
370
371 let retrieved = ctx.get_image_metadata("hash1");
372 assert!(retrieved.is_some());
373 assert_eq!(retrieved.unwrap().width, 800);
374 }
375}