1pub mod dna {
2 pub mod atp;
3 pub mod bch;
4 pub mod cmd;
5 pub mod exp;
6 pub mod ffi;
7 pub mod hel;
8 pub mod map;
9 pub mod mds;
10 pub mod ngs;
11 pub mod ops;
12 pub mod out;
13 pub mod tst;
14 pub mod compiler;
15}
16pub use dna::atp;
17pub use dna::bch;
18pub use dna::cmd;
19pub use dna::exp;
20pub use dna::ffi;
21pub use dna::hel;
22pub use dna::map;
23pub use dna::mds;
24pub use dna::ngs;
25pub use dna::ops;
26pub use dna::out;
27pub use dna::tst;
28pub use dna::compiler;
29pub use dna::compiler::Compiler;
30pub use dna::mds::optimizer::OptimizationLevel;
31#[cfg(test)]
32mod tests;
33#[cfg(test)]
34mod bch;
35
36#[cfg(test)]
37#[path = "dna/tst/integration_tests.rs"]
38mod integration_tests;
39
40pub use crate::dna::atp::types::{
41 HelixConfig, ProjectConfig, AgentConfig, WorkflowConfig, MemoryConfig, ContextConfig,
42 CrewConfig, PipelineConfig, RetryConfig, TriggerConfig, StepConfig, Value,
43 load_default_config, DataFormat, TrainingFormat, GenericJSONDataset, TrainingDataset,
44 TrainingSample, AlgorithmFormat,
45};
46pub use crate::dna::out::hlxb_config_format::{
47 HlxbWriter, HlxbReader, HlxbHeader, HLXB_MAGIC, HLXB_VERSION,
48};
49pub use crate::dna::atp::ast::{
50 HelixAst, Declaration, Expression, Statement, AgentDecl, WorkflowDecl, MemoryDecl,
51 ContextDecl, CrewDecl, PipelineDecl,
52};
53pub use crate::dna::atp::lexer::{Token, SourceLocation};
54pub use crate::dna::atp::parser::{Parser, ParseError};
55pub use crate::dna::mds::semantic::{SemanticAnalyzer, SemanticError};
56pub use crate::dna::mds::codegen::{CodeGenerator, HelixIR};
57pub use crate::dna::atp::types::HelixLoader;
58pub use crate::dna::mds::server::{HelixServer, ServerConfig};
59use std::path::Path;
60type ParseResult<T> = Result<T, ParseError>;
61#[cfg(feature = "js")]
62use napi::bindgen_prelude::*;
63#[cfg(feature = "js")]
64use napi_derive::napi;
65#[cfg(feature = "js")]
66#[derive(Debug, Clone)]
67pub struct NapiStringError(pub String);
68#[cfg(feature = "js")]
69impl AsRef<str> for NapiStringError {
70 fn as_ref(&self) -> &str {
71 &self.0
72 }
73}
74#[cfg(feature = "js")]
75impl From<String> for NapiStringError {
76 fn from(s: String) -> Self {
77 NapiStringError(s)
78 }
79}
80#[cfg(feature = "js")]
81impl std::fmt::Display for NapiStringError {
82 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
83 write!(f, "{}", self.0)
84 }
85}
86#[cfg(feature = "js")]
87impl std::error::Error for NapiStringError {}
88#[cfg(feature = "js")]
89impl From<NapiStringError> for napi::Error<NapiStringError> {
90 fn from(err: NapiStringError) -> Self {
91 napi::Error::new(err, napi::Status::GenericFailure)
92 }
93}
94#[cfg(feature = "js")]
95type ConfigResult<T> = Result<T, NapiStringError>;
96#[cfg(not(feature = "js"))]
97type ConfigResult<T> = Result<T, String>;
98pub fn parse(source: &str) -> std::result::Result<HelixAst, ParseError> {
99 parse_with_locations(source).or_else(|_| parse_legacy(source))
100}
101pub fn parse_with_locations(source: &str) -> std::result::Result<HelixAst, ParseError> {
102 use crate::dna::atp::lexer::{tokenize_with_locations, SourceMap};
103 let tokens_with_loc = match tokenize_with_locations(source) {
104 Ok(tokens) => tokens,
105 Err(e) => {
106 return Err(ParseError {
107 message: format!("Lexer error: {}", e),
108 location: None,
109 token_index: 0,
110 expected: None,
111 found: String::new(),
112 context: String::new(),
113 });
114 }
115 };
116 let source_map = SourceMap {
117 tokens: tokens_with_loc.clone(),
118 source: source.to_string(),
119 };
120 let mut parser = Parser::new_with_source_map(source_map);
121 match parser.parse() {
122 Ok(ast) => Ok(ast),
123 Err(msg) => {
124 Err(ParseError {
125 message: msg,
126 location: None,
127 token_index: 0,
128 expected: None,
129 found: String::new(),
130 context: String::new(),
131 })
132 }
133 }
134}
135fn parse_legacy(source: &str) -> std::result::Result<HelixAst, ParseError> {
136 let tokens = match crate::dna::atp::lexer::tokenize(source) {
137 Ok(tokens) => tokens,
138 Err(e) => {
139 return Err(ParseError {
140 message: format!("Lexer error: {}", e),
141 location: None,
142 token_index: 0,
143 expected: None,
144 found: String::new(),
145 context: String::new(),
146 });
147 }
148 };
149 let mut parser = Parser::new(tokens);
150 match parser.parse() {
151 Ok(ast) => Ok(ast),
152 Err(msg) => {
153 Err(ParseError {
154 message: msg,
155 location: None,
156 token_index: 0,
157 expected: None,
158 found: String::new(),
159 context: String::new(),
160 })
161 }
162 }
163}
164#[cfg(feature = "js")]
165pub fn parse_and_validate(
166 source: &str,
167) -> std::result::Result<HelixConfig, NapiStringError> {
168 let ast = match parse(source) {
169 Ok(ast) => ast,
170 Err(e) => return Err(NapiStringError(e.to_string())),
171 };
172 validate(&ast)?;
173 ast_to_config(ast)
174}
175#[cfg(not(feature = "js"))]
176pub fn parse_and_validate(source: &str) -> std::result::Result<HelixConfig, String> {
177 let ast = match parse(source) {
178 Ok(ast) => ast,
179 Err(e) => return Err(e.to_string()),
180 };
181 validate(&ast)?;
182 ast_to_config(ast)
183}
184#[cfg(feature = "js")]
185pub fn validate(ast: &HelixAst) -> std::result::Result<(), NapiStringError> {
186 let mut analyzer = SemanticAnalyzer::new();
187 match analyzer.analyze(ast) {
188 Ok(()) => Ok(()),
189 Err(errors) => {
190 Err(
191 NapiStringError(
192 errors
193 .iter()
194 .map(|e| format!("{:?}", e))
195 .collect::<Vec<_>>()
196 .join("\n"),
197 ),
198 )
199 }
200 }
201}
202#[cfg(not(feature = "js"))]
203pub fn validate(ast: &HelixAst) -> std::result::Result<(), String> {
204 let mut analyzer = SemanticAnalyzer::new();
205 match analyzer.analyze(ast) {
206 Ok(()) => Ok(()),
207 Err(errors) => {
208 Err(errors.iter().map(|e| format!("{:?}", e)).collect::<Vec<_>>().join("\n"))
209 }
210 }
211}
212#[cfg(feature = "js")]
213pub fn ast_to_config(
214 ast: HelixAst,
215) -> std::result::Result<HelixConfig, NapiStringError> {
216 let loader = crate::dna::atp::types::HelixLoader::new();
217 match loader.ast_to_config(ast) {
218 Ok(config) => Ok(config),
219 Err(e) => Err(NapiStringError(e.to_string())),
220 }
221}
222#[cfg(not(feature = "js"))]
223pub fn ast_to_config(ast: HelixAst) -> std::result::Result<HelixConfig, String> {
224 let loader = crate::dna::atp::types::HelixLoader::new();
225 match loader.ast_to_config(ast) {
226 Ok(config) => Ok(config),
227 Err(e) => Err(e.to_string()),
228 }
229}
230#[cfg(feature = "js")]
231pub fn load_file<P: AsRef<Path>>(
232 path: P,
233) -> std::result::Result<HelixConfig, NapiStringError> {
234 let content = match std::fs::read_to_string(path) {
235 Ok(content) => content,
236 Err(e) => return Err(NapiStringError(format!("Failed to read file: {}", e))),
237 };
238 parse_and_validate(&content)
239}
240#[cfg(not(feature = "js"))]
241pub fn load_file<P: AsRef<Path>>(path: P) -> std::result::Result<HelixConfig, String> {
242 let content = match std::fs::read_to_string(path) {
243 Ok(content) => content,
244 Err(e) => return Err(format!("Failed to read file: {}", e)),
245 };
246 parse_and_validate(&content)
247}
248#[cfg(feature = "js")]
249pub fn load_directory<P: AsRef<Path>>(
250 path: P,
251) -> std::result::Result<Vec<HelixConfig>, NapiStringError> {
252 let mut configs = Vec::new();
253 let entries = match std::fs::read_dir(path) {
254 Ok(entries) => entries,
255 Err(e) => return Err(NapiStringError(format!("Failed to read directory: {}", e))),
256 };
257 for entry in entries {
258 let entry = match entry {
259 Ok(entry) => entry,
260 Err(e) => return Err(NapiStringError(format!("Failed to read entry: {}", e))),
261 };
262 let path = entry.path();
263 if path.extension().and_then(|s| s.to_str()) == Some("HELIX") {
264 let config = match load_file(&path) {
265 Ok(config) => config,
266 Err(e) => return Err(e),
267 };
268 configs.push(config);
269 }
270 }
271 Ok(configs)
272}
273#[cfg(not(feature = "js"))]
274pub fn load_directory<P: AsRef<Path>>(
275 path: P,
276) -> std::result::Result<Vec<HelixConfig>, String> {
277 let mut configs = Vec::new();
278 let entries = match std::fs::read_dir(path) {
279 Ok(entries) => entries,
280 Err(e) => return Err(format!("Failed to read directory: {}", e)),
281 };
282 for entry in entries {
283 let entry = match entry {
284 Ok(entry) => entry,
285 Err(e) => return Err(format!("Failed to read entry: {}", e)),
286 };
287 let path = entry.path();
288 if path.extension().and_then(|s| s.to_str()) == Some("HELIX") {
289 let config = match load_file(&path) {
290 Ok(config) => config,
291 Err(e) => return Err(e),
292 };
293 configs.push(config);
294 }
295 }
296 Ok(configs)
297}
298pub fn pretty_print(ast: &HelixAst) -> String {
299 let mut printer = crate::dna::atp::ast::AstPrettyPrinter::new();
300 printer.print(ast)
301}
302#[cfg(feature = "php")]
303use std::ffi::{CStr, CString};
304#[cfg(feature = "php")]
305use std::os::raw::c_char;
306#[cfg(feature = "php")]
308#[no_mangle]
309pub extern "C" fn helix_execute_ffi(code_ptr: *const c_char) -> *mut c_char {
310 if code_ptr.is_null() {
311 return std::ptr::null_mut();
312 }
313 let code = unsafe { CStr::from_ptr(code_ptr) };
314 let code_str = match code.to_str() {
315 Ok(s) => s,
316 Err(_) => {
317 let error_str = "Error: Invalid UTF-8 in code string";
318 if let Ok(cstring) = CString::new(error_str) {
319 return cstring.into_raw();
320 }
321 return std::ptr::null_mut();
322 }
323 };
324 let runtime = match tokio::runtime::Runtime::new() {
325 Ok(rt) => rt,
326 Err(e) => {
327 let error_str = format!("Error: Failed to create runtime: {}", e);
328 if let Ok(cstring) = CString::new(error_str) {
329 return cstring.into_raw();
330 }
331 return std::ptr::null_mut();
332 }
333 };
334 let result = runtime
335 .block_on(async {
336 let mut hlx = match crate::dna_hlx::Hlx::new().await {
337 Ok(h) => h,
338 Err(e) => return Err(format!("Failed to initialize Helix: {}", e)),
339 };
340 hlx.execute(code_str).await.map_err(|e| format!("Execution error: {}", e))
341 });
342 match result {
343 Ok(value) => {
344 let result_str = format!("{}", value);
345 match CString::new(result_str) {
346 Ok(cstring) => cstring.into_raw(),
347 Err(e) => {
348 let error_str = format!(
349 "Error: Failed to create result string: {}", e
350 );
351 if let Ok(cstring) = CString::new(error_str) {
352 cstring.into_raw()
353 } else {
354 std::ptr::null_mut()
355 }
356 }
357 }
358 }
359 Err(e) => {
360 match CString::new(e) {
361 Ok(cstring) => cstring.into_raw(),
362 Err(_) => {
363 let error_str = "Error: Failed to create error string";
364 if let Ok(cstring) = CString::new(error_str) {
365 cstring.into_raw()
366 } else {
367 std::ptr::null_mut()
368 }
369 }
370 }
371 }
372 }
373}
374#[cfg(feature = "php")]
376#[no_mangle]
377pub extern "C" fn helix_parse_ffi(code_ptr: *const c_char) -> *mut c_char {
378 if code_ptr.is_null() {
379 return std::ptr::null_mut();
380 }
381 let code = unsafe { CStr::from_ptr(code_ptr) };
382 let code_str = match code.to_str() {
383 Ok(s) => s,
384 Err(_) => {
385 let error_str = "Error: Invalid UTF-8 in code string";
386 if let Ok(cstring) = CString::new(error_str) {
387 return cstring.into_raw();
388 }
389 return std::ptr::null_mut();
390 }
391 };
392 let dispatcher = crate::dispatch::HelixDispatcher::new();
393 let result = dispatcher.parse_only(code_str);
394 match result {
395 Ok(ast) => {
396 let ast_json = match serde_json::to_string_pretty(&ast) {
397 Ok(json) => json,
398 Err(_) => format!("{:?}", ast),
399 };
400 let result_str = format!("Parsed AST:\n{}", ast_json);
401 match CString::new(result_str) {
402 Ok(cstring) => cstring.into_raw(),
403 Err(e) => {
404 let error_str = format!("Error: Failed to create AST string: {}", e);
405 if let Ok(cstring) = CString::new(error_str) {
406 cstring.into_raw()
407 } else {
408 std::ptr::null_mut()
409 }
410 }
411 }
412 }
413 Err(e) => {
414 let error_str = format!("Parse Error: {}", e);
415 match CString::new(error_str) {
416 Ok(cstring) => cstring.into_raw(),
417 Err(_) => {
418 let error_str = "Error: Failed to create parse error string";
419 if let Ok(cstring) = CString::new(error_str) {
420 cstring.into_raw()
421 } else {
422 std::ptr::null_mut()
423 }
424 }
425 }
426 }
427 }
428}
429#[cfg(feature = "php")]
431#[no_mangle]
432pub extern "C" fn helix_load_file_ffi(file_path_ptr: *const c_char) -> *mut c_char {
433 if file_path_ptr.is_null() {
434 return std::ptr::null_mut();
435 }
436 let file_path = unsafe { CStr::from_ptr(file_path_ptr) };
437 let file_path_str = match file_path.to_str() {
438 Ok(s) => s,
439 Err(_) => {
440 let error_str = "Error: Invalid UTF-8 in file path";
441 if let Ok(cstring) = CString::new(error_str) {
442 return cstring.into_raw();
443 }
444 return std::ptr::null_mut();
445 }
446 };
447 let runtime = match tokio::runtime::Runtime::new() {
448 Ok(rt) => rt,
449 Err(e) => {
450 let error_str = format!("Error: Failed to create runtime: {}", e);
451 if let Ok(cstring) = CString::new(error_str) {
452 return cstring.into_raw();
453 }
454 return std::ptr::null_mut();
455 }
456 };
457 let result = runtime
458 .block_on(async {
459 let content = std::fs::read_to_string(file_path_str)
460 .map_err(|e| format!("Failed to read file '{}': {}", file_path_str, e))?;
461 let mut hlx = crate::dna_hlx::Hlx::new()
462 .await
463 .map_err(|e| format!("Failed to initialize Helix: {}", e))?;
464 hlx.execute(&content).await.map_err(|e| format!("Execution error: {}", e))
465 });
466 match result {
467 Ok(value) => {
468 let result_str = format!(
469 "File '{}' executed successfully:\n{}", file_path_str, value
470 );
471 match CString::new(result_str) {
472 Ok(cstring) => cstring.into_raw(),
473 Err(e) => {
474 let error_str = format!(
475 "Error: Failed to create result string: {}", e
476 );
477 if let Ok(cstring) = CString::new(error_str) {
478 cstring.into_raw()
479 } else {
480 std::ptr::null_mut()
481 }
482 }
483 }
484 }
485 Err(e) => {
486 match CString::new(e) {
487 Ok(cstring) => cstring.into_raw(),
488 Err(_) => {
489 let error_str = "Error: Failed to create error string";
490 if let Ok(cstring) = CString::new(error_str) {
491 cstring.into_raw()
492 } else {
493 std::ptr::null_mut()
494 }
495 }
496 }
497 }
498 }
499}
500#[cfg(feature = "php")]
502#[no_mangle]
503pub extern "C" fn helix_free_string(ptr: *mut c_char) {
504 if !ptr.is_null() {
505 unsafe {
506 let _ = CString::from_raw(ptr);
507 }
508 }
509}
510#[cfg(feature = "php")]
512#[no_mangle]
513pub extern "C" fn helix_version() -> *mut c_char {
514 let version = env!("CARGO_PKG_VERSION");
515 match CString::new(version) {
516 Ok(cstring) => cstring.into_raw(),
517 Err(_) => std::ptr::null_mut(),
518 }
519}
520#[cfg(feature = "php")]
522#[no_mangle]
523pub extern "C" fn helix_test_ffi() -> *mut c_char {
524 match CString::new("Hello from Helix PHP SDK FFI!") {
525 Ok(cstring) => cstring.into_raw(),
526 Err(_) => std::ptr::null_mut(),
527 }
528}
529#[cfg(feature = "php")]
531#[no_mangle]
532pub extern "C" fn helix_init() {}
533#[cfg(feature = "js")]
534#[napi(js_name = "Value")]
536#[derive(Clone)]
537pub struct JsValue {
538 inner: HlxValue,
539}
540#[cfg(feature = "js")]
541#[napi]
542impl JsValue {
543 #[napi(constructor)]
544 pub fn new(value: String) -> Result<Self> {
545 Ok(JsValue {
546 inner: HlxValue::String(value),
547 })
548 }
549 #[napi(getter)]
550 pub fn type_name(&self) -> &str {
551 match &self.inner {
552 HlxValue::String(_) => "string",
553 HlxValue::Number(_) => "number",
554 HlxValue::Bool(_) => "boolean",
555 HlxValue::Array(_) => "array",
556 HlxValue::Object(_) => "object",
557 HlxValue::Null => "null",
558 }
559 }
560 #[napi(getter)]
561 pub fn is_string(&self) -> bool {
562 matches!(& self.inner, HlxValue::String(_))
563 }
564 #[napi(getter)]
565 pub fn is_number(&self) -> bool {
566 matches!(& self.inner, HlxValue::Number(_))
567 }
568 #[napi(getter)]
569 pub fn is_boolean(&self) -> bool {
570 matches!(& self.inner, HlxValue::Bool(_))
571 }
572 #[napi(getter)]
573 pub fn is_array(&self) -> bool {
574 matches!(& self.inner, HlxValue::Array(_))
575 }
576 #[napi(getter)]
577 pub fn is_object(&self) -> bool {
578 matches!(& self.inner, HlxValue::Object(_))
579 }
580 #[napi(getter)]
581 pub fn is_null(&self) -> bool {
582 matches!(& self.inner, HlxValue::Null)
583 }
584 #[napi]
585 pub fn as_string(&self) -> Option<String> {
586 match &self.inner {
587 HlxValue::String(s) => Some(s.clone()),
588 _ => None,
589 }
590 }
591 #[napi]
592 pub fn as_number(&self) -> Option<f64> {
593 match &self.inner {
594 HlxValue::Number(n) => Some(*n),
595 _ => None,
596 }
597 }
598 #[napi]
599 pub fn as_boolean(&self) -> Option<bool> {
600 match &self.inner {
601 HlxValue::Bool(b) => Some(*b),
602 _ => None,
603 }
604 }
605 #[napi]
606 pub fn as_array(&self) -> Option<Vec<JsValue>> {
607 match &self.inner {
608 HlxValue::Array(arr) => {
609 Some(arr.iter().map(|v| JsValue { inner: v.clone() }).collect())
610 }
611 _ => None,
612 }
613 }
614 #[napi]
615 pub fn as_object(&self) -> Option<HashMap<String, JsValue>> {
616 match &self.inner {
617 HlxValue::Object(obj) => {
618 let mut result = HashMap::new();
619 for (k, v) in obj {
620 result.insert(k.clone(), JsValue { inner: v.clone() });
621 }
622 Some(result)
623 }
624 _ => None,
625 }
626 }
627 #[napi]
628 pub fn to_string(&self) -> String {
629 match &self.inner {
630 HlxValue::String(s) => s.clone(),
631 HlxValue::Number(n) => n.to_string(),
632 HlxValue::Bool(b) => b.to_string(),
633 HlxValue::Array(arr) => format!("[{}]", arr.len()),
634 HlxValue::Object(obj) => format!("{{{}}}", obj.len()),
635 HlxValue::Null => "null".to_string(),
636 }
637 }
638 #[napi]
639 pub fn to_json(&self) -> String {
640 serde_json::to_string(&self.inner).unwrap_or_else(|_| "null".to_string())
641 }
642}
643#[cfg(feature = "js")]
644#[napi(js_name = "HelixConfig")]
646pub struct JsHelixConfig {
647 config: HelixConfig,
648}
649#[cfg(feature = "js")]
650#[napi]
651impl JsHelixConfig {
652 #[napi(constructor)]
653 pub fn new() -> Self {
654 JsHelixConfig {
655 config: HelixConfig::default(),
656 }
657 }
658 #[napi]
659 pub fn get(&self, key: String) -> Option<JsValue> {
660 Some(JsValue {
661 inner: HlxValue::String(key),
662 })
663 }
664 #[napi]
665 pub fn set(&mut self, key: String, value: String) -> Result<()> {
666 Ok(())
667 }
668 #[napi]
669 pub fn keys(&self) -> Vec<String> {
670 let mut keys = Vec::new();
671 if !self.config.agents.is_empty() {
672 keys.push("agents".to_string());
673 }
674 if !self.config.workflows.is_empty() {
675 keys.push("workflows".to_string());
676 }
677 if !self.config.contexts.is_empty() {
678 keys.push("contexts".to_string());
679 }
680 if self.config.memory.is_some() {
681 keys.push("memory".to_string());
682 }
683 keys
684 }
685 #[napi]
686 pub fn has(&self, key: String) -> bool {
687 match key.as_str() {
688 "agents" => !self.config.agents.is_empty(),
689 "workflows" => !self.config.workflows.is_empty(),
690 "contexts" => !self.config.contexts.is_empty(),
691 "memory" => self.config.memory.is_some(),
692 "crews" => !self.config.crews.is_empty(),
693 _ => false,
694 }
695 }
696 #[napi]
697 pub fn size(&self) -> u32 {
698 self.keys().len() as u32
699 }
700 #[napi]
701 pub fn items(&self) -> HashMap<String, String> {
702 let mut result = HashMap::new();
703 for key in self.keys() {
704 let value = self
705 .get(key.clone())
706 .map(|v| v.to_string())
707 .unwrap_or_else(|| "null".to_string());
708 result.insert(key, value);
709 }
710 result
711 }
712 #[napi]
713 pub fn delete(&mut self, key: String) -> bool {
714 false
715 }
716 #[napi]
717 pub fn clear(&mut self) -> Result<()> {
718 self.config = HelixConfig::default();
719 Ok(())
720 }
721 #[napi]
722 pub fn to_object(&self) -> HashMap<String, JsValue> {
723 let mut result = HashMap::new();
724 result
725 .insert(
726 "agents_count".to_string(),
727 JsValue {
728 inner: HlxValue::Number(self.config.agents.len() as f64),
729 },
730 );
731 result
732 .insert(
733 "workflows_count".to_string(),
734 JsValue {
735 inner: HlxValue::Number(self.config.workflows.len() as f64),
736 },
737 );
738 result
739 .insert(
740 "contexts_count".to_string(),
741 JsValue {
742 inner: HlxValue::Number(self.config.contexts.len() as f64),
743 },
744 );
745 result
746 }
747}
748#[cfg(feature = "js")]
749#[napi]
750pub fn parse_helix_config(source: String) -> Result<JsHelixConfig> {
751 match crate::parse_and_validate(&source) {
752 Ok(config) => Ok(JsHelixConfig { config }),
753 Err(err) => Err(Error::from_reason(format!("Parse error: {}", err))),
754 }
755}
756#[cfg(feature = "js")]
757#[napi]
758pub async fn execute(
759 expression: String,
760 context: Option<HashMap<String, String>>,
761) -> Result<JsValue> {
762 let rt = tokio::runtime::Runtime::new()
763 .map_err(|e| Error::from_reason(format!("Runtime error: {}", e)))?;
764 let result = rt
765 .block_on(async {
766 let mut interpreter = crate::interpreter::HelixInterpreter::new()
767 .await
768 .map_err(|e| Error::from_reason(
769 format!("Interpreter initialization error: {}", e),
770 ))?;
771 match crate::parse(&expression) {
772 Ok(ast) => {
773 match interpreter.execute_ast(&ast).await {
774 Ok(value) => Ok(JsValue { inner: value }),
775 Err(e) => {
776 Err(Error::from_reason(format!("Execution error: {}", e)))
777 }
778 }
779 }
780 Err(parse_err) => {
781 let result_value = HlxValue::String(
782 format!("Expression result: {}", expression),
783 );
784 Ok(JsValue { inner: result_value })
785 }
786 }
787 });
788 result
789}
790#[cfg(feature = "js")]
791#[napi]
792pub fn load_helix_config(file_path: String) -> Result<JsHelixConfig> {
793 match crate::load_file(&file_path) {
794 Ok(config) => Ok(JsHelixConfig { config }),
795 Err(err) => Err(napi::Error::from_reason(format!("File load error: {}", err))),
796 }
797}
798#[cfg(feature = "js")]
799#[napi(js_name = "ExecutionContext")]
800#[derive(Clone)]
801pub struct JsExecutionContext {
802 request: Option<HashMap<String, String>>,
803 session: HashMap<String, String>,
804 cookies: HashMap<String, String>,
805 params: HashMap<String, String>,
806 query: HashMap<String, String>,
807}
808#[cfg(feature = "js")]
809#[napi]
810impl JsExecutionContext {
811 #[napi(constructor)]
812 pub fn new(
813 request: Option<HashMap<String, String>>,
814 session: Option<HashMap<String, String>>,
815 cookies: Option<HashMap<String, String>>,
816 params: Option<HashMap<String, String>>,
817 query: Option<HashMap<String, String>>,
818 ) -> Self {
819 JsExecutionContext {
820 request,
821 session: session.unwrap_or_default(),
822 cookies: cookies.unwrap_or_default(),
823 params: params.unwrap_or_default(),
824 query: query.unwrap_or_default(),
825 }
826 }
827 #[napi(getter)]
828 pub fn request(&self) -> Option<HashMap<String, String>> {
829 self.request.clone()
830 }
831 #[napi(getter)]
832 pub fn session(&self) -> HashMap<String, String> {
833 self.session.clone()
834 }
835 #[napi(getter)]
836 pub fn cookies(&self) -> HashMap<String, String> {
837 self.cookies.clone()
838 }
839 #[napi(getter)]
840 pub fn params(&self) -> HashMap<String, String> {
841 self.params.clone()
842 }
843 #[napi(getter)]
844 pub fn query(&self) -> HashMap<String, String> {
845 self.query.clone()
846 }
847}
848#[cfg(feature = "js")]
849#[napi(js_name = "OperatorRegistry")]
850pub struct JsOperatorRegistry {
851 context: Option<JsExecutionContext>,
852}
853#[cfg(feature = "js")]
854#[napi]
855impl JsOperatorRegistry {
856 #[napi(constructor)]
857 pub fn new(context: Option<&JsExecutionContext>) -> Self {
858 JsOperatorRegistry {
859 context: context.map(|c| c.clone()),
860 }
861 }
862 #[napi(getter)]
863 pub fn context(&self) -> Option<JsExecutionContext> {
864 self.context.clone()
865 }
866 #[napi]
867 pub async fn execute(&self, operator: String, params: String) -> Result<JsValue> {
868 let result = HlxValue::String(
869 format!("Operator '{}' executed with params: {}", operator, params),
870 );
871 Ok(JsValue { inner: result })
872 }
873}
874#[cfg(feature = "js")]
875#[napi(js_name = "HelixInterpreter")]
876pub struct JsHelixInterpreter {
877 interpreter: Option<crate::interpreter::HelixInterpreter>,
878}
879#[cfg(feature = "js")]
880#[napi]
881impl JsHelixInterpreter {
882 #[napi(constructor)]
883 pub fn new() -> Result<Self> {
884 Ok(JsHelixInterpreter {
885 interpreter: None,
886 })
887 }
888 #[napi]
889 pub async unsafe fn execute(&mut self, expression: String) -> Result<JsValue> {
890 if self.interpreter.is_none() {
891 let interpreter = crate::interpreter::HelixInterpreter::new()
892 .await
893 .map_err(|e| Error::from_reason(
894 format!("Interpreter initialization error: {}", e),
895 ))?;
896 self.interpreter = Some(interpreter);
897 }
898 let interpreter = self.interpreter.as_mut().unwrap();
899 match crate::parse(&expression) {
900 Ok(ast) => {
901 match interpreter.execute_ast(&ast).await {
902 Ok(value) => Ok(JsValue { inner: value }),
903 Err(e) => Err(Error::from_reason(format!("Execution error: {}", e))),
904 }
905 }
906 Err(parse_err) => {
907 let result_value = HlxValue::String(
908 format!("Expression result: {}", expression),
909 );
910 Ok(JsValue { inner: result_value })
911 }
912 }
913 }
914 #[napi]
915 pub async unsafe fn set_variable(
916 &mut self,
917 name: String,
918 value: String,
919 ) -> Result<()> {
920 if self.interpreter.is_none() {
921 let interpreter = crate::interpreter::HelixInterpreter::new()
922 .await
923 .map_err(|e| Error::from_reason(
924 format!("Interpreter initialization error: {}", e),
925 ))?;
926 self.interpreter = Some(interpreter);
927 }
928 if let Some(interpreter) = &mut self.interpreter {
929 interpreter.set_variable(name, HlxValue::String(value));
930 }
931 Ok(())
932 }
933 #[napi]
934 pub fn get_variable(&self, name: String) -> Option<JsValue> {
935 self.interpreter
936 .as_ref()?
937 .get_variable(&name)
938 .map(|v| JsValue { inner: v.clone() })
939 }
940}