use crate::errors::{Error, Result};
use crate::{EpochDeadlines, WasmtimeEngineProvider, WasmtimeEngineProviderPre};
#[cfg(feature = "async")]
use crate::{WasmtimeEngineProviderAsync, WasmtimeEngineProviderAsyncPre};
#[allow(missing_debug_implementations)]
#[derive(Default)]
pub struct WasmtimeEngineProviderBuilder<'a> {
engine: Option<wasmtime::Engine>,
module: Option<wasmtime::Module>,
module_bytes: Option<&'a [u8]>,
#[cfg(feature = "cache")]
cache_enabled: bool,
#[cfg(feature = "cache")]
cache_path: Option<std::path::PathBuf>,
#[cfg(feature = "wasi")]
wasi_params: Option<wapc::WasiParams>,
epoch_deadlines: Option<EpochDeadlines>,
}
#[allow(deprecated)]
impl<'a> WasmtimeEngineProviderBuilder<'a> {
#[must_use]
pub fn new() -> Self {
Default::default()
}
#[must_use]
pub fn module_bytes(mut self, module_bytes: &'a [u8]) -> Self {
self.module_bytes = Some(module_bytes);
self
}
#[must_use]
pub fn module(mut self, module: wasmtime::Module) -> Self {
self.module = Some(module);
self
}
#[must_use]
pub fn engine(mut self, engine: wasmtime::Engine) -> Self {
self.engine = Some(engine);
self
}
#[cfg(feature = "wasi")]
#[cfg_attr(docsrs, doc(cfg(feature = "wasi")))]
#[must_use]
pub fn wasi_params(mut self, wasi: wapc::WasiParams) -> Self {
self.wasi_params = Some(wasi);
self
}
#[cfg(feature = "cache")]
#[cfg_attr(docsrs, doc(cfg(feature = "cache")))]
#[must_use]
pub fn enable_cache(mut self, path: Option<&std::path::Path>) -> Self {
self.cache_enabled = true;
self.cache_path = path.map(|p| p.to_path_buf());
self
}
#[must_use]
pub fn enable_epoch_interruptions(mut self, epoch_deadlines: EpochDeadlines) -> Self {
self.epoch_deadlines = Some(epoch_deadlines);
self
}
pub fn build_pre(&self) -> Result<WasmtimeEngineProviderPre> {
if self.module_bytes.is_some() && self.module.is_some() {
return Err(Error::BuilderInvalidConfig(
"`module_bytes` and `module` cannot be provided at the same time".to_owned(),
));
}
if self.module_bytes.is_none() && self.module.is_none() {
return Err(Error::BuilderInvalidConfig(
"Neither `module_bytes` nor `module` have been provided".to_owned(),
));
}
let pre = match &self.engine {
Some(e) => {
let module = self.module_bytes.as_ref().map_or_else(
|| Ok(self.module.as_ref().unwrap().clone()),
|module_bytes| wasmtime::Module::new(e, module_bytes),
)?;
cfg_if::cfg_if! {
if #[cfg(feature = "wasi")] {
WasmtimeEngineProviderPre::new(e.clone(), module, self.wasi_params.clone())
} else {
WasmtimeEngineProviderPre::new(e.clone(), module)
}
}
}
None => {
let mut config = wasmtime::Config::default();
if self.epoch_deadlines.is_some() {
config.epoch_interruption(true);
}
cfg_if::cfg_if! {
if #[cfg(feature = "cache")] {
if self.cache_enabled {
config.strategy(wasmtime::Strategy::Cranelift);
let cache = self.cache_path.as_ref().map_or_else(
|| wasmtime::CacheConfig::from_file(None).and_then(wasmtime::Cache::new),
|cache_path| {
let mut cache_config = wasmtime::CacheConfig::new();
cache_config.with_directory(cache_path);
wasmtime::Cache::new(cache_config)
}
).map_or_else(
|e| {
log::warn!("Wasmtime cache configuration not found ({e}). Repeated loads will speed up significantly with a cache configuration. See https://docs.wasmtime.dev/cli-cache.html for more information.");
None
},
Some,
);
config.cache(cache);
}
}
}
let engine = wasmtime::Engine::new(&config)?;
let module = self.module_bytes.as_ref().map_or_else(
|| Ok(self.module.as_ref().unwrap().clone()),
|module_bytes| wasmtime::Module::new(&engine, module_bytes),
)?;
cfg_if::cfg_if! {
if #[cfg(feature = "wasi")] {
WasmtimeEngineProviderPre::new(engine, module, self.wasi_params.clone())
} else {
WasmtimeEngineProviderPre::new(engine, module)
}
}
}
}?;
Ok(pre)
}
pub fn build(&self) -> Result<WasmtimeEngineProvider> {
let pre = self.build_pre()?;
pre.rehydrate(self.epoch_deadlines)
}
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
pub fn build_async_pre(&self) -> Result<WasmtimeEngineProviderAsyncPre> {
if self.module_bytes.is_some() && self.module.is_some() {
return Err(Error::BuilderInvalidConfig(
"`module_bytes` and `module` cannot be provided at the same time".to_owned(),
));
}
if self.module_bytes.is_none() && self.module.is_none() {
return Err(Error::BuilderInvalidConfig(
"Neither `module_bytes` nor `module` have been provided".to_owned(),
));
}
let pre = match &self.engine {
Some(e) => {
let module = self.module_bytes.as_ref().map_or_else(
|| Ok(self.module.as_ref().unwrap().clone()),
|module_bytes| wasmtime::Module::new(e, module_bytes),
)?;
cfg_if::cfg_if! {
if #[cfg(feature = "wasi")] {
WasmtimeEngineProviderAsyncPre::new(e.clone(), module, self.wasi_params.clone(), self.epoch_deadlines)
} else {
WasmtimeEngineProviderAsyncPre::new(e.clone(), module, self.epoch_deadlines)
}
}
}
None => {
let mut config = wasmtime::Config::default();
config.async_support(true);
if self.epoch_deadlines.is_some() {
config.epoch_interruption(true);
}
cfg_if::cfg_if! {
if #[cfg(feature = "cache")] {
if self.cache_enabled {
config.strategy(wasmtime::Strategy::Cranelift);
let cache = self.cache_path.as_ref().map_or_else(
|| wasmtime::CacheConfig::from_file(None).and_then(wasmtime::Cache::new),
|cache_path| {
let mut cache_config = wasmtime::CacheConfig::new();
cache_config.with_directory(cache_path);
wasmtime::Cache::new(cache_config)
}
).map_or_else(
|e| {
log::warn!("Wasmtime cache configuration not found ({e}). Repeated loads will speed up significantly with a cache configuration. See https://docs.wasmtime.dev/cli-cache.html for more information.");
None
},
Some,
);
config.cache(cache);
}
}
}
let engine = wasmtime::Engine::new(&config)?;
let module = self.module_bytes.as_ref().map_or_else(
|| Ok(self.module.as_ref().unwrap().clone()),
|module_bytes| wasmtime::Module::new(&engine, module_bytes),
)?;
cfg_if::cfg_if! {
if #[cfg(feature = "wasi")] {
WasmtimeEngineProviderAsyncPre::new(engine, module, self.wasi_params.clone(), self.epoch_deadlines)
} else {
WasmtimeEngineProviderAsyncPre::new(engine, module, self.epoch_deadlines)
}
}
}
}?;
Ok(pre)
}
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
pub fn build_async(&self) -> Result<WasmtimeEngineProviderAsync> {
let pre = self.build_async_pre()?;
pre.rehydrate()
}
}