locus_lib/
storage.rs

1use std::{
2    fs,
3    io::{Error, ErrorKind},
4    path::PathBuf,
5};
6
7use crate::json::Jsonable;
8
9/// Trait for types that can be stored.
10///
11/// Contains logic to create or get the path to the storage file and storage dir.
12pub trait StoragePath {
13    /// The name of the storage file.
14    fn storage_file_name() -> &'static str;
15
16    /// The name of the storage directory.
17    fn storage_dir_name() -> &'static str;
18
19    /// The base directory path.
20    fn base_dir_path() -> PathBuf {
21        dirs::home_dir().unwrap()
22    }
23
24    /// Gets or creates the storage directory.
25    ///
26    /// If its creation fails. an [`ErrorKind::Unsupported`] error is returned.
27    fn get_or_create_storage_path() -> Result<PathBuf, Error> {
28        let mut storage_dir_path = PathBuf::new();
29        storage_dir_path.push(Self::base_dir_path());
30        storage_dir_path.push(Self::storage_dir_name());
31        if storage_dir_path.exists() {
32            return Ok(storage_dir_path);
33        }
34        match fs::create_dir_all(&storage_dir_path) {
35            Ok(_) => Ok(storage_dir_path),
36            Err(_) => Err(Error::new(
37                ErrorKind::Unsupported,
38                "Could not create storage directory",
39            )),
40        }
41    }
42
43    /// Gets or creates the storage file path.
44    fn get_or_create_storage_file_path() -> Result<PathBuf, Error> {
45        let storage_dir_path = match Self::get_or_create_storage_path() {
46            Ok(path) => path,
47            Err(e) => return Err(e),
48        };
49        let storage_file_path = storage_dir_path.join(Self::storage_file_name());
50
51        Ok(storage_file_path)
52    }
53}
54
55/// Trait for types that can be stored in a file.
56///
57/// If implemented, the type can be loaded from and saved to a file.
58/// The file is located in the user's home directory.
59/// It requieres the type to implement the [`Jsonable`] and [`StoragePath`] traits.
60pub trait Storable: Jsonable + StoragePath {
61    /// Loads the type from the storage file.
62    fn load() -> Result<Self, Error> {
63        let storage_file_path = match Self::get_or_create_storage_file_path() {
64            Ok(path) => path,
65            Err(e) => return Err(e),
66        };
67
68        Self::from_json(&storage_file_path)
69    }
70
71    /// Saves the type to the storage file.
72    fn save(&self) -> Result<(), Error> {
73        let storage_file_path = match Self::get_or_create_storage_file_path() {
74            Ok(path) => path,
75            Err(e) => return Err(e),
76        };
77
78        match fs::write(&storage_file_path, self.to_json()) {
79            Ok(_) => Ok(()),
80            Err(err) => Err(Error::new(
81                ErrorKind::Other,
82                format!("Could not save storage. {err:?}, file_name: {storage_file_path:?}"),
83            )),
84        }
85    }
86}