use crate::cache::Cache; use crate::hash::Hash; use std::fs::{create_dir_all, File}; use std::io::{self, Write}; use std::path::PathBuf; use wasmer::{DeserializeError, Module, SerializeError, Store}; /// Representation of a directory that contains compiled wasm artifacts. /// /// The `FileSystemCache` type implements the [`Cache`] trait, which allows it to be used /// generically when some sort of cache is required. /// /// # Usage /// /// ``` /// use wasmer::{DeserializeError, SerializeError}; /// use wasmer_cache::{Cache, FileSystemCache, Hash}; /// /// # use wasmer::{Module}; /// fn store_module(module: &Module, bytes: &[u8]) -> Result<(), SerializeError> { /// // Create a new file system cache. /// let mut fs_cache = FileSystemCache::new("some/directory/goes/here")?; /// /// // Compute a key for a given WebAssembly binary /// let key = Hash::generate(bytes); /// /// // Store a module into the cache given a key /// fs_cache.store(key, module)?; /// /// Ok(()) /// } /// ``` pub struct FileSystemCache { path: PathBuf, ext: Option, } impl FileSystemCache { /// Construct a new `FileSystemCache` around the specified directory. pub fn new>(path: P) -> io::Result { let path: PathBuf = path.into(); if path.exists() { let metadata = path.metadata()?; if metadata.is_dir() { if !metadata.permissions().readonly() { Ok(Self { path, ext: None }) } else { // This directory is readonly. Err(io::Error::new( io::ErrorKind::PermissionDenied, format!("the supplied path is readonly: {}", path.display()), )) } } else { // This path points to a file. Err(io::Error::new( io::ErrorKind::PermissionDenied, format!("the supplied path already points to a file: {}", path.display()), )) } } else { // Create the directory and any parent directories if they don't yet exist. create_dir_all(&path)?; Ok(Self { path, ext: None }) } } /// Set the extension for this cached file. /// /// This is needed for loading native files from Windows, as otherwise /// loading the library will fail (it requires a `.dll` extension) pub fn set_cache_extension(&mut self, ext: Option) { self.ext = ext.map(|ext| ext.to_string()); } } impl Cache for FileSystemCache { type DeserializeError = DeserializeError; type SerializeError = SerializeError; unsafe fn load(&self, store: &Store, key: Hash) -> Result { let filename = if let Some(ref ext) = self.ext { format!("{}.{}", key.to_string(), ext) } else { key.to_string() }; let path = self.path.join(filename); Module::deserialize_from_file(&store, path) } fn store(&mut self, key: Hash, module: &Module) -> Result<(), Self::SerializeError> { let filename = if let Some(ref ext) = self.ext { format!("{}.{}", key.to_string(), ext) } else { key.to_string() }; let path = self.path.join(filename); let mut file = File::create(path)?; let buffer = module.serialize()?; file.write_all(&buffer)?; Ok(()) } }