helix/dna/ngs/
python.rs

1use pyo3::prelude::*;
2use pyo3::exceptions::{PyValueError, PyRuntimeError};
3use std::collections::HashMap;
4use crate::dna::atp::value::Value as HlxValue;
5use crate::dna::atp::interpreter::HelixInterpreter;
6use crate::dna::ops::fundamental::{OperatorRegistry, ExecutionContext, RequestData};
7#[pyclass]
8#[derive(Clone, Debug)]
9pub struct Value {
10    inner: HlxValue,
11}
12#[pymethods]
13impl Value {
14    fn as_string(&self) -> PyResult<String> {
15        match &self.inner {
16            HlxValue::String(s) => Ok(s.clone()),
17            _ => Err(PyValueError::new_err("Value is not a string")),
18        }
19    }
20    fn as_number(&self) -> PyResult<f64> {
21        match &self.inner {
22            HlxValue::Number(n) => Ok(*n),
23            _ => Err(PyValueError::new_err("Value is not a number")),
24        }
25    }
26    fn as_bool(&self) -> PyResult<bool> {
27        match &self.inner {
28            HlxValue::Bool(b) => Ok(*b),
29            _ => Err(PyValueError::new_err("Value is not a boolean")),
30        }
31    }
32    fn as_dict(&self) -> PyResult<HashMap<String, PyObject>> {
33        match &self.inner {
34            HlxValue::Object(obj) => {
35                let mut result = HashMap::new();
36                for (k, v) in obj {
37                    result.insert(k.clone(), value_to_pyobject(v)?);
38                }
39                Ok(result)
40            }
41            _ => Err(PyValueError::new_err("Value is not an object")),
42        }
43    }
44    fn as_list(&self) -> PyResult<Vec<PyObject>> {
45        match &self.inner {
46            HlxValue::Array(arr) => {
47                let mut result = Vec::new();
48                for v in arr {
49                    result.push(value_to_pyobject(v)?);
50                }
51                Ok(result)
52            }
53            _ => Err(PyValueError::new_err("Value is not an array")),
54        }
55    }
56    fn is_null(&self) -> bool {
57        matches!(& self.inner, HlxValue::Null)
58    }
59    fn to_python(&self, py: Python) -> PyObject {
60        value_to_pyobject(&self.inner).unwrap_or_else(|_| py.None())
61    }
62    fn __str__(&self) -> String {
63        format!("{:?}", self.inner)
64    }
65    fn __repr__(&self) -> String {
66        format!("Value({:?})", self.inner)
67    }
68}
69fn types_value_to_pyobject(value: &crate::dna::atp::types::Value) -> PyResult<PyObject> {
70    Python::with_gil(|py| {
71        match value {
72            crate::dna::atp::types::Value::String(s) => Ok(s.clone().into_py(py)),
73            crate::dna::atp::types::Value::Number(n) => Ok(n.into_py(py)),
74            crate::dna::atp::types::Value::Bool(b) => Ok(b.into_py(py)),
75            crate::dna::atp::types::Value::Array(arr) => {
76                let mut result = Vec::new();
77                for v in arr {
78                    result.push(types_value_to_pyobject(v)?);
79                }
80                Ok(result.into_py(py))
81            }
82            crate::dna::atp::types::Value::Object(obj) => {
83                let dict = pyo3::types::PyDict::new_bound(py);
84                for (k, v) in obj {
85                    dict.set_item(k, types_value_to_pyobject(v)?)?;
86                }
87                Ok(dict.unbind().into())
88            }
89            crate::dna::atp::types::Value::Null => Ok(py.None()),
90            _ => Ok(py.None()),
91        }
92    })
93}
94fn value_to_pyobject(value: &HlxValue) -> PyResult<PyObject> {
95    Python::with_gil(|py| {
96        match value {
97            HlxValue::String(s) => Ok(s.clone().into_py(py)),
98            HlxValue::Number(n) => Ok(n.into_py(py)),
99            HlxValue::Bool(b) => Ok(b.into_py(py)),
100            HlxValue::Array(arr) => {
101                let mut result = Vec::new();
102                for v in arr {
103                    result.push(value_to_pyobject(v)?);
104                }
105                Ok(result.into_py(py))
106            }
107            HlxValue::Object(obj) => {
108                let dict = pyo3::types::PyDict::new_bound(py);
109                for (k, v) in obj {
110                    dict.set_item(k, value_to_pyobject(v)?)?;
111                }
112                Ok(dict.unbind().into())
113            }
114            HlxValue::Null => Ok(py.None()),
115            _ => Ok(py.None()),
116        }
117    })
118}
119#[pyclass]
120#[derive(Clone, Debug)]
121pub struct PyExecutionContext {
122    inner: ExecutionContext,
123}
124#[pymethods]
125impl PyExecutionContext {
126    #[new]
127    fn new(
128        request: Option<HashMap<String, PyObject>>,
129        session: Option<HashMap<String, PyObject>>,
130        cookies: Option<HashMap<String, String>>,
131        params: Option<HashMap<String, String>>,
132        query: Option<HashMap<String, String>>,
133    ) -> Self {
134        let mut context = ExecutionContext::default();
135        if let Some(req) = request {
136            let mut request_data = HashMap::new();
137            for (k, v) in req {
138                request_data.insert(k, format!("{:?}", v));
139            }
140            context.request = Some(RequestData {
141                method: "GET".to_string(),
142                url: "".to_string(),
143                headers: HashMap::new(),
144                body: "".to_string(),
145            });
146        }
147        if let Some(session) = session {
148            // Handle session data if needed
149            for (k, v) in session {
150                // Store session data in context if needed
151                let _ = (k, v);
152            }
153        }
154        if let Some(cookies) = cookies {
155            context.cookies = cookies.clone();
156        }
157        if let Some(params) = params {
158            context.params = params.clone();
159        }
160        if let Some(query) = query {
161            context.query = query.clone();
162        }
163        PyExecutionContext {
164            inner: context,
165        }
166    }
167    #[getter]
168    fn request(&self) -> Option<HashMap<String, String>> {
169        self.inner
170            .request
171            .as_ref()
172            .map(|req| {
173                let mut result = HashMap::new();
174                result.insert("method".to_string(), req.method.clone());
175                result.insert("url".to_string(), req.url.clone());
176                result
177            })
178    }
179    #[getter]
180    fn session(&self) -> HashMap<String, String> {
181        HashMap::new()
182    }
183    #[getter]
184    fn cookies(&self) -> HashMap<String, String> {
185        self.inner.cookies.clone()
186    }
187    #[getter]
188    fn params(&self) -> HashMap<String, String> {
189        self.inner.params.clone()
190    }
191    #[getter]
192    fn query(&self) -> HashMap<String, String> {
193        self.inner.query.clone()
194    }
195}
196#[pyclass]
197pub struct PyOperatorRegistry {
198    inner: OperatorRegistry,
199}
200#[pymethods]
201impl PyOperatorRegistry {
202    #[new]
203    fn new(context: PyExecutionContext) -> PyResult<Self> {
204        let _ = context; // Use context parameter to avoid warning
205        let registry = tokio::runtime::Runtime::new()
206            .unwrap()
207            .block_on(async { OperatorRegistry::new().await })
208            .map_err(|e| PyRuntimeError::new_err(
209                format!("Failed to create registry: {}", e),
210            ))?;
211        Ok(PyOperatorRegistry {
212            inner: registry,
213        })
214    }
215    fn execute(&self, operator: String, params: String) -> PyResult<Value> {
216        let result = tokio::runtime::Runtime::new()
217            .unwrap()
218            .block_on(async { self.inner.execute(&operator, &params).await })
219            .map_err(|e| PyRuntimeError::new_err(
220                format!("Operator execution failed: {}", e),
221            ))?;
222        Ok(Value { inner: result })
223    }
224    fn context(&self) -> PyExecutionContext {
225        use std::sync::Arc;
226        let context = Arc::clone(&self.inner.context());
227        PyExecutionContext {
228            inner: (*context).clone(),
229        }
230    }
231}
232#[pyclass]
233#[derive(Clone, Debug)]
234pub struct PyHelixConfig {
235    data: HashMap<String, HlxValue>,
236}
237#[pymethods]
238impl PyHelixConfig {
239    #[new]
240    fn new() -> Self {
241        PyHelixConfig {
242            data: HashMap::new(),
243        }
244    }
245    fn get(&self, key: String) -> Option<PyObject> {
246        self.data
247            .get(&key)
248            .map(|value| {
249                Python::with_gil(|py| {
250                    match value {
251                        HlxValue::String(s) => s.clone().into_py(py),
252                        HlxValue::Number(n) => n.into_py(py),
253                        HlxValue::Bool(b) => b.into_py(py),
254                        HlxValue::Array(_) => format!("{:?}", value).into_py(py),
255                        HlxValue::Object(_) => format!("{:?}", value).into_py(py),
256                        HlxValue::Null => py.None(),
257                        HlxValue::Duration(d) => format!("{:?}", d).into_py(py),
258                        HlxValue::Reference(s) => s.clone().into_py(py),
259                        HlxValue::Identifier(s) => s.clone().into_py(py),
260                    }
261                })
262            })
263    }
264    fn set(&mut self, key: String, value: PyObject) -> PyResult<()> {
265        Python::with_gil(|py| {
266            let helix_value = if value.bind(py).is_none() {
267                HlxValue::Null
268            } else if let Ok(s) = value.extract::<String>(py) {
269                HlxValue::String(s)
270            } else if let Ok(n) = value.extract::<f64>(py) {
271                HlxValue::Number(n)
272            } else if let Ok(b) = value.extract::<bool>(py) {
273                HlxValue::Bool(b)
274            } else {
275                HlxValue::String(format!("{:?}", value))
276            };
277            self.data.insert(key, helix_value);
278            Ok(())
279        })
280    }
281    fn keys(&self) -> Vec<String> {
282        self.data.keys().cloned().collect()
283    }
284    fn items(&self) -> Vec<(String, PyObject)> {
285        self.data
286            .iter()
287            .map(|(k, v)| {
288                (
289                    k.clone(),
290                    Python::with_gil(|py| {
291                        value_to_pyobject(v).unwrap_or_else(|_| py.None())
292                    }),
293                )
294            })
295            .collect()
296    }
297}
298#[pyclass]
299pub struct PyHelixInterpreter {
300    inner: HelixInterpreter,
301}
302#[pymethods]
303impl PyHelixInterpreter {
304    #[new]
305    fn new() -> PyResult<Self> {
306        let interpreter = tokio::runtime::Runtime::new()
307            .unwrap()
308            .block_on(async { HelixInterpreter::new().await })
309            .map_err(|e| PyRuntimeError::new_err(
310                format!("Failed to create interpreter: {}", e),
311            ))?;
312        Ok(PyHelixInterpreter {
313            inner: interpreter,
314        })
315    }
316    fn execute<'py>(&self, py: Python<'py>, expression: String) -> PyResult<PyObject> {
317        let rt = tokio::runtime::Runtime::new()
318            .map_err(|e| PyRuntimeError::new_err(
319                format!("Failed to create runtime: {}", e),
320            ))?;
321        rt.block_on(async {
322            let mut interpreter = HelixInterpreter::new()
323                .await
324                .map_err(|e| PyRuntimeError::new_err(
325                    format!("Failed to create interpreter: {}", e),
326                ))?;
327            if let Some(value) = interpreter.get_variable(&expression) {
328                value_to_pyobject(value)
329            } else {
330                Ok(format!("Executed: {}", expression).into_py(py))
331            }
332        })
333    }
334    fn set_variable(&mut self, name: String, value: PyObject) -> PyResult<()> {
335        Python::with_gil(|py| {
336            let helix_value = match value.extract::<String>(py) {
337                Ok(s) => HlxValue::String(s),
338                Err(_) => match value.extract::<f64>(py) {
339                    Ok(n) => HlxValue::Number(n),
340                    Err(_) => match value.extract::<bool>(py) {
341                        Ok(b) => HlxValue::Bool(b),
342                        Err(_) => HlxValue::String(format!("{:?}", value.bind(py))),
343                    },
344                },
345            };
346            self.inner.set_variable(name, helix_value);
347            Ok(())
348        })
349    }
350    fn get_variable(&self, name: String) -> Option<PyObject> {
351        if let Some(value) = self.inner.get_variable(&name) {
352            Python::with_gil(|py| {
353                let obj: PyObject = match value {
354                    HlxValue::String(s) => s.clone().into_py(py),
355                    HlxValue::Number(n) => n.into_py(py),
356                    HlxValue::Bool(b) => b.into_py(py),
357                    _ => format!("{:?}", value).into_py(py),
358                };
359                Some(obj)
360            })
361        } else {
362            None
363        }
364    }
365}
366#[pyfunction]
367fn parse(source: String) -> PyResult<PyHelixConfig> {
368    let config_result = crate::parse_and_validate(&source)
369        .map_err(|e| PyValueError::new_err(format!("Parse error: {}", e)))?;
370    let mut py_config = PyHelixConfig::new();
371    if let Ok(config_json) = serde_json::to_string(&config_result) {
372        if let Ok(value) = serde_json::from_str::<serde_json::Value>(&config_json) {
373            if let serde_json::Value::Object(map) = value {
374                for (k, v) in map {
375                    let helix_value = match v {
376                        serde_json::Value::String(s) => HlxValue::String(s),
377                        serde_json::Value::Number(n) => {
378                            if let Some(f) = n.as_f64() {
379                                HlxValue::Number(f)
380                            } else if let Some(i) = n.as_i64() {
381                                HlxValue::Number(i as f64)
382                            } else {
383                                HlxValue::String(format!("{}", n))
384                            }
385                        }
386                        serde_json::Value::Bool(b) => HlxValue::Bool(b),
387                        serde_json::Value::Array(arr) => {
388                            let mut helix_array = Vec::new();
389                            for item in arr {
390                                match item {
391                                    serde_json::Value::String(s) => {
392                                        helix_array.push(HlxValue::String(s))
393                                    }
394                                    serde_json::Value::Number(n) => {
395                                        if let Some(f) = n.as_f64() {
396                                            helix_array.push(HlxValue::Number(f));
397                                        }
398                                    }
399                                    serde_json::Value::Bool(b) => {
400                                        helix_array.push(HlxValue::Bool(b))
401                                    }
402                                    _ => helix_array.push(HlxValue::String(format!("{}", item))),
403                                }
404                            }
405                            HlxValue::Array(helix_array)
406                        }
407                        serde_json::Value::Object(obj) => {
408                            let mut helix_obj = HashMap::new();
409                            for (obj_k, obj_v) in obj {
410                                match obj_v {
411                                    serde_json::Value::String(s) => {
412                                        helix_obj.insert(obj_k, HlxValue::String(s));
413                                    }
414                                    serde_json::Value::Number(n) => {
415                                        if let Some(f) = n.as_f64() {
416                                            helix_obj.insert(obj_k, HlxValue::Number(f));
417                                        }
418                                    }
419                                    serde_json::Value::Bool(b) => {
420                                        helix_obj.insert(obj_k, HlxValue::Bool(b));
421                                    }
422                                    _ => {
423                                        helix_obj
424                                            .insert(obj_k, HlxValue::String(format!("{}", obj_v)));
425                                    }
426                                }
427                            }
428                            HlxValue::Object(helix_obj)
429                        }
430                        serde_json::Value::Null => HlxValue::Null,
431                    };
432                    py_config.data.insert(k, helix_value);
433                }
434            }
435        }
436    }
437    Ok(py_config)
438}
439#[pyfunction]
440fn execute(
441    expression: String,
442    context: Option<HashMap<String, PyObject>>,
443) -> PyResult<PyObject> {
444    let rt = tokio::runtime::Runtime::new()
445        .map_err(|e| PyRuntimeError::new_err(
446            format!("Failed to create runtime: {}", e),
447        ))?;
448    rt.block_on(async {
449        let mut interpreter = HelixInterpreter::new()
450            .await
451            .map_err(|e| PyRuntimeError::new_err(
452                format!("Failed to create interpreter: {}", e),
453            ))?;
454        if let Some(ctx) = context {
455            for (key, value) in ctx {
456                let helix_value = Python::with_gil(|py| {
457                    match value.extract::<String>(py) {
458                        Ok(s) => HlxValue::String(s),
459                        Err(_) => match value.extract::<f64>(py) {
460                            Ok(n) => HlxValue::Number(n),
461                            Err(_) => match value.extract::<bool>(py) {
462                                Ok(b) => HlxValue::Bool(b),
463                                Err(_) => HlxValue::String(format!("{:?}", value.bind(py))),
464                            },
465                        },
466                    }
467                });
468                // Store a clone for set_variable since helix_value will be moved
469                let helix_value_clone = helix_value.clone();
470                // Convert from atp::value::Value to atp::types::Value
471                let types_value = match helix_value {
472                    HlxValue::String(s) => crate::dna::atp::types::Value::String(s),
473                    HlxValue::Number(n) => crate::dna::atp::types::Value::Number(n),
474                    HlxValue::Bool(b) => crate::dna::atp::types::Value::Bool(b),
475                    HlxValue::Array(arr) => crate::dna::atp::types::Value::Array(
476                        arr.into_iter().map(|v| match v {
477                            HlxValue::String(s) => crate::dna::atp::types::Value::String(s),
478                            HlxValue::Number(n) => crate::dna::atp::types::Value::Number(n),
479                            HlxValue::Bool(b) => crate::dna::atp::types::Value::Bool(b),
480                            HlxValue::Null => crate::dna::atp::types::Value::Null,
481                            _ => crate::dna::atp::types::Value::String(format!("{:?}", v)),
482                        }).collect()
483                    ),
484                    HlxValue::Object(obj) => crate::dna::atp::types::Value::Object(
485                        obj.into_iter().map(|(k, v)| (k, match v {
486                            HlxValue::String(s) => crate::dna::atp::types::Value::String(s),
487                            HlxValue::Number(n) => crate::dna::atp::types::Value::Number(n),
488                            HlxValue::Bool(b) => crate::dna::atp::types::Value::Bool(b),
489                            HlxValue::Null => crate::dna::atp::types::Value::Null,
490                            _ => crate::dna::atp::types::Value::String(format!("{:?}", v)),
491                        })).collect()
492                    ),
493                    HlxValue::Null => crate::dna::atp::types::Value::Null,
494                    HlxValue::Duration(d) => crate::dna::atp::types::Value::Duration(d),
495                    HlxValue::Reference(r) => crate::dna::atp::types::Value::Reference(r),
496                    HlxValue::Identifier(i) => crate::dna::atp::types::Value::Identifier(i),
497                };
498                interpreter.set_variable(key.to_string(), helix_value_clone);
499            }
500        }
501        if let Some(value) = interpreter.get_variable(&expression) {
502            Python::with_gil(|py| {
503                let obj: PyObject = match value {
504                    HlxValue::String(s) => s.clone().into_py(py),
505                    HlxValue::Number(n) => n.into_py(py),
506                    HlxValue::Bool(b) => b.into_py(py),
507                    _ => format!("{:?}", value).into_py(py),
508                };
509                Ok(obj)
510            })
511        } else {
512            Python::with_gil(|py| {
513                Ok(format!("Executed: {}", expression).into_py(py))
514            })
515        }
516    })
517}
518#[pyfunction]
519fn load_file(file_path: String) -> PyResult<PyHelixConfig> {
520    let config_result = crate::load_file(&file_path)
521        .map_err(|e| PyValueError::new_err(format!("File load error: {}", e)))?;
522    let mut py_config = PyHelixConfig::new();
523    if let Ok(config_json) = serde_json::to_string(&config_result) {
524        if let Ok(value) = serde_json::from_str::<serde_json::Value>(&config_json) {
525            if let serde_json::Value::Object(map) = value {
526                for (k, v) in map {
527                    let helix_value = match v {
528                        serde_json::Value::String(s) => HlxValue::String(s),
529                        serde_json::Value::Number(n) => {
530                            if let Some(f) = n.as_f64() {
531                                HlxValue::Number(f)
532                            } else {
533                                HlxValue::String(format!("{}", n))
534                            }
535                        }
536                        serde_json::Value::Bool(b) => HlxValue::Bool(b),
537                        serde_json::Value::Array(arr) => {
538                            let mut helix_array = Vec::new();
539                            for item in arr {
540                                match item {
541                                    serde_json::Value::String(s) => {
542                                        helix_array.push(HlxValue::String(s))
543                                    }
544                                    serde_json::Value::Number(n) => {
545                                        if let Some(f) = n.as_f64() {
546                                            helix_array.push(HlxValue::Number(f));
547                                        }
548                                    }
549                                    serde_json::Value::Bool(b) => {
550                                        helix_array.push(HlxValue::Bool(b))
551                                    }
552                                    _ => helix_array.push(HlxValue::String(format!("{}", item))),
553                                }
554                            }
555                            HlxValue::Array(helix_array)
556                        }
557                        serde_json::Value::Object(obj) => {
558                            let mut helix_obj = HashMap::new();
559                            for (obj_k, obj_v) in obj {
560                                match obj_v {
561                                    serde_json::Value::String(s) => {
562                                        helix_obj.insert(obj_k, HlxValue::String(s));
563                                    }
564                                    serde_json::Value::Number(n) => {
565                                        if let Some(f) = n.as_f64() {
566                                            helix_obj.insert(obj_k, HlxValue::Number(f));
567                                        }
568                                    }
569                                    serde_json::Value::Bool(b) => {
570                                        helix_obj.insert(obj_k, HlxValue::Bool(b));
571                                    }
572                                    _ => {
573                                        helix_obj
574                                            .insert(obj_k, HlxValue::String(format!("{}", obj_v)));
575                                    }
576                                }
577                            }
578                            HlxValue::Object(helix_obj)
579                        }
580                        serde_json::Value::Null => HlxValue::Null,
581                    };
582                    py_config.data.insert(k, helix_value);
583                }
584            }
585        }
586    }
587    Ok(py_config)
588}
589#[pymodule]
590fn _core(m: &Bound<'_, PyModule>) -> PyResult<()> {
591    m.add_class::<Value>()?;
592    m.add_class::<PyExecutionContext>()?;
593    m.add_class::<PyOperatorRegistry>()?;
594    m.add_class::<PyHelixConfig>()?;
595    m.add_class::<PyHelixInterpreter>()?;
596    m.add_function(wrap_pyfunction_bound!(parse, m)?)?;
597    m.add_function(wrap_pyfunction_bound!(execute, m)?)?;
598    m.add_function(wrap_pyfunction_bound!(load_file, m)?)?;
599    Ok(())
600}