Skip to main content

wasmer_cache_near/
filesystem.rs

1use crate::cache::Cache;
2use crate::hash::Hash;
3use std::fs::{create_dir_all, File};
4use std::io::{self, Write};
5use std::path::PathBuf;
6use wasmer::{DeserializeError, Module, SerializeError, Store};
7
8/// Representation of a directory that contains compiled wasm artifacts.
9///
10/// The `FileSystemCache` type implements the [`Cache`] trait, which allows it to be used
11/// generically when some sort of cache is required.
12///
13/// # Usage
14///
15/// ```
16/// use wasmer::{DeserializeError, SerializeError};
17/// use wasmer_cache::{Cache, FileSystemCache, Hash};
18///
19/// # use wasmer::{Module};
20/// fn store_module(module: &Module, bytes: &[u8]) -> Result<(), SerializeError> {
21///     // Create a new file system cache.
22///     let mut fs_cache = FileSystemCache::new("some/directory/goes/here")?;
23///
24///     // Compute a key for a given WebAssembly binary
25///     let key = Hash::generate(bytes);
26///
27///     // Store a module into the cache given a key
28///     fs_cache.store(key, module)?;
29///
30///     Ok(())
31/// }
32/// ```
33pub struct FileSystemCache {
34    path: PathBuf,
35    ext: Option<String>,
36}
37
38impl FileSystemCache {
39    /// Construct a new `FileSystemCache` around the specified directory.
40    pub fn new<P: Into<PathBuf>>(path: P) -> io::Result<Self> {
41        let path: PathBuf = path.into();
42        if path.exists() {
43            let metadata = path.metadata()?;
44            if metadata.is_dir() {
45                if !metadata.permissions().readonly() {
46                    Ok(Self { path, ext: None })
47                } else {
48                    // This directory is readonly.
49                    Err(io::Error::new(
50                        io::ErrorKind::PermissionDenied,
51                        format!("the supplied path is readonly: {}", path.display()),
52                    ))
53                }
54            } else {
55                // This path points to a file.
56                Err(io::Error::new(
57                    io::ErrorKind::PermissionDenied,
58                    format!(
59                        "the supplied path already points to a file: {}",
60                        path.display()
61                    ),
62                ))
63            }
64        } else {
65            // Create the directory and any parent directories if they don't yet exist.
66            create_dir_all(&path)?;
67            Ok(Self { path, ext: None })
68        }
69    }
70
71    /// Set the extension for this cached file.
72    ///
73    /// This is needed for loading native files from Windows, as otherwise
74    /// loading the library will fail (it requires a `.dll` extension)
75    pub fn set_cache_extension(&mut self, ext: Option<impl ToString>) {
76        self.ext = ext.map(|ext| ext.to_string());
77    }
78}
79
80impl Cache for FileSystemCache {
81    type DeserializeError = DeserializeError;
82    type SerializeError = SerializeError;
83
84    unsafe fn load(&self, store: &Store, key: Hash) -> Result<Module, Self::DeserializeError> {
85        let filename = if let Some(ref ext) = self.ext {
86            format!("{}.{}", key.to_string(), ext)
87        } else {
88            key.to_string()
89        };
90        let path = self.path.join(filename);
91        Module::deserialize_from_file(&store, path)
92    }
93
94    fn store(&mut self, key: Hash, module: &Module) -> Result<(), Self::SerializeError> {
95        let filename = if let Some(ref ext) = self.ext {
96            format!("{}.{}", key.to_string(), ext)
97        } else {
98            key.to_string()
99        };
100        let path = self.path.join(filename);
101        let mut file = File::create(path)?;
102
103        let buffer = module.serialize()?;
104        file.write_all(&buffer)?;
105
106        Ok(())
107    }
108}