linkrs_query/executor/expression/functions/
table.rs1use crate::executor::expression::{ExpressionError, ExpressionErrorType};
6use linkrs_core::Value;
7
8pub trait TableFunction: Send + Sync + std::fmt::Debug {
13 fn name(&self) -> &str;
15
16 fn execute(&self, args: &[Value]) -> Result<Vec<Vec<Value>>, ExpressionError>;
19
20 fn description(&self) -> &str;
22}
23
24#[derive(Debug, Clone)]
26pub enum BuiltinTableFunction {
27 ReadCsv,
29}
30
31impl TableFunction for BuiltinTableFunction {
32 fn name(&self) -> &str {
33 match self {
34 BuiltinTableFunction::ReadCsv => "read_csv",
35 }
36 }
37
38 fn execute(&self, args: &[Value]) -> Result<Vec<Vec<Value>>, ExpressionError> {
39 match self {
40 BuiltinTableFunction::ReadCsv => execute_read_csv(args),
41 }
42 }
43
44 fn description(&self) -> &str {
45 match self {
46 BuiltinTableFunction::ReadCsv => "Read a CSV file and return its rows",
47 }
48 }
49}
50
51fn execute_read_csv(args: &[Value]) -> Result<Vec<Vec<Value>>, ExpressionError> {
52 if args.is_empty() {
53 return Err(ExpressionError::new(
54 ExpressionErrorType::InvalidArgumentCount,
55 "read_csv expects at least 1 argument (path)".to_string(),
56 ));
57 }
58
59 let path = match &args[0] {
60 Value::String(s) => s.clone(),
61 other => {
62 return Err(ExpressionError::new(
63 ExpressionErrorType::TypeError,
64 format!("read_csv path must be a string, got {:?}", other),
65 ))
66 }
67 };
68
69 let delimiter = if args.len() > 1 {
70 match &args[1] {
71 Value::String(s) => s.as_bytes().first().copied().unwrap_or(b','),
72 _ => b',',
73 }
74 } else {
75 b','
76 };
77
78 let file = std::fs::File::open(&path).map_err(|e| {
79 ExpressionError::new(
80 ExpressionErrorType::FunctionExecutionError,
81 format!("read_csv failed to open '{path}': {e}"),
82 )
83 })?;
84
85 let mut reader = csv::ReaderBuilder::new()
86 .has_headers(true)
87 .delimiter(delimiter)
88 .trim(csv::Trim::All)
89 .flexible(true)
90 .from_reader(std::io::BufReader::new(file));
91
92 let mut rows: Vec<Vec<Value>> = Vec::new();
93 for result in reader.records() {
94 let record = result.map_err(|e| {
95 ExpressionError::new(
96 ExpressionErrorType::FunctionExecutionError,
97 format!("read_csv row error: {e}"),
98 )
99 })?;
100 let row: Vec<Value> = record.iter().map(|s| Value::String(s.into())).collect();
101 rows.push(row);
102 }
103
104 Ok(rows)
105}