mirror of
https://github.com/gorules/zen.git
synced 2026-10-05 00:02:26 +00:00
feat: add validation methods to python bindings (#305)
* add validation methods to python bindings * centralize validation logic * rename functions * format
This commit is contained in:
@@ -11,6 +11,7 @@ crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
anyhow = { workspace = true }
|
||||
bumpalo = "3"
|
||||
either = "1.13"
|
||||
pyo3 = { version = "0.23", features = ["anyhow", "serde", "either"] }
|
||||
pyo3-async-runtimes = { version = "0.23", features = ["tokio-runtime", "attributes"] }
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
use crate::variable::PyVariable;
|
||||
use anyhow::{anyhow, Context};
|
||||
use either::Either;
|
||||
use pyo3::types::PyDict;
|
||||
use pyo3::types::{PyAnyMethods, PyDict};
|
||||
use pyo3::{pyclass, pyfunction, pymethods, Bound, IntoPyObjectExt, Py, PyAny, PyResult, Python};
|
||||
use pythonize::depythonize;
|
||||
use zen_expression::expression::{Standard, Unary};
|
||||
use zen_expression::validate::{validate_expression, validate_unary_expression, ValidationError};
|
||||
use zen_expression::{Expression, Variable};
|
||||
|
||||
#[pyfunction]
|
||||
@@ -97,3 +98,31 @@ impl PyExpression {
|
||||
PyVariable(result).into_py_any(py)
|
||||
}
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
#[pyo3(name = "validate_expression")]
|
||||
pub fn py_validate_expression(py: Python, expression: String) -> PyResult<Option<Py<PyDict>>> {
|
||||
let Some(err) = validate_expression(expression.as_str()) else {
|
||||
return Ok(None);
|
||||
};
|
||||
return Ok(Some(convert_error_to_dict(py, &err)));
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
#[pyo3(name = "validate_unary_expression")]
|
||||
pub fn py_validate_unary_expression(
|
||||
py: Python,
|
||||
expression: String,
|
||||
) -> PyResult<Option<Py<PyDict>>> {
|
||||
let Some(err) = validate_unary_expression(expression.as_str()) else {
|
||||
return Ok(None);
|
||||
};
|
||||
return Ok(Some(convert_error_to_dict(py, &err)));
|
||||
}
|
||||
|
||||
fn convert_error_to_dict(py: Python, err: &ValidationError) -> Py<PyDict> {
|
||||
let dict = PyDict::new(py);
|
||||
dict.set_item("type", &err.error_type).unwrap();
|
||||
dict.set_item("source", &err.source).unwrap();
|
||||
return dict.unbind();
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ use crate::decision::PyZenDecision;
|
||||
use crate::engine::PyZenEngine;
|
||||
use crate::expression::{
|
||||
compile_expression, compile_unary_expression, evaluate_expression, evaluate_unary_expression,
|
||||
render_template, PyExpression,
|
||||
py_validate_expression, py_validate_unary_expression, render_template, PyExpression,
|
||||
};
|
||||
use pyo3::prelude::PyModuleMethods;
|
||||
use pyo3::types::PyModule;
|
||||
@@ -28,6 +28,8 @@ fn zen(_py: Python, m: &Bound<'_, PyModule>) -> PyResult<()> {
|
||||
m.add_function(wrap_pyfunction!(render_template, m)?)?;
|
||||
m.add_function(wrap_pyfunction!(compile_expression, m)?)?;
|
||||
m.add_function(wrap_pyfunction!(compile_unary_expression, m)?)?;
|
||||
m.add_function(wrap_pyfunction!(py_validate_expression, m)?)?;
|
||||
m.add_function(wrap_pyfunction!(py_validate_unary_expression, m)?)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+14
-1
@@ -1,5 +1,5 @@
|
||||
from collections.abc import Awaitable
|
||||
from typing import Any, Optional, TypedDict
|
||||
from typing import Any, Optional, TypedDict, Literal
|
||||
|
||||
|
||||
class EvaluateResponse(TypedDict):
|
||||
@@ -42,5 +42,18 @@ def compile_expression(expression: str) -> Expression: ...
|
||||
|
||||
def compile_unary_expression(expression: str) -> Expression: ...
|
||||
|
||||
|
||||
class Expression:
|
||||
def evaluate(self, ctx: Optional[dict] = None) -> Any: ...
|
||||
|
||||
|
||||
def validate_expression(expression: str) -> Optional[ValidationResponse]: ...
|
||||
|
||||
|
||||
def validate_unary_expression(expression: str) -> Optional[ValidationResponse]: ...
|
||||
|
||||
|
||||
class ValidationResponse(TypedDict):
|
||||
type: Literal["lexerError","parserError", "compilerError"]
|
||||
source: str
|
||||
|
||||
|
||||
@@ -64,6 +64,7 @@ mod function;
|
||||
pub mod intellisense;
|
||||
pub mod lexer;
|
||||
pub mod parser;
|
||||
pub mod validate;
|
||||
pub mod variable;
|
||||
pub mod vm;
|
||||
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
use bumpalo::Bump;
|
||||
|
||||
use crate::{compiler::Compiler, lexer::Lexer, parser::Parser};
|
||||
|
||||
pub struct ValidationError {
|
||||
pub error_type: String,
|
||||
pub source: String,
|
||||
}
|
||||
|
||||
pub fn validate_unary_expression(expression: &str) -> Option<ValidationError> {
|
||||
let mut lexer = Lexer::new();
|
||||
let tokens = match lexer.tokenize(expression) {
|
||||
Err(e) => {
|
||||
return Some(ValidationError {
|
||||
error_type: "lexerError".to_string(),
|
||||
source: e.to_string(),
|
||||
})
|
||||
}
|
||||
Ok(tokens) => tokens,
|
||||
};
|
||||
|
||||
let bump = Bump::new();
|
||||
let parser = match Parser::try_new(tokens, &bump) {
|
||||
Err(e) => {
|
||||
return Some(ValidationError {
|
||||
error_type: "parserError".to_string(),
|
||||
source: e.to_string(),
|
||||
})
|
||||
}
|
||||
Ok(p) => p.unary(),
|
||||
};
|
||||
|
||||
let parser_result = parser.parse();
|
||||
match parser_result.error() {
|
||||
Err(e) => {
|
||||
return Some(ValidationError {
|
||||
error_type: "parserError".to_string(),
|
||||
source: e.to_string(),
|
||||
})
|
||||
}
|
||||
Ok(n) => n,
|
||||
};
|
||||
|
||||
let mut compiler = Compiler::new();
|
||||
if let Err(e) = compiler.compile(parser_result.root) {
|
||||
return Some(ValidationError {
|
||||
error_type: "compilerError".to_string(),
|
||||
source: e.to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
pub fn validate_expression(expression: &str) -> Option<ValidationError> {
|
||||
let mut lexer = Lexer::new();
|
||||
let tokens = match lexer.tokenize(expression) {
|
||||
Err(e) => {
|
||||
return Some(ValidationError {
|
||||
error_type: "lexerError".to_string(),
|
||||
source: e.to_string(),
|
||||
})
|
||||
}
|
||||
Ok(tokens) => tokens,
|
||||
};
|
||||
|
||||
let bump = Bump::new();
|
||||
let parser = match Parser::try_new(tokens, &bump) {
|
||||
Err(e) => {
|
||||
return Some(ValidationError {
|
||||
error_type: "parserError".to_string(),
|
||||
source: e.to_string(),
|
||||
})
|
||||
}
|
||||
Ok(p) => p.standard(),
|
||||
};
|
||||
|
||||
let parser_result = parser.parse();
|
||||
match parser_result.error() {
|
||||
Err(e) => {
|
||||
return Some(ValidationError {
|
||||
error_type: "parserError".to_string(),
|
||||
source: e.to_string(),
|
||||
})
|
||||
}
|
||||
Ok(n) => n,
|
||||
};
|
||||
|
||||
let mut compiler = Compiler::new();
|
||||
if let Err(e) = compiler.compile(parser_result.root) {
|
||||
return Some(ValidationError {
|
||||
error_type: "compilerError".to_string(),
|
||||
source: e.to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
Reference in New Issue
Block a user