Skip to content

Commit 9d49d02

Browse files
refactor caching code
1 parent 0fa1e64 commit 9d49d02

File tree

2 files changed

+108
-78
lines changed

2 files changed

+108
-78
lines changed

src/core/api/series_information.rs

+4-24
Original file line numberDiff line numberDiff line change
@@ -57,33 +57,13 @@ pub struct Country {
5757
pub name: String,
5858
}
5959

60-
/// Structure carrying both series main information and it's string for caching
61-
pub struct SeriesInfoAndStr(SeriesMainInformation, String);
62-
63-
impl SeriesInfoAndStr {
64-
/// constructs a new SeriesInfoAndStr
65-
pub fn new(series_main_information: SeriesMainInformation, json_string: String) -> Self {
66-
Self(series_main_information, json_string)
67-
}
68-
69-
/// Gets the underlying information (series main info information and json string information)
70-
pub fn get_data(self) -> (SeriesMainInformation, String) {
71-
(self.0, self.1)
72-
}
73-
}
74-
75-
pub async fn get_series_main_info_with_url(url: String) -> Result<SeriesInfoAndStr, ApiError> {
76-
let prettified_json = get_pretty_json_from_url(url)
60+
pub async fn get_series_main_info_with_url(url: String) -> Result<String, ApiError> {
61+
get_pretty_json_from_url(url)
7762
.await
78-
.map_err(ApiError::Network)?;
79-
80-
Ok(SeriesInfoAndStr::new(
81-
deserialize_json(&prettified_json)?,
82-
prettified_json,
83-
))
63+
.map_err(ApiError::Network)
8464
}
8565

86-
pub async fn get_series_main_info_with_id(series_id: u32) -> Result<SeriesInfoAndStr, ApiError> {
66+
pub async fn get_series_main_info_with_id(series_id: u32) -> Result<String, ApiError> {
8767
get_series_main_info_with_url(format!("{}{}", SERIES_INFORMATION_ADDRESS, series_id)).await
8868
}
8969

src/core/caching.rs

+104-54
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use std::io::{self, ErrorKind};
12
use std::path;
23

34
use crate::core::api::{self, deserialize_json};
@@ -7,20 +8,27 @@ use directories::ProjectDirs;
78
use lazy_static::lazy_static;
89
use tokio::fs;
910
use tokio::io::AsyncWriteExt;
10-
use tracing::info;
11+
use tracing::{error, info};
1112

1213
const SERIES_CACHE_DIRECTORY: &str = "series-troxide-series-data";
1314
const IMAGES_CACHE_DIRECTORY: &str = "series-troxide-images-data";
15+
const EPISODE_LIST_FILENAME: &str = "episode-list";
16+
const SERIES_MAIN_INFORMATION_FILENAME: &str = "main-info";
1417

1518
lazy_static! {
1619
pub static ref CACHER: Cacher = Cacher::init();
1720
}
1821

19-
pub enum CacheType {
22+
pub enum CacheFolderType {
2023
Series,
2124
Images,
2225
}
2326

27+
pub enum CacheFilePath {
28+
SeriesMainInformation(u32),
29+
SeriesEpisodeList(u32),
30+
}
31+
2432
pub struct Cacher {
2533
cache_path: path::PathBuf,
2634
}
@@ -37,19 +45,43 @@ impl Cacher {
3745
}
3846
}
3947

40-
pub fn get_cache_path(&self, cache_type: CacheType) -> path::PathBuf {
48+
pub fn get_cache_folder_path(&self, cache_type: CacheFolderType) -> path::PathBuf {
4149
let mut cache_path = self.cache_path.clone();
4250
match cache_type {
43-
CacheType::Series => cache_path.push(SERIES_CACHE_DIRECTORY),
44-
CacheType::Images => cache_path.push(IMAGES_CACHE_DIRECTORY),
51+
CacheFolderType::Series => cache_path.push(SERIES_CACHE_DIRECTORY),
52+
CacheFolderType::Images => cache_path.push(IMAGES_CACHE_DIRECTORY),
4553
}
4654
cache_path
4755
}
56+
57+
/// This method is used to retrieve cache files for individual files in the series cache directory
58+
/// i.e episode-list, main-info
59+
pub fn get_cache_file_path(&self, cache_file_type: CacheFilePath) -> path::PathBuf {
60+
match cache_file_type {
61+
CacheFilePath::SeriesMainInformation(series_id) => {
62+
let mut cache_folder = self.get_series_cache_folder_path(series_id);
63+
cache_folder.push(SERIES_MAIN_INFORMATION_FILENAME);
64+
cache_folder
65+
}
66+
CacheFilePath::SeriesEpisodeList(series_id) => {
67+
let mut cache_folder = self.get_series_cache_folder_path(series_id);
68+
cache_folder.push(EPISODE_LIST_FILENAME);
69+
cache_folder
70+
}
71+
}
72+
}
73+
74+
/// This method is used to retrieve the series folder path that is a parent to individual cache files
75+
pub fn get_series_cache_folder_path(&self, series_id: u32) -> path::PathBuf {
76+
let mut cache_folder = self.get_cache_folder_path(CacheFolderType::Series);
77+
cache_folder.push(format!("{series_id}"));
78+
cache_folder
79+
}
4880
}
4981

5082
/// Loads the image from the provided url
5183
pub async fn load_image(image_url: String) -> Option<Vec<u8>> {
52-
let mut image_path = CACHER.get_cache_path(CacheType::Images);
84+
let mut image_path = CACHER.get_cache_folder_path(CacheFolderType::Images);
5385

5486
// Hashing the image url as a file name as the forward slashes in web urls
5587
// mimic paths
@@ -64,7 +96,7 @@ pub async fn load_image(image_url: String) -> Option<Vec<u8>> {
6496
match fs::read(&image_path).await {
6597
Ok(image_bytes) => return Some(image_bytes),
6698
Err(err) => {
67-
let images_directory = CACHER.get_cache_path(CacheType::Images);
99+
let images_directory = CACHER.get_cache_folder_path(CacheFolderType::Images);
68100
if !images_directory.exists() {
69101
info!("creating images cache directory as none exists");
70102
fs::DirBuilder::new()
@@ -93,10 +125,46 @@ pub async fn load_image(image_url: String) -> Option<Vec<u8>> {
93125
};
94126
}
95127

128+
pub async fn read_cache(cache_filepath: impl AsRef<path::Path>) -> io::Result<String> {
129+
fs::read_to_string(cache_filepath).await
130+
}
131+
132+
pub async fn write_cache(cache_data: &str, cache_filepath: &path::Path) {
133+
loop {
134+
if let Err(err) = fs::write(cache_filepath, cache_data).await {
135+
if err.kind() == ErrorKind::NotFound {
136+
let mut cache_folder = path::PathBuf::from(cache_filepath);
137+
cache_folder.pop();
138+
match fs::create_dir_all(&cache_folder).await {
139+
Err(err) => {
140+
error!(
141+
"failed to create cache directory '{}': {}",
142+
cache_folder.display(),
143+
err
144+
);
145+
break;
146+
}
147+
Ok(_) => continue,
148+
};
149+
} else {
150+
error!(
151+
"failed to write cache '{}': {}",
152+
cache_filepath.display(),
153+
err
154+
);
155+
break;
156+
}
157+
}
158+
break;
159+
}
160+
}
161+
96162
pub mod series_information {
97163
use super::api::series_information;
98164
use super::*;
99165

166+
use std::io::ErrorKind;
167+
100168
pub async fn get_series_main_info_with_url(
101169
url: String,
102170
) -> Result<SeriesMainInformation, ApiError> {
@@ -113,33 +181,22 @@ pub mod series_information {
113181
pub async fn get_series_main_info_with_id(
114182
series_id: u32,
115183
) -> Result<SeriesMainInformation, ApiError> {
116-
let name = format!("{}", series_id);
117-
let mut series_main_info_path = CACHER.get_cache_path(CacheType::Series);
118-
series_main_info_path.push(&name); // creating the series folder path
119-
let series_directory = series_main_info_path.clone(); // creating a copy before we make it path to file
120-
series_main_info_path.push(&name); // creating the series information json filename path
184+
let series_information_path =
185+
CACHER.get_cache_file_path(CacheFilePath::SeriesMainInformation(series_id));
121186

122-
let series_information_json = match fs::read_to_string(&series_main_info_path).await {
123-
Ok(info) => info,
187+
let series_information_json = match read_cache(&series_information_path).await {
188+
Ok(json_string) => json_string,
124189
Err(err) => {
125-
info!(
126-
"falling back online for 'series information' with id {}: {}",
127-
series_id, err
128-
);
129-
fs::DirBuilder::new()
130-
.recursive(true)
131-
.create(series_directory)
132-
.await
133-
.unwrap();
134-
let (series_information, json_string) =
135-
series_information::get_series_main_info_with_id(series_id)
136-
.await?
137-
.get_data();
138-
fs::write(series_main_info_path, json_string).await.unwrap();
139-
return Ok(series_information);
190+
info!("falling back online for 'series information' for series id: {series_id}");
191+
let json_string =
192+
series_information::get_series_main_info_with_id(series_id).await?;
193+
194+
if err.kind() == ErrorKind::NotFound {
195+
write_cache(&json_string, &series_information_path).await;
196+
}
197+
json_string
140198
}
141199
};
142-
143200
deserialize_json(&series_information_json)
144201
}
145202

@@ -167,14 +224,14 @@ pub mod show_images {
167224
show_images::{get_show_images as get_show_images_api, Image, ImageType},
168225
ApiError,
169226
},
170-
caching::{load_image, CacheType, CACHER},
227+
caching::{load_image, CacheFolderType, CACHER},
171228
};
172229
use tokio::fs;
173230
use tracing::info;
174231

175232
pub async fn get_show_images(series_id: u32) -> Result<Vec<Image>, ApiError> {
176233
let name = format!("{}", series_id);
177-
let mut series_images_list_path = CACHER.get_cache_path(CacheType::Series);
234+
let mut series_images_list_path = CACHER.get_cache_folder_path(CacheFolderType::Series);
178235
series_images_list_path.push(&name); // creating the series folder path
179236
let series_directory = series_images_list_path.clone(); // creating a copy before we make it path to file
180237
series_images_list_path.push("series-images-list"); // creating the episode list json filename path
@@ -228,52 +285,45 @@ pub mod show_images {
228285
}
229286

230287
pub mod episode_list {
231-
use std::collections::HashSet;
288+
use std::{collections::HashSet, io::ErrorKind};
232289

233290
use crate::core::{
234291
api::{
235292
deserialize_json,
236293
episodes_information::{get_episode_list, Episode},
237294
ApiError,
238295
},
239-
caching::{CacheType, CACHER},
296+
caching::CACHER,
240297
};
241298
use chrono::{DateTime, Datelike, Local, Timelike, Utc};
242-
use tokio::fs;
243299
use tracing::info;
244300

301+
use super::{read_cache, write_cache, CacheFilePath};
302+
245303
#[derive(Clone, Debug)]
246304
pub struct EpisodeList {
247305
episodes: Vec<Episode>,
248306
}
249307

250308
impl EpisodeList {
251309
pub async fn new(series_id: u32) -> Result<Self, ApiError> {
252-
let name = format!("{}", series_id);
253-
let mut episode_list_path = CACHER.get_cache_path(CacheType::Series);
254-
episode_list_path.push(&name); // creating the series folder path
255-
let series_directory = episode_list_path.clone(); // creating a copy before we make it path to file
256-
episode_list_path.push("episode-list"); // creating the episode list json filename path
257-
258-
let series_information_json = match fs::read_to_string(&episode_list_path).await {
259-
Ok(info) => info,
310+
let episodes_list_path =
311+
CACHER.get_cache_file_path(CacheFilePath::SeriesEpisodeList(series_id));
312+
313+
let json_string = match read_cache(&episodes_list_path).await {
314+
Ok(json_string) => json_string,
260315
Err(err) => {
261-
info!(
262-
"falling back online for 'episode list' for series id {}: {}",
263-
series_id, err
264-
);
265-
fs::DirBuilder::new()
266-
.recursive(true)
267-
.create(series_directory)
268-
.await
269-
.unwrap();
316+
info!("falling back online for 'episode_list' for series id: {series_id}");
270317
let (episodes, json_string) = get_episode_list(series_id).await?;
271-
fs::write(episode_list_path, json_string).await.unwrap();
318+
319+
if err.kind() == ErrorKind::NotFound {
320+
write_cache(&json_string, &episodes_list_path).await;
321+
}
272322
return Ok(Self { episodes });
273323
}
274324
};
275325

276-
let episodes = deserialize_json::<Vec<Episode>>(&series_information_json)?;
326+
let episodes = deserialize_json::<Vec<Episode>>(&json_string)?;
277327
Ok(Self { episodes })
278328
}
279329

0 commit comments

Comments
 (0)