compiled, needs further progress

This commit is contained in:
filipriec
2025-03-08 12:49:54 +01:00
parent 9a87940311
commit 6281702575
4 changed files with 133 additions and 0 deletions

View File

@@ -1 +1,4 @@
// src/steel/handlers.rs // src/steel/handlers.rs
pub mod execution;
pub use execution::*;

View 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(),
})
}

View File

@@ -5,6 +5,10 @@ use common::proto::multieko2::table_script::{PostTableScriptRequest, TableScript
use crate::steel::validation::script::validate_script; use crate::steel::validation::script::validate_script;
use serde_json::Value; 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"]; const SYSTEM_COLUMNS: &[&str] = &["id", "deleted", "created_at"];
fn validate_target_column( fn validate_target_column(
@@ -51,6 +55,15 @@ pub async fn post_table_script(
.map_err(|e| Status::internal(format!("Database error: {}", e)))? .map_err(|e| Status::internal(format!("Database error: {}", e)))?
.ok_or_else(|| Status::not_found("Table definition not found"))?; .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 // Call validation functions
validate_script(&request.script) validate_script(&request.script)
.map_err(|e| Status::invalid_argument(e.to_string()))?; .map_err(|e| Status::invalid_argument(e.to_string()))?;

View File

@@ -4,6 +4,7 @@ use sqlx::{PgPool, Arguments};
use sqlx::postgres::PgArguments; use sqlx::postgres::PgArguments;
use chrono::{DateTime, Utc}; use chrono::{DateTime, Utc};
use common::proto::multieko2::tables_data::{PostTableDataRequest, PostTableDataResponse}; use common::proto::multieko2::tables_data::{PostTableDataRequest, PostTableDataResponse};
use crate::steel::handlers::execution::{self, ScriptOperation};
use std::collections::HashMap; use std::collections::HashMap;
pub async fn post_table_data( 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 // Prepare SQL parameters
let mut params = PgArguments::default(); let mut params = PgArguments::default();
let mut columns_list = Vec::new(); let mut columns_list = Vec::new();