bbc_news_cli/
image_cache.rs1use image::{DynamicImage, ImageReader, Rgba, RgbaImage};
2use std::collections::HashMap;
3use std::io::Cursor;
4use std::sync::{Arc, Mutex};
5
6pub struct ImageCache {
8 cache: HashMap<String, DynamicImage>,
9 max_size: usize,
10}
11
12impl ImageCache {
13 pub fn new() -> Self {
14 Self {
15 cache: HashMap::new(),
16 max_size: 50, }
18 }
19
20 pub fn get_or_load(&mut self, url: &str) -> DynamicImage {
22 if let Some(img) = self.cache.get(url) {
24 return img.clone();
25 }
26
27 match Self::download_image(url) {
29 Ok(img) => {
30 if self.cache.len() >= self.max_size {
32 self.cache.clear();
34 }
35 self.cache.insert(url.to_string(), img.clone());
36 img
37 }
38 Err(_) => {
39 Self::create_bbc_logo()
41 }
42 }
43 }
44
45 fn download_image(url: &str) -> anyhow::Result<DynamicImage> {
47 let client = reqwest::blocking::Client::builder()
49 .user_agent("Mozilla/5.0 (Linux; Android 10; SM-A307G) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36")
50 .timeout(std::time::Duration::from_secs(15))
51 .build()?;
52
53 let response = client.get(url).send()?;
54 let bytes = response.bytes()?;
55 let img = ImageReader::new(Cursor::new(bytes))
56 .with_guessed_format()?
57 .decode()?;
58 Ok(img)
59 }
60
61 pub fn create_bbc_logo() -> DynamicImage {
63 let width = 240;
64 let height = 135;
65
66 let mut img = RgbaImage::new(width, height);
67
68 let bbc_red = Rgba([234, 68, 57, 255]);
70
71 for pixel in img.pixels_mut() {
73 *pixel = bbc_red;
74 }
75
76 let white = Rgba([255, 255, 255, 255]);
79
80 let center_x = width / 2;
82 let center_y = height / 2;
83 let block_width = 15;
84 let block_height = 30;
85 let spacing = 5;
86
87 let x1 = center_x - block_width * 2 - spacing * 2;
89 draw_rect(&mut img, x1, center_y - block_height / 2, block_width, block_height, white);
90
91 let x2 = center_x - block_width - spacing;
93 draw_rect(&mut img, x2, center_y - block_height / 2, block_width, block_height, white);
94
95 let x3 = center_x + spacing;
97 draw_rect(&mut img, x3, center_y - block_height / 2, block_width, block_height, white);
98
99 DynamicImage::ImageRgba8(img)
100 }
101
102 pub fn clear(&mut self) {
104 self.cache.clear();
105 }
106}
107
108fn draw_rect(img: &mut RgbaImage, x: u32, y: u32, width: u32, height: u32, color: Rgba<u8>) {
110 for dy in 0..height {
111 for dx in 0..width {
112 let px = x + dx;
113 let py = y + dy;
114 if px < img.width() && py < img.height() {
115 img.put_pixel(px, py, color);
116 }
117 }
118 }
119}
120
121lazy_static::lazy_static! {
123 pub static ref GLOBAL_IMAGE_CACHE: Arc<Mutex<ImageCache>> = Arc::new(Mutex::new(ImageCache::new()));
124}
125
126pub fn get_image(url: Option<&str>) -> DynamicImage {
128 match url {
129 Some(url_str) => {
130 let mut cache = GLOBAL_IMAGE_CACHE.lock().unwrap();
131 cache.get_or_load(url_str)
132 }
133 None => {
134 ImageCache::create_bbc_logo()
135 }
136 }
137}