use crate::engine::Database;
use crate::error::{DbxError, DbxResult};
use std::collections::HashMap;
use std::sync::Arc;
pub trait Callable: Send + Sync {
fn call(&self, ctx: &ExecutionContext, args: &[Value]) -> DbxResult<Value>;
fn name(&self) -> &str;
fn signature(&self) -> &Signature;
}
pub struct ExecutionContext {
pub dbx: Arc<Database>,
pub tx_id: Option<u64>,
pub metadata: HashMap<String, Value>,
}
impl ExecutionContext {
pub fn new(dbx: Arc<Database>) -> Self {
Self {
dbx,
tx_id: None,
metadata: HashMap::new(),
}
}
pub fn with_tx(mut self, tx_id: u64) -> Self {
self.tx_id = Some(tx_id);
self
}
}
#[derive(Debug, Clone)]
pub struct Signature {
pub params: Vec<DataType>,
pub return_type: DataType,
pub is_variadic: bool,
}
impl Signature {
pub fn validate_args(&self, args: &[Value]) -> DbxResult<()> {
if !self.is_variadic && args.len() != self.params.len() {
return Err(DbxError::InvalidArguments(format!(
"Expected {} arguments, got {}",
self.params.len(),
args.len()
)));
}
if self.is_variadic && args.len() < self.params.len() {
return Err(DbxError::InvalidArguments(format!(
"Expected at least {} arguments, got {}",
self.params.len(),
args.len()
)));
}
for (expected, actual) in self.params.iter().zip(args.iter()) {
if !actual.matches_type(expected) {
return Err(DbxError::TypeMismatch {
expected: format!("{:?}", expected),
actual: format!("{:?}", actual.data_type()),
});
}
}
Ok(())
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum DataType {
Null,
Boolean,
Int,
Float,
String,
Bytes,
Row,
RecordBatch,
Array,
Table,
}
#[derive(Debug, Clone)]
pub enum Value {
Null,
Boolean(bool),
Int(i64),
Float(f64),
String(String),
Bytes(Vec<u8>),
Array(Vec<Value>),
Table(Vec<Vec<Value>>),
}
impl Value {
pub fn data_type(&self) -> DataType {
match self {
Value::Null => DataType::Null,
Value::Boolean(_) => DataType::Boolean,
Value::Int(_) => DataType::Int,
Value::Float(_) => DataType::Float,
Value::String(_) => DataType::String,
Value::Bytes(_) => DataType::Bytes,
Value::Array(_) => DataType::Array,
Value::Table(_) => DataType::Table,
}
}
pub fn matches_type(&self, expected: &DataType) -> bool {
match (self, expected) {
(Value::Null, _) => true, _ => &self.data_type() == expected,
}
}
pub fn as_bool(&self) -> DbxResult<bool> {
match self {
Value::Boolean(b) => Ok(*b),
_ => Err(DbxError::TypeMismatch {
expected: "Boolean".to_string(),
actual: format!("{:?}", self.data_type()),
}),
}
}
pub fn as_i64(&self) -> DbxResult<i64> {
match self {
Value::Int(i) => Ok(*i),
_ => Err(DbxError::TypeMismatch {
expected: "Int".to_string(),
actual: format!("{:?}", self.data_type()),
}),
}
}
pub fn as_f64(&self) -> DbxResult<f64> {
match self {
Value::Float(f) => Ok(*f),
_ => Err(DbxError::TypeMismatch {
expected: "Float".to_string(),
actual: format!("{:?}", self.data_type()),
}),
}
}
pub fn as_str(&self) -> DbxResult<&str> {
match self {
Value::String(s) => Ok(s),
_ => Err(DbxError::TypeMismatch {
expected: "String".to_string(),
actual: format!("{:?}", self.data_type()),
}),
}
}
pub fn as_bytes(&self) -> DbxResult<&[u8]> {
match self {
Value::Bytes(b) => Ok(b),
_ => Err(DbxError::TypeMismatch {
expected: "Bytes".to_string(),
actual: format!("{:?}", self.data_type()),
}),
}
}
pub fn is_truthy(&self) -> bool {
match self {
Value::Null => false,
Value::Boolean(b) => *b,
Value::Int(i) => *i != 0,
Value::Float(f) => *f != 0.0,
Value::String(s) => !s.is_empty(),
Value::Bytes(b) => !b.is_empty(),
Value::Array(a) => !a.is_empty(),
Value::Table(t) => !t.is_empty(),
}
}
pub fn as_array(&self) -> DbxResult<&Vec<Value>> {
match self {
Value::Array(a) => Ok(a),
_ => Err(DbxError::TypeMismatch {
expected: "Array".to_string(),
actual: format!("{:?}", self.data_type()),
}),
}
}
pub fn as_table(&self) -> DbxResult<&Vec<Vec<Value>>> {
match self {
Value::Table(t) => Ok(t),
_ => Err(DbxError::TypeMismatch {
expected: "Table".to_string(),
actual: format!("{:?}", self.data_type()),
}),
}
}
}