extism/
lib.rs

1// Makes proc-macros able to resolve `::extism` correctly
2extern crate self as extism;
3
4macro_rules! catch_out_of_fuel {
5    ($store: expr, $x:expr) => {{
6        let y = $x;
7        if y.is_err() && $store.get_fuel().is_ok_and(|x| x == 0) {
8            Err(Error::msg("plugin ran out of fuel"))
9        } else {
10            y
11        }
12    }};
13}
14
15pub(crate) use extism_convert::*;
16pub(crate) use std::collections::BTreeMap;
17use std::str::FromStr;
18pub(crate) use wasmtime::*;
19
20#[doc(inline)]
21pub use extism_convert as convert;
22
23pub use anyhow::Error;
24
25mod current_plugin;
26mod function;
27mod internal;
28pub(crate) mod manifest;
29pub(crate) mod pdk;
30mod plugin;
31mod plugin_builder;
32mod pool;
33mod readonly_dir;
34mod timer;
35
36/// Extism C API
37pub mod sdk;
38
39pub use current_plugin::CurrentPlugin;
40pub use extism_convert::{FromBytes, FromBytesOwned, ToBytes};
41pub use extism_manifest::{Manifest, Wasm, WasmMetadata};
42pub use function::{Function, UserData, Val, ValType, PTR};
43pub use plugin::{
44    CancelHandle, CompiledPlugin, Plugin, WasmInput, EXTISM_ENV_MODULE, EXTISM_USER_MODULE,
45};
46pub use plugin_builder::{DebugOptions, PluginBuilder};
47pub use pool::{Pool, PoolBuilder, PoolPlugin};
48
49pub(crate) use internal::{Internal, Wasi};
50pub(crate) use timer::{Timer, TimerAction};
51pub(crate) use tracing::{debug, error, trace, warn};
52
53#[cfg(test)]
54mod tests;
55
56pub(crate) const VERSION: &str = concat!(env!("CARGO_PKG_VERSION"), "\0");
57
58/// Returns a string containing the Extism version of the current runtime, this is the same as the Cargo package
59/// version
60pub fn extism_version() -> &'static str {
61    VERSION
62}
63
64#[derive(Clone)]
65struct LogFunction<F: Clone + Fn(&str)> {
66    func: F,
67}
68
69unsafe impl<F: Clone + Fn(&str)> Send for LogFunction<F> {}
70unsafe impl<F: Clone + Fn(&str)> Sync for LogFunction<F> {}
71
72impl<F: Clone + Fn(&str)> std::io::Write for LogFunction<F> {
73    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
74        if let Ok(s) = std::str::from_utf8(buf) {
75            (self.func)(s)
76        }
77
78        Ok(buf.len())
79    }
80
81    fn flush(&mut self) -> std::io::Result<()> {
82        Ok(())
83    }
84}
85
86/// Sets a custom callback to handle logs, each line will be passed to the provided callback instead of being
87/// logged to a file. This initializes a default `tracing_subscriber` and should only be called once.
88///
89/// `filter` may contain a general level like `trace` or `error`, but can also be more specific to enable logging only
90/// from specific crates. For example, to enable trace-level logging only for the extism crate use: `extism=trace`.
91pub fn set_log_callback<F: 'static + Clone + Fn(&str)>(
92    func: F,
93    filter: impl AsRef<str>,
94) -> Result<(), Error> {
95    let filter = filter.as_ref();
96    let is_level = tracing::Level::from_str(filter).is_ok();
97    let cfg = tracing_subscriber::FmtSubscriber::builder().with_env_filter({
98        let x = tracing_subscriber::EnvFilter::builder()
99            .with_default_directive(tracing::Level::ERROR.into());
100        if is_level {
101            x.parse_lossy(format!("extism={filter}"))
102        } else {
103            x.parse_lossy(filter)
104        }
105    });
106    let w = LogFunction { func };
107    cfg.with_ansi(false)
108        .with_writer(move || w.clone())
109        .try_init()
110        .map_err(|x| Error::msg(x.to_string()))?;
111    Ok(())
112}