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