duat_core/context/
cache.rs1use std::{
31 ffi::OsString,
32 fs::File,
33 hash::{DefaultHasher, Hash, Hasher},
34 path::{Path, PathBuf},
35};
36
37use bincode::config;
38pub use bincode::{self, Decode, Encode};
39
40use self::bincode::encode_into_std_write;
41use crate::{
42 text::{Text, txt},
43 utils::{duat_name, src_crate},
44};
45
46pub fn load<C: Decode<()> + Default + 'static>(path: impl AsRef<Path>) -> Result<C, Text> {
54 let mut cache_file = cache_file::<C>(path.as_ref(), false)?;
55
56 if cache_file.metadata()?.len() == 0 {
57 return Ok(C::default());
58 }
59
60 let config = config::standard();
61 bincode::decode_from_std_read(&mut cache_file, config).map_err(|err| txt!("{err}"))
62}
63
64pub fn store<C: Encode + 'static>(path: impl AsRef<Path>, cache: C) -> Result<usize, Text> {
72 let mut cache_file = cache_file::<C>(path.as_ref(), true)?;
73
74 let config = config::standard();
75 encode_into_std_write(cache, &mut cache_file, config).map_err(|err| txt!("{err}"))
76}
77
78pub fn delete(path: impl Into<PathBuf>) {
83 fn delete_cache_inner(path: PathBuf) {
84 let (Some(cache_dir), Some(file_name)) = (dirs_next::cache_dir(), path.file_name()) else {
85 return;
86 };
87
88 let mut hasher = DefaultHasher::new();
89 path.hash(&mut hasher);
90 let hash_value = hasher.finish();
91
92 let cached_file_name = {
93 let mut name = OsString::from(format!("{hash_value}-"));
94 name.push(file_name);
95 name
96 };
97
98 let src = cache_dir
99 .join("duat")
100 .join("structs")
101 .join(cached_file_name);
102 let _ = std::fs::remove_dir_all(src);
105 }
106
107 delete_cache_inner(path.into());
108}
109
110pub fn delete_for<C: 'static>(path: impl AsRef<Path>) {
112 let path = path.as_ref();
113 let (Some(cache_dir), Some(file_name)) = (dirs_next::cache_dir(), path.file_name()) else {
114 return;
115 };
116
117 let mut hasher = DefaultHasher::new();
118 path.hash(&mut hasher);
119 let hash_value = hasher.finish();
120
121 let cached_file_name = {
122 let mut name = OsString::from(format!("{hash_value}-"));
123 name.push(file_name);
124 name
125 };
126
127 let src = cache_dir
128 .join("duat")
129 .join("structs")
130 .join(cached_file_name)
131 .join(format!("{}-{}", src_crate::<C>(), duat_name::<C>()));
132
133 if let Ok(true) = src.try_exists() {
134 std::fs::remove_file(src).unwrap();
135 }
136}
137
138fn cache_file<C: 'static>(path: &Path, truncate: bool) -> std::io::Result<File> {
139 let mut hasher = DefaultHasher::new();
140 path.hash(&mut hasher);
141 let hash_value = hasher.finish();
142
143 let cached_file_name = {
144 let mut name = OsString::from(format!("{hash_value}-"));
145 name.push(path.file_name().ok_or_else(|| {
146 std::io::Error::new(
147 std::io::ErrorKind::IsADirectory,
148 format!("{path:?} is a directory, not a buffer for caching"),
149 )
150 })?);
151 name
152 };
153
154 let src_dir = dirs_next::cache_dir()
155 .ok_or_else(|| {
156 std::io::Error::new(std::io::ErrorKind::NotFound, "cache directory isn't set")
157 })?
158 .join("duat")
159 .join("structs")
160 .join(cached_file_name.clone());
161
162 if !src_dir.exists() {
163 std::fs::create_dir_all(src_dir.clone())?;
164 }
165
166 let src = src_dir.join(format!("{}-{}", src_crate::<C>(), duat_name::<C>()));
167
168 std::fs::OpenOptions::new()
169 .create(true)
170 .read(true)
171 .write(true)
172 .truncate(truncate)
173 .open(src)
174}