1use std::{any::TypeId, fs::File, path::PathBuf};
31
32use base64::Engine;
33pub use bincode;
34
35use self::bincode::{
36 Decode, Encode,
37 config::{Configuration, Fixint, LittleEndian, NoLimit},
38 encode_into_std_write,
39};
40use crate::{duat_name, src_crate};
41
42pub(super) fn load_cache<C: Decode<()> + 'static>(path: impl Into<PathBuf>) -> Option<C> {
48 fn contents(path: PathBuf, type_id: TypeId, type_name: String) -> Option<File> {
49 if type_id == TypeId::of::<()>() {
50 return None;
51 }
52
53 let file_name = path.file_name()?.to_str()?;
54 let mut src = dirs_next::cache_dir()?;
55
56 let encoded: String = {
57 let base64 = base64::prelude::BASE64_URL_SAFE.encode(path.to_str().unwrap());
58 base64.chars().step_by(5).collect()
59 };
60
61 src.push("duat/structs");
62 src.push(format!("{encoded}:{file_name}"));
63 src.push(type_name);
64
65 std::fs::OpenOptions::new().read(true).open(src).ok()
66 }
67
68 let type_name = format!("{}::{}", src_crate::<C>(), duat_name::<C>());
69 let mut file = contents(path.into(), TypeId::of::<C>(), type_name)?;
70
71 let config = Configuration::<LittleEndian, Fixint, NoLimit>::default();
72 bincode::decode_from_std_read(&mut file, config).ok()
73}
74
75pub(super) fn store_cache<C: Encode + 'static>(path: impl Into<PathBuf>, cache: C) {
81 fn cache_file(path: PathBuf, type_id: TypeId, type_name: String) -> Option<File> {
82 if type_id == TypeId::of::<()>() {
83 return None;
84 }
85
86 let file_name = path.file_name().unwrap().to_str().unwrap();
87 let mut src = dirs_next::cache_dir()?;
88
89 let encoded: String = {
90 let base64 = base64::prelude::BASE64_URL_SAFE.encode(path.to_str().unwrap());
91 base64.chars().step_by(5).collect()
92 };
93
94 src.push("duat/structs");
95 src.push(format!("{encoded}:{file_name}"));
96
97 if !src.exists() {
98 std::fs::create_dir_all(src.clone()).unwrap();
99 }
100
101 src.push(type_name);
102
103 std::fs::OpenOptions::new()
104 .create(true)
105 .write(true)
106 .truncate(true)
107 .open(src)
108 .ok()
109 }
110
111 let type_name = format!("{}::{}", src_crate::<C>(), duat_name::<C>());
112 let Some(mut cache_file) = cache_file(path.into(), TypeId::of::<C>(), type_name) else {
113 return;
114 };
115
116 let config = Configuration::<LittleEndian, Fixint, NoLimit>::default();
117 encode_into_std_write(cache, &mut cache_file, config).unwrap();
118}
119
120pub(super) fn delete_cache(path: impl Into<PathBuf>) {
125 fn delete_cache_inner(path: PathBuf) {
126 let file_name = path.file_name().unwrap().to_str().unwrap();
127 let Some(mut src) = dirs_next::cache_dir() else {
128 return;
129 };
130
131 let encoded: String = {
132 let base64 = base64::prelude::BASE64_URL_SAFE.encode(path.to_str().unwrap());
133 base64.chars().step_by(5).collect()
134 };
135
136 src.push("duat/structs");
137 src.push(format!("{encoded}:{file_name}"));
138
139 if src.exists() {
140 std::fs::remove_dir_all(src).unwrap();
141 }
142 }
143
144 delete_cache_inner(path.into());
145}
146
147pub(super) fn delete_cache_for<C: 'static>(path: impl Into<PathBuf>) {
148 fn delete_cache_for_inner(path: PathBuf, type_name: String) {
149 let file_name = path.file_name().unwrap().to_str().unwrap();
150 let Some(mut src) = dirs_next::cache_dir() else {
151 return;
152 };
153
154 let encoded: String = {
155 let base64 = base64::prelude::BASE64_URL_SAFE.encode(path.to_str().unwrap());
156 base64.chars().step_by(5).collect()
157 };
158
159 src.push("duat/structs");
160 src.push(format!("{encoded}:{file_name}"));
161 src.push(type_name);
162
163 if src.exists() {
164 std::fs::remove_file(src).unwrap();
165 }
166 }
167
168 let type_name = format!("{}::{}", src_crate::<C>(), duat_name::<C>());
169 delete_cache_for_inner(path.into(), type_name);
170}