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    Array,
100    Table,
101}
102
103/// 값
104#[derive(Debug, Clone)]
105pub enum Value {
106    Null,
107    Boolean(bool),
108    Int(i64),
109    Float(f64),
110    String(String),
111    Bytes(Vec<u8>),
112    /// 1차원 배열/콜럼
113    Array(Vec<Value>),
114    /// 테이블 결과 (행 × 열)
115    Table(Vec<Vec<Value>>),
116}
117
118impl Value {
119    pub fn data_type(&self) -> DataType {
120        match self {
121            Value::Null => DataType::Null,
122            Value::Boolean(_) => DataType::Boolean,
123            Value::Int(_) => DataType::Int,
124            Value::Float(_) => DataType::Float,
125            Value::String(_) => DataType::String,
126            Value::Bytes(_) => DataType::Bytes,
127            Value::Array(_) => DataType::Array,
128            Value::Table(_) => DataType::Table,
129        }
130    }
131
132    pub fn matches_type(&self, expected: &DataType) -> bool {
133        match (self, expected) {
134            (Value::Null, _) => true, // Null은 모든 타입과 호환
135            _ => &self.data_type() == expected,
136        }
137    }
138
139    // 타입 변환 헬퍼
140    pub fn as_bool(&self) -> DbxResult<bool> {
141        match self {
142            Value::Boolean(b) => Ok(*b),
143            _ => Err(DbxError::TypeMismatch {
144                expected: "Boolean".to_string(),
145                actual: format!("{:?}", self.data_type()),
146            }),
147        }
148    }
149
150    pub fn as_i64(&self) -> DbxResult<i64> {
151        match self {
152            Value::Int(i) => Ok(*i),
153            _ => Err(DbxError::TypeMismatch {
154                expected: "Int".to_string(),
155                actual: format!("{:?}", self.data_type()),
156            }),
157        }
158    }
159
160    pub fn as_f64(&self) -> DbxResult<f64> {
161        match self {
162            Value::Float(f) => Ok(*f),
163            _ => Err(DbxError::TypeMismatch {
164                expected: "Float".to_string(),
165                actual: format!("{:?}", self.data_type()),
166            }),
167        }
168    }
169
170    pub fn as_str(&self) -> DbxResult<&str> {
171        match self {
172            Value::String(s) => Ok(s),
173            _ => Err(DbxError::TypeMismatch {
174                expected: "String".to_string(),
175                actual: format!("{:?}", self.data_type()),
176            }),
177        }
178    }
179
180    pub fn as_bytes(&self) -> DbxResult<&[u8]> {
181        match self {
182            Value::Bytes(b) => Ok(b),
183            _ => Err(DbxError::TypeMismatch {
184                expected: "Bytes".to_string(),
185                actual: format!("{:?}", self.data_type()),
186            }),
187        }
188    }
189
190    /// truthy 판단 (트리거 조건 평가용)
191    pub fn is_truthy(&self) -> bool {
192        match self {
193            Value::Null => false,
194            Value::Boolean(b) => *b,
195            Value::Int(i) => *i != 0,
196            Value::Float(f) => *f != 0.0,
197            Value::String(s) => !s.is_empty(),
198            Value::Bytes(b) => !b.is_empty(),
199            Value::Array(a) => !a.is_empty(),
200            Value::Table(t) => !t.is_empty(),
201        }
202    }
203
204    /// 콜럼 데이터 반환
205    pub fn as_array(&self) -> DbxResult<&Vec<Value>> {
206        match self {
207            Value::Array(a) => Ok(a),
208            _ => Err(DbxError::TypeMismatch {
209                expected: "Array".to_string(),
210                actual: format!("{:?}", self.data_type()),
211            }),
212        }
213    }
214
215    /// 테이블 데이터 반환
216    pub fn as_table(&self) -> DbxResult<&Vec<Vec<Value>>> {
217        match self {
218            Value::Table(t) => Ok(t),
219            _ => Err(DbxError::TypeMismatch {
220                expected: "Table".to_string(),
221                actual: format!("{:?}", self.data_type()),
222            }),
223        }
224    }
225}