wasmer_cache/
filesystem.rs1#![cfg_attr(not(feature = "filesystem"), allow(unused))]
2use crate::cache::Cache;
3use crate::hash::Hash;
4use std::fs::{create_dir_all, File};
5use std::io::{self, Write};
6use std::path::PathBuf;
7use wasmer::{AsEngineRef, DeserializeError, Module, SerializeError};
8
9#[derive(Debug, Clone)]
35pub struct FileSystemCache {
36 path: PathBuf,
37 ext: Option<String>,
38}
39
40#[cfg(feature = "filesystem")]
41impl FileSystemCache {
42 pub fn new<P: Into<PathBuf>>(path: P) -> io::Result<Self> {
44 let path: PathBuf = path.into();
45 if path.exists() {
46 let metadata = path.metadata()?;
47 if metadata.is_dir() {
48 if !metadata.permissions().readonly() {
49 Ok(Self { path, ext: None })
50 } else {
51 Err(io::Error::new(
53 io::ErrorKind::PermissionDenied,
54 format!("the supplied path is readonly: {}", path.display()),
55 ))
56 }
57 } else {
58 Err(io::Error::new(
60 io::ErrorKind::PermissionDenied,
61 format!(
62 "the supplied path already points to a file: {}",
63 path.display()
64 ),
65 ))
66 }
67 } else {
68 let res = create_dir_all(&path);
70 if res.is_err() {
71 Err(io::Error::new(
72 io::ErrorKind::Other,
73 format!("failed to create cache directory: {}", path.display()),
74 ))
75 } else {
76 Ok(Self { path, ext: None })
77 }
78 }
79 }
80
81 pub fn set_cache_extension(&mut self, ext: Option<impl ToString>) {
86 self.ext = ext.map(|ext| ext.to_string());
87 }
88}
89
90#[cfg(feature = "filesystem")]
91impl Cache for FileSystemCache {
92 type DeserializeError = DeserializeError;
93 type SerializeError = SerializeError;
94
95 unsafe fn load(
96 &self,
97 engine: &impl AsEngineRef,
98 key: Hash,
99 ) -> Result<Module, Self::DeserializeError> {
100 let filename = if let Some(ref ext) = self.ext {
101 format!("{}.{}", key, ext)
102 } else {
103 key.to_string()
104 };
105 let path = self.path.join(filename);
106 let ret = Module::deserialize_from_file(engine, path.clone());
107 if ret.is_err() {
108 let _ = std::fs::remove_file(path);
111 }
112 ret
113 }
114
115 fn store(&mut self, key: Hash, module: &Module) -> Result<(), Self::SerializeError> {
116 let filename = if let Some(ref ext) = self.ext {
117 format!("{}.{}", key, ext)
118 } else {
119 key.to_string()
120 };
121 let path = self.path.join(filename);
122 let mut file = File::create(path)?;
123
124 let buffer = module.serialize()?;
125 file.write_all(&buffer)?;
126
127 Ok(())
128 }
129}
130
131#[cfg(test)]
132mod tests {
133 use super::*;
134
135 #[test]
136 fn test_fs_cache() {
137 let dir = tempfile::tempdir().unwrap();
138
139 let mut cache = FileSystemCache::new(dir.path()).unwrap();
140
141 let engine = wasmer::Engine::default();
142
143 let bytes = include_bytes!("../../wasix/tests/envvar.wasm");
144
145 let module = Module::from_binary(&engine, bytes).unwrap();
146 let key = Hash::generate(bytes);
147
148 cache.store(key, &module).unwrap();
149 let _restored = unsafe { cache.load(&engine, key).unwrap() };
150 }
151}