Skip to main content

dbx_core/automation/
callable.rs

1//! Callable trait and execution context
2//!
3//! 모든 실행 가능한 객체(UDF, 트리거, 스케줄 작업)의 공통 인터페이스
4
5use crate::engine::Database;
6use crate::error::{DbxError, DbxResult};
7use std::collections::HashMap;
8use std::sync::Arc;
9
10/// 실행 가능한 모든 객체의 공통 인터페이스
11pub trait Callable: Send + Sync {
12    /// 함수 실행
13    fn call(&self, ctx: &ExecutionContext, args: &[Value]) -> DbxResult<Value>;
14
15    /// 함수 이름
16    fn name(&self) -> &str;
17
18    /// 함수 시그니처
19    fn signature(&self) -> &Signature;
20}
21
22/// 실행 컨텍스트
23pub struct ExecutionContext {
24    /// DBX 인스턴스 참조
25    pub dbx: Arc<Database>,
26    /// 트랜잭션 ID (옵션)
27    pub tx_id: Option<u64>,
28    /// 메타데이터
29    pub metadata: HashMap<String, Value>,
30}
31
32impl ExecutionContext {
33    pub fn new(dbx: Arc<Database>) -> Self {
34        Self {
35            dbx,
36            tx_id: None,
37            metadata: HashMap::new(),
38        }
39    }
40
41    pub fn with_tx(mut self, tx_id: u64) -> Self {
42        self.tx_id = Some(tx_id);
43        self
44    }
45}
46
47/// 함수 시그니처
48#[derive(Debug, Clone)]
49pub struct Signature {
50    pub params: Vec<DataType>,
51    pub return_type: DataType,
52    pub is_variadic: bool,
53}
54
55impl Signature {
56    /// 인자 타입 검증
57    pub fn validate_args(&self, args: &[Value]) -> DbxResult<()> {
58        if !self.is_variadic && args.len() != self.params.len() {
59            return Err(DbxError::InvalidArguments(format!(
60                "Expected {} arguments, got {}",
61                self.params.len(),
62                args.len()
63            )));
64        }
65
66        if self.is_variadic && args.len() < self.params.len() {
67            return Err(DbxError::InvalidArguments(format!(
68                "Expected at least {} arguments, got {}",
69                self.params.len(),
70                args.len()
71            )));
72        }
73
74        // 타입 검증
75        for (expected, actual) in self.params.iter().zip(args.iter()) {
76            if !actual.matches_type(expected) {
77                return Err(DbxError::TypeMismatch {
78                    expected: format!("{:?}", expected),
79                    actual: format!("{:?}", actual.data_type()),
80                });
81            }
82        }
83
84        Ok(())
85    }
86}
87
88/// 데이터 타입
89#[derive(Debug, Clone, PartialEq)]
90pub enum DataType {
91    Null,
92    Boolean,
93    Int,
94    Float,
95    String,
96    Bytes,
97    Row,
98    RecordBatch,
99    Table,
100}
101
102/// 값
103#[derive(Debug, Clone)]
104pub enum Value {
105    Null,
106    Boolean(bool),
107    Int(i64),
108    Float(f64),
109    String(String),
110    Bytes(Vec<u8>),
111    /// 테이블 결과 (행 × 열)
112    Table(Vec<Vec<Value>>),
113}
114
115impl Value {
116    pub fn data_type(&self) -> DataType {
117        match self {
118            Value::Null => DataType::Null,
119            Value::Boolean(_) => DataType::Boolean,
120            Value::Int(_) => DataType::Int,
121            Value::Float(_) => DataType::Float,
122            Value::String(_) => DataType::String,
123            Value::Bytes(_) => DataType::Bytes,
124            Value::Table(_) => DataType::Table,
125        }
126    }
127
128    pub fn matches_type(&self, expected: &DataType) -> bool {
129        match (self, expected) {
130            (Value::Null, _) => true, // Null은 모든 타입과 호환
131            _ => &self.data_type() == expected,
132        }
133    }
134
135    // 타입 변환 헬퍼
136    pub fn as_bool(&self) -> DbxResult<bool> {
137        match self {
138            Value::Boolean(b) => Ok(*b),
139            _ => Err(DbxError::TypeMismatch {
140                expected: "Boolean".to_string(),
141                actual: format!("{:?}", self.data_type()),
142            }),
143        }
144    }
145
146    pub fn as_i64(&self) -> DbxResult<i64> {
147        match self {
148            Value::Int(i) => Ok(*i),
149            _ => Err(DbxError::TypeMismatch {
150                expected: "Int".to_string(),
151                actual: format!("{:?}", self.data_type()),
152            }),
153        }
154    }
155
156    pub fn as_f64(&self) -> DbxResult<f64> {
157        match self {
158            Value::Float(f) => Ok(*f),
159            _ => Err(DbxError::TypeMismatch {
160                expected: "Float".to_string(),
161                actual: format!("{:?}", self.data_type()),
162            }),
163        }
164    }
165
166    pub fn as_str(&self) -> DbxResult<&str> {
167        match self {
168            Value::String(s) => Ok(s),
169            _ => Err(DbxError::TypeMismatch {
170                expected: "String".to_string(),
171                actual: format!("{:?}", self.data_type()),
172            }),
173        }
174    }
175
176    pub fn as_bytes(&self) -> DbxResult<&[u8]> {
177        match self {
178            Value::Bytes(b) => Ok(b),
179            _ => Err(DbxError::TypeMismatch {
180                expected: "Bytes".to_string(),
181                actual: format!("{:?}", self.data_type()),
182            }),
183        }
184    }
185
186    /// truthy 판단 (트리거 조건 평가용)
187    pub fn is_truthy(&self) -> bool {
188        match self {
189            Value::Null => false,
190            Value::Boolean(b) => *b,
191            Value::Int(i) => *i != 0,
192            Value::Float(f) => *f != 0.0,
193            Value::String(s) => !s.is_empty(),
194            Value::Bytes(b) => !b.is_empty(),
195            Value::Table(t) => !t.is_empty(),
196        }
197    }
198
199    /// 테이블 데이터 반환
200    pub fn as_table(&self) -> DbxResult<&Vec<Vec<Value>>> {
201        match self {
202            Value::Table(t) => Ok(t),
203            _ => Err(DbxError::TypeMismatch {
204                expected: "Table".to_string(),
205                actual: format!("{:?}", self.data_type()),
206            }),
207        }
208    }
209}