fs_cache.rs 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. #![allow(non_snake_case)]
  2. use std::{
  3. ops::{Deref, DerefMut},
  4. path::PathBuf,
  5. time::Duration,
  6. };
  7. /// Information about the freshness of a rendered response
  8. #[derive(Debug, Clone, Copy)]
  9. pub struct RenderFreshness {
  10. /// The age of the rendered response
  11. age: u64,
  12. /// The maximum age of the rendered response
  13. max_age: Option<u64>,
  14. }
  15. impl RenderFreshness {
  16. /// Create new freshness information
  17. pub fn new(age: u64, max_age: u64) -> Self {
  18. Self {
  19. age,
  20. max_age: Some(max_age),
  21. }
  22. }
  23. /// Create new freshness information with only the age
  24. pub fn new_age(age: u64) -> Self {
  25. Self { age, max_age: None }
  26. }
  27. /// Create new freshness information at the current time
  28. pub fn now(max_age: Option<Duration>) -> Self {
  29. Self {
  30. age: 0,
  31. max_age: max_age.map(|d| d.as_secs()),
  32. }
  33. }
  34. /// Get the age of the rendered response in seconds
  35. pub fn age(&self) -> u64 {
  36. self.age
  37. }
  38. /// Get the maximum age of the rendered response in seconds
  39. pub fn max_age(&self) -> Option<u64> {
  40. self.max_age
  41. }
  42. /// Write the freshness to the response headers.
  43. pub fn write(&self, headers: &mut http::HeaderMap<http::HeaderValue>) {
  44. let age = self.age();
  45. headers.insert(http::header::AGE, age.into());
  46. if let Some(max_age) = self.max_age() {
  47. headers.insert(
  48. http::header::CACHE_CONTROL,
  49. http::HeaderValue::from_str(&format!("max-age={}", max_age)).unwrap(),
  50. );
  51. }
  52. }
  53. }
  54. struct WriteBuffer {
  55. buffer: Vec<u8>,
  56. }
  57. impl std::fmt::Write for WriteBuffer {
  58. fn write_str(&mut self, s: &str) -> std::fmt::Result {
  59. self.buffer.extend_from_slice(s.as_bytes());
  60. Ok(())
  61. }
  62. }
  63. impl Deref for WriteBuffer {
  64. type Target = Vec<u8>;
  65. fn deref(&self) -> &Self::Target {
  66. &self.buffer
  67. }
  68. }
  69. impl DerefMut for WriteBuffer {
  70. fn deref_mut(&mut self) -> &mut Self::Target {
  71. &mut self.buffer
  72. }
  73. }
  74. pub(crate) struct ValidCachedPath {
  75. pub(crate) full_path: PathBuf,
  76. pub(crate) timestamp: std::time::SystemTime,
  77. }
  78. impl ValidCachedPath {
  79. pub fn try_from_path(value: PathBuf) -> Option<Self> {
  80. if value.extension() != Some(std::ffi::OsStr::new("html")) {
  81. return None;
  82. }
  83. let timestamp = decode_timestamp(value.file_stem()?.to_str()?)?;
  84. let full_path = value;
  85. Some(Self {
  86. full_path,
  87. timestamp,
  88. })
  89. }
  90. #[cfg(not(target_arch = "wasm32"))]
  91. pub fn freshness(&self, max_age: Option<std::time::Duration>) -> Option<RenderFreshness> {
  92. let age = self.timestamp.elapsed().ok()?.as_secs();
  93. let max_age = max_age.map(|max_age| max_age.as_secs());
  94. Some(RenderFreshness::new(age, max_age?))
  95. }
  96. }
  97. fn decode_timestamp(timestamp: &str) -> Option<std::time::SystemTime> {
  98. let timestamp = u64::from_str_radix(timestamp, 16).ok()?;
  99. Some(std::time::UNIX_EPOCH + std::time::Duration::from_secs(timestamp))
  100. }
  101. pub fn timestamp() -> String {
  102. let datetime = std::time::SystemTime::now();
  103. let timestamp = datetime
  104. .duration_since(std::time::UNIX_EPOCH)
  105. .unwrap()
  106. .as_secs();
  107. format!("{:x}", timestamp)
  108. }