#![deny(clippy::pedantic)]
use anyhow::Result;
use camino::Utf8PathBuf;
use clap::{ArgGroup, Parser};
use opa_wasm::Runtime;
use tracing::Instrument;
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, EnvFilter, Registry};
use wasmtime::{Engine, Module, Store};
#[derive(Parser)]
#[clap(group(
ArgGroup::new("policy")
.required(true)
))]
struct Cli {
#[arg(short, long, group = "policy")]
module: Option<Utf8PathBuf>,
#[arg(short, long, group = "policy")]
bundle: Option<Utf8PathBuf>,
#[arg(short, long)]
entrypoint: String,
#[arg(short, long = "data", group = "data", value_name = "JSON")]
data_value: Option<serde_json::Value>,
#[arg(short = 'D', long, group = "data", value_name = "PATH")]
data_path: Option<Utf8PathBuf>,
#[arg(short, long = "input", group = "input", value_name = "JSON")]
input_value: Option<serde_json::Value>,
#[arg(short = 'I', long, group = "input", value_name = "PATH")]
input_path: Option<Utf8PathBuf>,
}
#[tokio::main]
async fn main() -> Result<()> {
Registry::default()
.with(tracing_forest::ForestLayer::default())
.with(EnvFilter::from_default_env())
.init();
let (data, input, module, entrypoint) = (async move {
let cli = Cli::parse();
let data = if let Some(path) = cli.data_path {
let content = tokio::fs::read(path).await?;
serde_json::from_slice(&content)?
} else if let Some(data) = cli.data_value {
data
} else {
serde_json::Value::Object(serde_json::Map::default())
};
let input = if let Some(path) = cli.input_path {
let content = tokio::fs::read(path).await?;
serde_json::from_slice(&content)?
} else if let Some(input) = cli.input_value {
input
} else {
serde_json::Value::Object(serde_json::Map::default())
};
let module = if let Some(path) = cli.module {
tokio::fs::read(path)
.instrument(tracing::info_span!("read_module"))
.await?
} else if let Some(path) = cli.bundle {
opa_wasm::read_bundle(path).await?
} else {
unreachable!()
};
let entrypoint = cli.entrypoint;
Ok::<_, anyhow::Error>((data, input, module, entrypoint))
})
.instrument(tracing::info_span!("load_args"))
.await?;
let (mut store, module) = (async move {
let engine = Engine::default();
let module = Module::new(&engine, module)?;
let store = Store::new(&engine, ());
Ok::<_, anyhow::Error>((store, module))
})
.instrument(tracing::info_span!("compile_module"))
.await?;
let runtime = Runtime::new(&mut store, &module)
.instrument(tracing::info_span!("instanciate_module"))
.await?;
let policy = runtime
.with_data(&mut store, &data)
.instrument(tracing::info_span!("load_data"))
.await?;
let res: serde_json::Value = policy
.evaluate(&mut store, &entrypoint, &input)
.instrument(tracing::info_span!("evaluate"))
.await?;
println!("{res}");
Ok(())
}