compiled, needs further progress
This commit is contained in:
@@ -1 +1,4 @@
|
||||
// src/steel/handlers.rs
|
||||
pub mod execution;
|
||||
|
||||
pub use execution::*;
|
||||
|
||||
66
server/src/steel/handlers/execution.rs
Normal file
66
server/src/steel/handlers/execution.rs
Normal file
@@ -0,0 +1,66 @@
|
||||
// src/steel/execution.rs
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ScriptOperation {
|
||||
SetToColumn { source: String },
|
||||
// Future operations can be added here
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ScriptExecutionError {
|
||||
ParseError(String),
|
||||
MissingSourceColumn(String),
|
||||
Mismatch { expected: String, actual: String },
|
||||
}
|
||||
|
||||
impl fmt::Display for ScriptExecutionError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::ParseError(msg) => write!(f, "Parse error: {}", msg),
|
||||
Self::MissingSourceColumn(col) => write!(f, "Missing source column: {}", col),
|
||||
Self::Mismatch { expected, actual } => write!(
|
||||
f,
|
||||
"Value does not match script expectation. Expected: {}, Actual: {}",
|
||||
expected, actual
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_script(script: &str, expected_target: &str) -> Result<ScriptOperation, ScriptExecutionError> {
|
||||
let script = script.trim();
|
||||
|
||||
if !script.starts_with("(set! ") || !script.ends_with(')') {
|
||||
return Err(ScriptExecutionError::ParseError(
|
||||
"Script must be in the form (set! target source)".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
let content = &script[6..script.len() - 1].trim();
|
||||
let mut parts = content.split_whitespace();
|
||||
|
||||
let target = parts.next().ok_or_else(|| {
|
||||
ScriptExecutionError::ParseError("Missing target in set! expression".to_string())
|
||||
})?;
|
||||
let source = parts.next().ok_or_else(|| {
|
||||
ScriptExecutionError::ParseError("Missing source in set! expression".to_string())
|
||||
})?;
|
||||
|
||||
if parts.next().is_some() {
|
||||
return Err(ScriptExecutionError::ParseError(
|
||||
"Too many arguments in set! expression".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
if target != expected_target {
|
||||
return Err(ScriptExecutionError::ParseError(format!(
|
||||
"Script target '{}' does not match expected '{}'",
|
||||
target, expected_target
|
||||
)));
|
||||
}
|
||||
|
||||
Ok(ScriptOperation::SetToColumn {
|
||||
source: source.to_string(),
|
||||
})
|
||||
}
|
||||
@@ -5,6 +5,10 @@ use common::proto::multieko2::table_script::{PostTableScriptRequest, TableScript
|
||||
use crate::steel::validation::script::validate_script;
|
||||
use serde_json::Value;
|
||||
|
||||
// Add these imports for the execution module and ScriptOperation
|
||||
use crate::steel::handlers::execution;
|
||||
use crate::steel::handlers::ScriptOperation;
|
||||
|
||||
const SYSTEM_COLUMNS: &[&str] = &["id", "deleted", "created_at"];
|
||||
|
||||
fn validate_target_column(
|
||||
@@ -51,6 +55,15 @@ pub async fn post_table_script(
|
||||
.map_err(|e| Status::internal(format!("Database error: {}", e)))?
|
||||
.ok_or_else(|| Status::not_found("Table definition not found"))?;
|
||||
|
||||
// Use the full path to parse_script
|
||||
let operation = execution::parse_script(&request.script, &request.target_column)
|
||||
.map_err(|e| Status::invalid_argument(e.to_string()))?;
|
||||
|
||||
// Ensure the operation is valid (additional checks if needed)
|
||||
match operation {
|
||||
ScriptOperation::SetToColumn { .. } => {}, // Use directly without 'execution::'
|
||||
}
|
||||
|
||||
// Call validation functions
|
||||
validate_script(&request.script)
|
||||
.map_err(|e| Status::invalid_argument(e.to_string()))?;
|
||||
|
||||
@@ -4,6 +4,7 @@ use sqlx::{PgPool, Arguments};
|
||||
use sqlx::postgres::PgArguments;
|
||||
use chrono::{DateTime, Utc};
|
||||
use common::proto::multieko2::tables_data::{PostTableDataRequest, PostTableDataResponse};
|
||||
use crate::steel::handlers::execution::{self, ScriptOperation};
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub async fn post_table_data(
|
||||
@@ -100,6 +101,56 @@ pub async fn post_table_data(
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Validate Steel scripts
|
||||
let scripts = sqlx::query!(
|
||||
"SELECT target_column, script FROM table_scripts WHERE table_definitions_id = $1",
|
||||
table_def.id
|
||||
)
|
||||
.fetch_all(db_pool)
|
||||
.await
|
||||
.map_err(|e| Status::internal(format!("Failed to fetch scripts: {}", e)))?;
|
||||
|
||||
for script_record in scripts {
|
||||
let target_column = script_record.target_column;
|
||||
|
||||
// Check if target column is present in data
|
||||
if !data.contains_key(&target_column) {
|
||||
return Err(Status::invalid_argument(
|
||||
format!("Column '{}' is required due to an associated script", target_column)
|
||||
));
|
||||
}
|
||||
|
||||
// Parse the script
|
||||
let operation = execution::parse_script(&script_record.script, &target_column)
|
||||
.map_err(|e| Status::invalid_argument(e.to_string()))?;
|
||||
|
||||
// Get source column from operation
|
||||
let source_column = match operation {
|
||||
ScriptOperation::SetToColumn { source } => source,
|
||||
};
|
||||
|
||||
// Check source column presence
|
||||
let source_value = data.get(&source_column)
|
||||
.ok_or_else(|| Status::invalid_argument(
|
||||
format!("Source column '{}' required by script for '{}' is missing", source_column, target_column)
|
||||
))?;
|
||||
|
||||
// Get target value
|
||||
let target_value = data.get(&target_column)
|
||||
.ok_or_else(|| Status::invalid_argument(
|
||||
format!("Target column '{}' is missing in data", target_column)
|
||||
))?;
|
||||
|
||||
// Validate value match
|
||||
if target_value != source_value {
|
||||
return Err(Status::invalid_argument(
|
||||
format!("Value for '{}' must match '{}' as per script. Expected '{}', got '{}'",
|
||||
target_column, source_column, source_value, target_value)
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// Prepare SQL parameters
|
||||
let mut params = PgArguments::default();
|
||||
let mut columns_list = Vec::new();
|
||||
|
||||
Reference in New Issue
Block a user