#![allow(dead_code)]
use std::sync::Arc;
use hamelin_lib::ast::err::ContextualTranslationErrors;
use hamelin_lib::catalog::{Catalog, CatalogProvider as LibCatalogProvider, DataSetBuilder};
use hamelin_lib::translation::{ContextualResult, QueryTranslation};
use hamelin_lib::FunctionDescription;
use serde::{Deserialize, Serialize};
use thiserror::Error;
use tsify_next::Tsify;
use wasm_bindgen::prelude::*;
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
#[wasm_bindgen]
struct CatalogProvider {
wrapped: Arc<LibCatalogProvider>,
}
#[wasm_bindgen]
impl CatalogProvider {
pub fn try_from_catalog(catalog: Catalog) -> Result<Self, JsValue> {
Ok(Self {
wrapped: Arc::new(
LibCatalogProvider::try_from(catalog)
.map_err(|e| JsValue::from_str(e.to_string().as_str()))?,
),
})
}
}
#[wasm_bindgen]
struct Compiler {
wrapped: hamelin_lib::Compiler,
}
#[wasm_bindgen]
impl Compiler {
#[wasm_bindgen(constructor)]
pub fn new() -> Self {
Self {
wrapped: hamelin_lib::Compiler::new(),
}
}
pub fn get_function_descriptions(&self) -> Vec<FunctionDescription> {
self.wrapped.get_function_descriptions()
}
pub fn set_catalog_provider(&mut self, provider: CatalogProvider) {
self.wrapped.set_environment_provider(provider.wrapped);
}
#[cfg(target_arch = "wasm32")]
pub fn set_time_range(&mut self, start: Option<js_sys::Date>, end: Option<js_sys::Date>) {
use chrono::{DateTime, Utc};
use hamelin_lib::TimeRange;
self.wrapped.set_time_range(
TimeRange {
start: start.map(|d| DateTime::<Utc>::from(d)),
end: end.map(|d| DateTime::<Utc>::from(d)),
}
.into(),
);
}
pub fn set_time_range_expression(
&mut self,
expression: String,
) -> Option<ContextualTranslationErrors> {
match self.wrapped.set_time_range_expression(expression) {
Ok(_) => None,
Err(e) => Some(e),
}
}
pub fn compile_query(&self, query: String) -> CompileQueryResult {
let res = self.wrapped.compile_query(query);
match res {
Ok(t) => CompileQueryResult::Ok(t),
Err(e) => CompileQueryResult::Err(e),
}
}
pub fn compile_query_at(&self, query: String, at: Option<usize>) -> ContextualResult {
self.wrapped.compile_query_at(query, at)
}
pub fn get_statement_datasets(&self, query: String) -> QueryDatasetsResult {
match self.wrapped.get_statement_datasets(query) {
Ok(datasets) => QueryDatasetsResult::Ok(datasets),
Err(e) => QueryDatasetsResult::Err(e),
}
}
}
#[derive(Serialize, Tsify)]
#[tsify(into_wasm_abi)]
pub enum CompileQueryResult {
Ok(QueryTranslation),
Err(ContextualTranslationErrors),
}
#[derive(Serialize, Tsify)]
#[tsify(into_wasm_abi)]
pub enum QueryDatasetsResult {
Ok(Vec<String>),
Err(ContextualTranslationErrors),
}
#[derive(Serialize, Deserialize, Tsify)]
#[serde(rename_all = "camelCase")]
#[tsify(into_wasm_abi, from_wasm_abi)]
pub struct CatalogResource {
pub name: String,
pub query: String,
}
#[derive(Debug, Error, Serialize, Tsify)]
#[tsify(into_wasm_abi)]
#[serde(tag = "kind", rename_all = "camelCase")]
pub enum BuildCatalogError {
#[error("failed to initialize catalog: {message}")]
CatalogInit { message: String },
#[error("query compilation failed")]
Compilation {
name: String,
errors: ContextualTranslationErrors,
},
#[error("failed to parse dataset: {message}")]
DatasetParse { name: String, message: String },
}
#[derive(Serialize, Tsify)]
#[tsify(into_wasm_abi, hashmap_as_object)]
pub struct BuildCatalogOutput {
pub catalog: Catalog,
pub errors: Vec<BuildCatalogError>,
}
#[wasm_bindgen]
pub fn build_catalog(
starting_catalog: Catalog,
resources: Vec<CatalogResource>,
) -> BuildCatalogOutput {
let catalog_provider = match LibCatalogProvider::try_from(starting_catalog) {
Ok(provider) => provider,
Err(e) => {
return BuildCatalogOutput {
catalog: Catalog::default(),
errors: vec![BuildCatalogError::CatalogInit {
message: e.to_string(),
}],
};
}
};
let provider_arc: Arc<LibCatalogProvider> = Arc::new(catalog_provider);
let mut compiler = hamelin_lib::Compiler::new();
compiler.set_environment_provider(provider_arc.clone());
let mut errors = Vec::new();
for resource in resources {
match compiler.compile_query(resource.query.clone()) {
Ok(query_translation) => {
let mut builder = DataSetBuilder::new(&resource.name);
for column in query_translation.translation.columns {
builder.columns.push(column);
}
match builder.parse() {
Ok((name_identifier, column_map)) => {
provider_arc.set(name_identifier, column_map);
}
Err(e) => {
errors.push(BuildCatalogError::DatasetParse {
name: resource.name,
message: e.to_string(),
});
}
}
}
Err(e) => {
errors.push(BuildCatalogError::Compilation {
name: resource.name,
errors: e,
});
}
}
}
let final_catalog = provider_arc.get_catalog();
BuildCatalogOutput {
catalog: final_catalog,
errors,
}
}