use std::collections::{BTreeSet, HashMap};
use std::sync::LazyLock;
use crate::{
ast::{Name, RestrictedExpr, Value},
evaluator::{EvaluationError, RestrictedEvaluator},
extensions::{util, Extensions},
};
use miette::Diagnostic;
use smol_str::SmolStr;
use thiserror::Error;
use crate::validator::extension_schema::{ExtensionFunctionType, ExtensionSchema};
use self::extension_initialization_errors::FuncMultiplyDefinedError;
#[cfg(feature = "ipaddr")]
pub mod ipaddr;
#[cfg(feature = "decimal")]
pub mod decimal;
#[cfg(feature = "datetime")]
pub mod datetime;
pub mod partial_evaluation;
static ALL_AVAILABLE_EXTENSION_SCHEMA_OBJECTS: LazyLock<Vec<ExtensionSchema>> =
LazyLock::new(|| {
vec![
#[cfg(feature = "ipaddr")]
ipaddr::extension_schema(),
#[cfg(feature = "decimal")]
decimal::extension_schema(),
#[cfg(feature = "datetime")]
datetime::extension_schema(),
#[cfg(feature = "partial-eval")]
partial_evaluation::extension_schema(),
]
});
static ALL_AVAILABLE_EXTENSION_SCHEMAS: LazyLock<ExtensionSchemas<'static>> =
LazyLock::new(ExtensionSchemas::build_all_available);
#[derive(Debug)]
pub struct ExtensionSchemas<'a> {
function_types: HashMap<&'a Name, &'a ExtensionFunctionType>,
types_with_operator_overloading: BTreeSet<&'a Name>,
}
impl<'a> ExtensionSchemas<'a> {
fn build_all_available() -> ExtensionSchemas<'static> {
#[expect(
clippy::expect_used,
reason = "Builtin extension function definitions never conflict. Also tested by many different test cases."
)]
ExtensionSchemas::specific_extension_schemas(&ALL_AVAILABLE_EXTENSION_SCHEMA_OBJECTS)
.expect("Default extension schemas should never error on initialization")
}
pub fn all_available() -> &'static ExtensionSchemas<'static> {
&ALL_AVAILABLE_EXTENSION_SCHEMAS
}
pub fn specific_extension_schemas(
extension_schemas: &'a [ExtensionSchema],
) -> Result<ExtensionSchemas<'a>, ExtensionInitializationError> {
let function_types = util::collect_no_duplicates(
extension_schemas
.iter()
.flat_map(|ext| ext.function_types())
.map(|f| (f.name(), f)),
)
.map_err(|name| FuncMultiplyDefinedError { name: name.clone() })?;
let types_with_operator_overloading = extension_schemas
.iter()
.flat_map(|f| f.types_with_operator_overloading())
.collect();
Ok(Self {
function_types,
types_with_operator_overloading,
})
}
pub fn func_type(&self, name: &Name) -> Option<&ExtensionFunctionType> {
self.function_types.get(name).copied()
}
pub fn has_type_with_operator_overloading(&self, ext_ty_name: &Name) -> bool {
self.types_with_operator_overloading.contains(ext_ty_name)
}
pub fn types_with_operator_overloading(&self) -> impl Iterator<Item = &Name> + '_ {
self.types_with_operator_overloading.iter().copied()
}
}
fn eval_extension_constructor(
constructor_name: Name,
lit_str_arg: SmolStr,
) -> Result<Value, EvaluationError> {
let exts = Extensions::all_available();
let evaluator = RestrictedEvaluator::new(exts);
let constructor_call_expr =
RestrictedExpr::call_extension_fn(constructor_name, [RestrictedExpr::val(lit_str_arg)]);
evaluator.interpret(constructor_call_expr.as_borrowed())
}
#[derive(Diagnostic, Debug, Error)]
pub enum ExtensionInitializationError {
#[error(transparent)]
#[diagnostic(transparent)]
FuncMultiplyDefined(#[from] extension_initialization_errors::FuncMultiplyDefinedError),
}
mod extension_initialization_errors {
use crate::ast::Name;
use miette::Diagnostic;
use thiserror::Error;
#[derive(Diagnostic, Debug, Error)]
#[error("extension function `{name}` is defined multiple times")]
pub struct FuncMultiplyDefinedError {
pub(crate) name: Name,
}
}