A cli program to easily handle .gitignore files
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

103 lines
3.5 KiB

  1. //! Module to implement a simple cache
  2. use crate::errors::*;
  3. use core::time::Duration;
  4. use log::{debug, trace};
  5. use serde::Serialize;
  6. use std::fs::File as FsFile;
  7. use std::io::{BufReader, BufWriter};
  8. use std::path::PathBuf;
  9. use std::time::SystemTime;
  10. /// Trait for a simple cache
  11. ///
  12. /// Each struct implementing this trais has to offer functions to `get`, `set` an object and check
  13. /// if an value `exists`.
  14. pub trait Cache<T>
  15. where T: Serialize + serde::de::DeserializeOwned {
  16. /// Stores the `data` under the given `key`
  17. fn set(&self, key: &str, data: &T) -> Result<()>;
  18. /// Retrieves the data under the given `key`
  19. fn get(&self, key: &str) -> Result<T>;
  20. /// Checks whether the given key exists
  21. fn exists(&self, key: &str) -> bool;
  22. }
  23. /// A File cache
  24. #[derive(Debug)]
  25. pub struct File {
  26. /// The root path to the files
  27. root: PathBuf,
  28. /// Time to live for items
  29. ttl: Duration,
  30. }
  31. impl File {
  32. /// Create a new File cache
  33. ///
  34. /// The cached objects will be stored in the provided root folder.
  35. /// The folder will be created, if it does not yet exist.
  36. ///
  37. /// ## Errors
  38. /// The function returns an `Err` if the creation of the folders fails
  39. pub fn new(root: &PathBuf, ttl: Duration) -> Result<Self> {
  40. if !root.exists() {
  41. std::fs::create_dir_all(root)
  42. .chain_err(|| "Error while creating dirs for file cache")?;
  43. }
  44. Ok(Self { root: root.clone(), ttl })
  45. }
  46. /// Checks whether an element for the given key exists
  47. pub fn exists(&self, key: &str) -> bool {
  48. trace!("Check existence of {} in file cache", key);
  49. let mut path = self.root.clone();
  50. path.push(key);
  51. if !path.exists() {
  52. return false;
  53. }
  54. let modified: SystemTime = path
  55. .metadata()
  56. .map(|meta| meta.modified().unwrap_or(SystemTime::UNIX_EPOCH))
  57. .unwrap_or(SystemTime::UNIX_EPOCH);
  58. let age = modified.elapsed().unwrap_or(Duration::from_secs(u64::max_value()));
  59. debug!("Age of cache file: {} s", age.as_secs());
  60. if age > self.ttl {
  61. debug!("Cache file is too old (> {} seconds), won't be used", self.ttl.as_secs());
  62. return false;
  63. }
  64. true
  65. }
  66. /// Stores a `data` under the specified `key`
  67. pub fn set<T>(&self, key: &str, data: &T) -> Result<()>
  68. where T: Serialize {
  69. let mut path: PathBuf = self.root.clone();
  70. path.push(key);
  71. trace!("Serializing data to cache file {}", path.to_string_lossy());
  72. let cache =
  73. FsFile::create(path).chain_err(|| "Error while creating file cache to write")?;
  74. let writer = BufWriter::new(cache);
  75. serde_json::to_writer(writer, &data)
  76. .chain_err(|| "Error while serialzing templates to file cache")?;
  77. debug!("Serialization of data completed");
  78. Ok(())
  79. }
  80. /// Retrieves data for `key`
  81. pub fn get<T>(&self, key: &str) -> Result<T>
  82. where T: serde::de::DeserializeOwned {
  83. let mut path = self.root.clone();
  84. path.push(key);
  85. debug!("Retrieving {} from file cache", key);
  86. let f = FsFile::open(path)
  87. .chain_err(|| format!("Error while opening cache file for key {}", key))?;
  88. let reader = BufReader::new(f);
  89. let obj: T = serde_json::from_reader(reader)
  90. .chain_err(|| "Error while reading templates from file cache")?;
  91. debug!("Deserialized {} templates from file cache", "some");
  92. Ok(obj)
  93. }
  94. }