use async_trait::async_trait;
use eyre::{Context, Result, bail};
use include_dir::Dir;
use std::path::{Path, PathBuf};
use std::process::Command;
mod rust;
mod typescript;
pub use rust::Rust;
pub use typescript::TypeScript;
#[async_trait]
pub trait Language {
fn name(&self) -> &'static str;
fn aliases(&self) -> &'static [&'static str] {
&[]
}
fn template(&self) -> &'static Dir<'static>;
fn is_dir_a_component(&self, dir: &Path) -> bool;
fn get_wit_file_path(&self, dir: &Path) -> PathBuf;
fn get_package_wasm_path(&self, dir: &Path) -> PathBuf;
fn get_package_wit_path(&self, dir: &Path) -> PathBuf;
fn get_component_wasm_path(&self, dir: &Path) -> Result<PathBuf>;
async fn build_component(&self, dir: &Path, api_endpoint: &str) -> Result<PathBuf>;
}
pub fn all() -> Vec<Box<dyn Language>> {
vec![Box::new(Rust), Box::new(TypeScript)]
}
pub fn detect(dir: &Path) -> Option<Box<dyn Language>> {
all().into_iter().find(|lang| lang.is_dir_a_component(dir))
}
pub fn from_name(name: &str) -> Option<Box<dyn Language>> {
all()
.into_iter()
.find(|lang| lang.name() == name || lang.aliases().contains(&name))
}
pub(super) fn run_command(dir: &Path, program: &str, args: &[&str]) -> Result<()> {
let status = Command::new(program)
.args(args)
.current_dir(dir)
.status()
.wrap_err_with(|| format!("failed to run {}", program))?;
if !status.success() {
bail!("{} {} failed", program, args.join(" "));
}
Ok(())
}
pub fn supported_names() -> String {
all()
.iter()
.map(|lang| lang.name())
.collect::<Vec<_>>()
.join(", ")
}