use crate::WasmFileError;
use concepts::ContentDigest;
use concepts::{ComponentType, FunctionMetadata};
use std::path::Path;
use utils::wasm_tools::WasmComponent;
use wasmtime::Engine;
pub struct ComponentDetector {
pub component_type: ComponentType,
pub exports: Vec<FunctionMetadata>,
pub imports: Vec<FunctionMetadata>,
}
impl ComponentDetector {
#[must_use]
pub fn get_engine() -> Engine {
let mut wasmtime_config = wasmtime::Config::new();
wasmtime_config.wasm_component_model(true);
Engine::new(&wasmtime_config).unwrap()
}
pub fn new<P: AsRef<Path>>(wasm_path: P, engine: &Engine) -> Result<Self, WasmFileError> {
let wasm_path = wasm_path.as_ref();
let wasm_component = WasmComponent::new(wasm_path, engine)
.map_err(|err| WasmFileError::DecodeError(wasm_path.to_owned(), err))?;
let types = wasm_component
.exim
.exports
.iter()
.map(|pkg_ifc_fns| {
if pkg_ifc_fns.ifc_fqn.contains("workflow") {
ComponentType::WasmWorkflow
} else {
ComponentType::WasmActivity
}
})
.collect::<hashbrown::HashSet<_>>();
let component_type = match types.into_iter().collect::<Vec<_>>().as_slice() {
[item] => Ok(*item),
[] => Err(WasmFileError::NoExportedInterfaces), _ => Err(WasmFileError::MixedWorkflowsAndActivities), }?;
Ok(Self {
component_type,
exports: wasm_component.exported_functions().collect(),
imports: wasm_component.imported_functions().collect(),
})
}
}
#[cfg(not(madsim))]
pub async fn file_hash<P: AsRef<Path>>(path: P) -> Result<ContentDigest, std::io::Error> {
use sha2::Digest;
use tokio::io::AsyncReadExt;
let mut file = tokio::fs::File::open(path).await?;
let mut hasher = sha2::Sha256::default();
let mut buf = [0; 4096];
loop {
let n = file.read(&mut buf).await?;
if n == 0 {
break;
}
hasher.update(&buf[..n]);
}
Ok(ContentDigest::new(
concepts::HashType::Sha256,
format!("{:x}", hasher.finalize()),
))
}
#[cfg(madsim)]
#[allow(clippy::unused_async)]
pub async fn file_hash<P: AsRef<Path>>(_path: P) -> Result<ContentDigest, std::io::Error> {
Ok(ContentDigest::new(
concepts::HashType::Sha256,
ulid::Ulid::new().to_string(),
))
}
#[cfg(test)]
mod tests {
use crate::component_detector::ComponentDetector;
use crate::engines::{EngineConfig, Engines};
use concepts::ComponentType;
use test_utils::set_up;
#[rstest::rstest]
#[case(
test_programs_fibo_activity_builder::TEST_PROGRAMS_FIBO_ACTIVITY,
ComponentType::WasmActivity
)]
#[case(
test_programs_fibo_workflow_builder::TEST_PROGRAMS_FIBO_WORKFLOW,
ComponentType::WasmWorkflow
)]
#[tokio::test]
async fn detection(#[case] file: &'static str, #[case] expected: ComponentType) {
set_up();
let detected = ComponentDetector::new(
file,
&Engines::get_workflow_engine(EngineConfig::on_demand_testing()).unwrap(),
)
.unwrap();
assert_eq!(expected, detected.component_type);
}
}