use napi::{Result, bindgen_prelude::*};
use std::collections::HashMap;
use briefcase_core::{DriftCalculator, DriftMetrics, DriftStatus};
#[napi]
pub struct JsDriftCalculator {
inner: DriftCalculator,
}
#[napi]
impl JsDriftCalculator {
#[napi(constructor)]
pub fn new() -> Self {
Self {
inner: DriftCalculator::new(),
}
}
#[napi]
pub fn calculate_drift(&self, outputs: Vec<String>) -> JsDriftMetrics {
let metrics = self.inner.calculate_drift(&outputs);
JsDriftMetrics { inner: metrics }
}
#[napi]
pub fn with_similarity_threshold(&mut self, threshold: f64) -> Result<()> {
if threshold < 0.0 || threshold > 1.0 {
return Err(napi::Error::from_reason(
"Similarity threshold must be between 0.0 and 1.0"
));
}
self.inner = self.inner.clone().with_similarity_threshold(threshold);
Ok(())
}
#[napi]
pub fn with_consensus_threshold(&mut self, threshold: f64) -> Result<()> {
if threshold < 0.0 || threshold > 1.0 {
return Err(napi::Error::from_reason(
"Consensus threshold must be between 0.0 and 1.0"
));
}
self.inner = self.inner.clone().with_consensus_threshold(threshold);
Ok(())
}
#[napi(getter)]
pub fn similarity_threshold(&self) -> f64 {
self.inner.similarity_threshold
}
#[napi(getter)]
pub fn consensus_threshold(&self) -> f64 {
self.inner.consensus_threshold
}
}
#[napi]
pub struct JsDriftMetrics {
inner: DriftMetrics,
}
#[napi]
impl JsDriftMetrics {
#[napi(getter)]
pub fn consistency_score(&self) -> f64 {
self.inner.consistency_score
}
#[napi(getter)]
pub fn agreement_rate(&self) -> f64 {
self.inner.agreement_rate
}
#[napi(getter)]
pub fn drift_score(&self) -> f64 {
self.inner.drift_score
}
#[napi(getter)]
pub fn consensus_output(&self) -> Option<String> {
self.inner.consensus_output.clone()
}
#[napi(getter)]
pub fn outliers(&self) -> Vec<u32> {
self.inner.outliers.iter().map(|&i| i as u32).collect()
}
#[napi(getter)]
pub fn status(&self) -> String {
match self.inner.status {
DriftStatus::Stable => "stable".to_string(),
DriftStatus::Minor => "minor".to_string(),
DriftStatus::Significant => "significant".to_string(),
DriftStatus::Severe => "severe".to_string(),
}
}
#[napi(getter)]
pub fn total_samples(&self) -> u32 {
self.inner.total_samples as u32
}
#[napi(getter)]
pub fn unique_outputs(&self) -> u32 {
self.inner.unique_outputs as u32
}
#[napi]
pub fn to_object(&self) -> Result<serde_json::Value> {
Ok(serde_json::json!({
"consistency_score": self.inner.consistency_score,
"agreement_rate": self.inner.agreement_rate,
"drift_score": self.inner.drift_score,
"consensus_output": self.inner.consensus_output,
"outliers": self.inner.outliers,
"status": self.status(),
"total_samples": self.inner.total_samples,
"unique_outputs": self.inner.unique_outputs,
}))
}
}