1use std::collections::HashMap;
2use std::path::{Path, PathBuf};
3use crate::hel::error::HlxError;
4use crate::dna::atp::value::Value;
5use crate::hel::dispatch::{HelixDispatcher, DispatchResult};
6use crate::HelixConfig;
7use crate::ops::engine::OperatorEngine;
8
9pub trait IntoValue {
11 fn into_value(self) -> Value;
12}
13
14impl IntoValue for Value {
15 fn into_value(self) -> Value {
16 self
17 }
18}
19
20impl IntoValue for &str {
21 fn into_value(self) -> Value {
22 Value::String(self.to_string())
23 }
24}
25
26impl IntoValue for String {
27 fn into_value(self) -> Value {
28 Value::String(self)
29 }
30}
31
32impl IntoValue for &String {
33 fn into_value(self) -> Value {
34 Value::String(self.clone())
35 }
36}
37
38impl IntoValue for bool {
39 fn into_value(self) -> Value {
40 Value::Bool(self)
41 }
42}
43
44impl IntoValue for i8 {
45 fn into_value(self) -> Value {
46 Value::Number(self as f64)
47 }
48}
49
50impl IntoValue for i16 {
51 fn into_value(self) -> Value {
52 Value::Number(self as f64)
53 }
54}
55
56impl IntoValue for i32 {
57 fn into_value(self) -> Value {
58 Value::Number(self as f64)
59 }
60}
61
62impl IntoValue for i64 {
63 fn into_value(self) -> Value {
64 Value::Number(self as f64)
65 }
66}
67
68impl IntoValue for u8 {
69 fn into_value(self) -> Value {
70 Value::Number(self as f64)
71 }
72}
73
74impl IntoValue for u16 {
75 fn into_value(self) -> Value {
76 Value::Number(self as f64)
77 }
78}
79
80impl IntoValue for u32 {
81 fn into_value(self) -> Value {
82 Value::Number(self as f64)
83 }
84}
85
86impl IntoValue for u64 {
87 fn into_value(self) -> Value {
88 Value::Number(self as f64)
89 }
90}
91
92impl IntoValue for f32 {
93 fn into_value(self) -> Value {
94 Value::Number(self as f64)
95 }
96}
97
98impl IntoValue for f64 {
99 fn into_value(self) -> Value {
100 Value::Number(self)
101 }
102}
103
104impl<T: IntoValue> IntoValue for Vec<T> {
106 fn into_value(self) -> Value {
107 Value::Array(self.into_iter().map(|v| v.into_value()).collect())
108 }
109}
110
111pub struct Hlx {
112 pub config: Option<HelixConfig>,
113 pub data: HashMap<String, HashMap<String, Value>>,
114 pub file_path: Option<PathBuf>,
115 pub dispatcher: HelixDispatcher,
116 pub operator_engine: OperatorEngine,
117}
118impl Hlx {
119 pub async fn load<P: AsRef<Path>>(path: P) -> Result<Self, HlxError> {
120 let path = path.as_ref().to_path_buf();
121 let mut hlx = Self {
122 config: None,
123 data: HashMap::new(),
124 file_path: Some(path.clone()),
125 dispatcher: HelixDispatcher::new(),
126 operator_engine: OperatorEngine::new().await?,
127 };
128 hlx.dispatcher.initialize().await?;
129 if path.extension().and_then(|s| s.to_str()) == Some("hlxb") {
130 #[cfg(feature = "compiler")]
131 {
132 let loader = crate::mds::loader::BinaryLoader::new();
133 let binary = loader
134 .load_file(&path)
135 .map_err(|e| HlxError::compilation_error(
136 format!("Failed to load binary: {:?}", e),
137 "Ensure file is a valid HLXB file",
138 ))?;
139 hlx.config = Some(crate::HelixConfig::default());
141 }
142 #[cfg(not(feature = "compiler"))]
143 {
144 return Err(
145 HlxError::compilation_error(
146 "Binary file support not available",
147 "Compile with 'compiler' feature enabled",
148 ),
149 );
150 }
151 } else {
152 let content = std::fs::read_to_string(&path)
153 .map_err(|e| HlxError::io_error(
154 format!("Failed to read file: {}", e),
155 "Ensure file exists and is readable",
156 ))?;
157 match hlx.dispatcher.parse_and_execute(&content).await? {
158 DispatchResult::Executed(value) => {
159 if let Value::Object(obj) = value {
160 for (section, section_data) in obj {
161 if let Value::Object(section_obj) = section_data {
162 let mut section_map = HashMap::new();
163 for (key, val) in section_obj {
164 section_map.insert(key, val);
165 }
166 hlx.data.insert(section, section_map);
167 }
168 }
169 }
170 }
171 DispatchResult::Parsed(ast) => {
172 hlx.config = Some(
173 crate::ast_to_config(ast)
174 .map_err(|e| HlxError::config_conversion(
175 "conversion".to_string(),
176 e,
177 ))?,
178 );
179 }
180 _ => {}
181 }
182 }
183 Ok(hlx)
184 }
185 pub async fn new() -> Result<Self, HlxError> {
186 Ok(Self {
187 config: None,
188 data: HashMap::new(),
189 file_path: None,
190 dispatcher: HelixDispatcher::new(),
191 operator_engine: OperatorEngine::new().await?,
192 })
193 }
194 pub fn get(&self, section: &str, key: &str) -> Option<&Value> {
195 self.data.get(section)?.get(key)
196 }
197 pub fn set<T: IntoValue>(&mut self, section: &str, key: &str, value: T) {
219 self.data
220 .entry(section.to_string())
221 .or_insert_with(HashMap::new)
222 .insert(key.to_string(), value.into_value());
223 }
224
225 pub fn set_str(&mut self, section: &str, key: &str, value: &str) {
227 self.set(section, key, value);
228 }
229
230 pub fn set_num(&mut self, section: &str, key: &str, value: f64) {
231 self.set(section, key, value);
232 }
233
234 pub fn set_bool(&mut self, section: &str, key: &str, value: bool) {
235 self.set(section, key, value);
236 }
237
238 pub fn increase(&mut self, section: &str, key: &str, amount: f64) -> Result<f64, HlxError> {
242 let current_value = self.get(section, key)
243 .and_then(|v| v.as_number())
244 .unwrap_or(0.0);
245
246 let new_value = current_value + amount;
247
248 self.set(section, key, Value::Number(new_value));
249 Ok(new_value)
250 }
251 pub fn index(&self, section: &str) -> Option<&HashMap<String, Value>> {
252 self.data.get(section)
253 }
254 pub fn index_mut(&mut self, section: &str) -> Option<&mut HashMap<String, Value>> {
255 self.data.get_mut(section)
256 }
257 pub async fn server(&mut self) -> Result<(), HlxError> {
258 if self.dispatcher.is_ready() {
259 Ok(())
260 } else {
261 self.dispatcher.initialize().await
262 }
263 }
264 pub async fn watch(&mut self) -> Result<(), HlxError> {
265 #[cfg(feature = "compiler")]
266 {
267 if let Some(path) = &self.file_path {
268 println!("Watching {} for changes...", path.display());
269 Ok(())
270 } else {
271 Err(
272 HlxError::invalid_input(
273 "No file loaded for watching",
274 "Load a file first with Hlx::load()",
275 ),
276 )
277 }
278 }
279 #[cfg(not(feature = "compiler"))]
280 {
281 Err(
282 HlxError::compilation_error(
283 "Watch mode not available",
284 "Compile with 'compiler' feature enabled",
285 ),
286 )
287 }
288 }
289 pub async fn process(&mut self) -> Result<(), HlxError> {
290 if let Some(path) = &self.file_path {
291 let content = std::fs::read_to_string(path)
292 .map_err(|e| HlxError::io_error(
293 format!("Failed to read file: {}", e),
294 "Ensure file exists and is readable",
295 ))?;
296 match self.dispatcher.parse_and_execute(&content).await? {
297 DispatchResult::Executed(value) => {
298 println!("Processed successfully: {:?}", value);
299 Ok(())
300 }
301 _ => Ok(()),
302 }
303 } else {
304 Err(
305 HlxError::invalid_input(
306 "No file loaded for processing",
307 "Load a file first with Hlx::load()",
308 ),
309 )
310 }
311 }
312 pub async fn compile(&mut self) -> Result<(), HlxError> {
313 #[cfg(feature = "compiler")]
314 {
315 if let Some(path) = &self.file_path {
316 use crate::dna::compiler::{Compiler, OptimizationLevel};
317 let compiler = Compiler::builder()
318 .optimization_level(OptimizationLevel::Two)
319 .compression(true)
320 .cache(true)
321 .verbose(false)
322 .build();
323 let binary = compiler
324 .compile_file(path)
325 .map_err(|e| HlxError::compilation_error(
326 format!("Compilation failed: {}", e),
327 "Check file syntax and try again",
328 ))?;
329 let binary_path = path.with_extension("hlxb");
330 let serializer = crate::mds::serializer::BinarySerializer::new(true);
331 serializer
332 .write_to_file(&binary, &binary_path)
333 .map_err(|e| HlxError::io_error(
334 format!("Failed to write binary file: {}", e),
335 "Ensure output directory is writable",
336 ))?;
337 println!(
338 "✅ Successfully compiled {} to {}", path.display(), binary_path
339 .display()
340 );
341 Ok(())
342 } else {
343 Err(
344 HlxError::invalid_input(
345 "No file loaded for compilation",
346 "Load a file first with Hlx::load()",
347 ),
348 )
349 }
350 }
351 #[cfg(not(feature = "compiler"))]
352 {
353 Err(
354 HlxError::compilation_error(
355 "Compilation not available",
356 "Compile with 'compiler' feature enabled",
357 ),
358 )
359 }
360 }
361 pub async fn execute(&mut self, code: &str) -> Result<Value, HlxError> {
362 if !self.dispatcher.is_ready() {
363 self.dispatcher.initialize().await?;
364 }
365 match self.dispatcher.parse_and_execute(code).await {
366 Ok(DispatchResult::Executed(value)) => Ok(value),
367 Ok(DispatchResult::ParseError(err)) => {
368 Err(
369 HlxError::invalid_input(
370 format!("Parse error: {}", err),
371 "Check syntax",
372 ),
373 )
374 }
375 Ok(DispatchResult::ExecutionError(err)) => Err(err),
376 Ok(DispatchResult::Parsed(_)) => {
377 Err(
378 HlxError::execution_error(
379 "Parsed but not executed",
380 "Use process() for file processing",
381 ),
382 )
383 }
384 Err(e) => Err(e),
385 }
386 }
387 pub async fn execute_operator(
388 &self,
389 operator: &str,
390 params: &str,
391 ) -> Result<Value, HlxError> {
392 self.operator_engine.execute_operator(operator, params).await
393 }
394 pub fn sections(&self) -> Vec<&String> {
395 self.data.keys().collect()
396 }
397 pub fn keys(&self, section: &str) -> Option<Vec<&String>> {
398 self.data.get(section).map(|s| s.keys().collect())
399 }
400 pub fn save(&self) -> Result<(), HlxError> {
401 if let Some(path) = &self.file_path {
402 let mut content = String::new();
403
404 for (section, keys) in &self.data {
406 content.push_str(&format!("{} :\n", section));
408
409 for (key, value) in keys {
410 let formatted_value = match value {
412 Value::String(s) => format!("\"{}\"", s),
413 Value::Number(n) => n.to_string(),
414 Value::Bool(b) => b.to_string(),
415 Value::Array(arr) => {
416 let items: Vec<String> = arr.iter().map(|v| {
417 match v {
418 Value::String(s) => format!("\"{}\"", s),
419 Value::Number(n) => n.to_string(),
420 Value::Bool(b) => b.to_string(),
421 _ => format!("{}", v),
422 }
423 }).collect();
424 format!("[{}]", items.join(", "))
425 },
426 Value::Object(obj) => {
427 let pairs: Vec<String> = obj.iter().map(|(k, v)| {
428 format!("{} = {}", k, v)
429 }).collect();
430 format!("{{\n {}\n }}", pairs.join("\n "))
431 },
432 _ => format!("{}", value),
433 };
434
435 content.push_str(&format!(" {} = {}\n", key, formatted_value));
436 }
437
438 content.push_str(";\n\n");
439 }
440
441 std::fs::write(path, content)
442 .map_err(|e| HlxError::io_error(
443 format!("Failed to save file: {}", e),
444 "Ensure write permissions",
445 ))
446 } else {
447 Err(
448 HlxError::invalid_input(
449 "No file path set",
450 "Load a file first or set file_path manually",
451 ),
452 )
453 }
454 }
455
456 pub fn make(&self) -> Result<String, HlxError> {
467 let mut content = String::new();
468
469 for (section, keys) in &self.data {
471 content.push_str(&format!("{} :\n", section));
473
474 for (key, value) in keys {
475 let formatted_value = match value {
477 Value::String(s) => format!("\"{}\"", s),
478 Value::Number(n) => n.to_string(),
479 Value::Bool(b) => b.to_string(),
480 Value::Array(arr) => {
481 let items: Vec<String> = arr.iter().map(|v| {
482 match v {
483 Value::String(s) => format!("\"{}\"", s),
484 Value::Number(n) => n.to_string(),
485 Value::Bool(b) => b.to_string(),
486 _ => format!("{}", v),
487 }
488 }).collect();
489 format!("[{}]", items.join(", "))
490 },
491 Value::Object(obj) => {
492 let pairs: Vec<String> = obj.iter().map(|(k, v)| {
493 format!("{} = {}", k, v)
494 }).collect();
495 format!("{{\n {}\n }}", pairs.join("\n "))
496 },
497 _ => format!("{}", value),
498 };
499
500 content.push_str(&format!(" {} = {}\n", key, formatted_value));
501 }
502
503 content.push_str(";\n\n");
504 }
505
506 Ok(content)
507 }
508}
509impl std::ops::Index<&str> for Hlx {
510 type Output = HashMap<String, Value>;
511 fn index(&self, section: &str) -> &Self::Output {
512 self.data
513 .get(section)
514 .unwrap_or_else(|| panic!("Section '{}' not found", section))
515 }
516}
517impl std::ops::IndexMut<&str> for Hlx {
518 fn index_mut(&mut self, section: &str) -> &mut Self::Output {
519 self.data.entry(section.to_string()).or_insert_with(HashMap::new)
520 }
521}
522pub mod test_operators {
523 use super::*;
524 pub async fn test_fundamental_operators() -> Result<(), HlxError> {
525 let mut hlx = Hlx::new().await?;
526 println!("Testing fundamental operators...");
527 let result = hlx.execute(r#"@var(name="test_var", value="hello")"#).await?;
528 println!("@var result: {:?}", result);
529 let result = hlx.execute(r#"@env(key="HOME")"#).await?;
530 println!("@env result: {:?}", result);
531 let result = hlx.execute(r#"@date("Y-m-d")"#).await?;
532 println!("@date result: {:?}", result);
533 let result = hlx.execute(r#"@time("H:i:s")"#).await?;
534 println!("@time result: {:?}", result);
535 let result = hlx.execute("@uuid()").await?;
536 println!("@uuid result: {:?}", result);
537 let result = hlx.execute(r#"@string("hello world", "upper")"#).await?;
538 println!("@string result: {:?}", result);
539 let result = hlx.execute(r#"@math("5 + 3")"#).await?;
540 println!("@math result: {:?}", result);
541 let result = hlx.execute(r#"@calc("a = 10; b = 5; a + b")"#).await?;
542 println!("@calc result: {:?}", result);
543 let result = hlx
544 .execute(r#"@if(condition="true", then="yes", else="no")"#)
545 .await?;
546 println!("@if result: {:?}", result);
547 let result = hlx
548 .execute(r#"@array(values="[1,2,3]", operation="length")"#)
549 .await?;
550 println!("@array result: {:?}", result);
551 let result = hlx.execute(r#"@json('{"name":"test"}', "parse")"#).await?;
552 println!("@json result: {:?}", result);
553 let result = hlx.execute(r#"@base64("hello", "encode")"#).await?;
554 println!("@base64 result: {:?}", result);
555 let result = hlx.execute(r#"@hash("password", "sha256")"#).await?;
556 println!("@hash result: {:?}", result);
557 println!("All fundamental operators tested successfully!");
558 Ok(())
559 }
560 pub async fn test_conditional_operators() -> Result<(), HlxError> {
561 let mut hlx = Hlx::new().await?;
562 println!("Testing conditional operators...");
563 let result = hlx
564 .execute(r#"@if(condition="@math('5 > 3')", then="greater", else="less")"#)
565 .await?;
566 println!("@if with expression: {:?}", result);
567 let result = hlx
568 .execute(
569 r#"@switch(value="2", cases="{'1':'one','2':'two','3':'three'}", default="unknown")"#,
570 )
571 .await?;
572 println!("@switch result: {:?}", result);
573 let result = hlx
574 .execute(r#"@filter(array="[1,2,3,4,5]", condition="@math('value > 3')")"#)
575 .await?;
576 println!("@filter result: {:?}", result);
577 let result = hlx
578 .execute(r#"@map(array="[1,2,3]", transform="@math('value * 2')")"#)
579 .await?;
580 println!("@map result: {:?}", result);
581 let result = hlx
582 .execute(
583 r#"@reduce(array="[1,2,3,4]", initial="0", operation="@math('acc + value')")"#,
584 )
585 .await?;
586 println!("@reduce result: {:?}", result);
587 println!("All conditional operators tested successfully!");
588 Ok(())
589 }
590}
591#[cfg(test)]
592mod tests {
593 use super::*;
594 #[tokio::test]
595 async fn test_hlx_interface() {
596 let mut hlx = Hlx::new().await.unwrap();
597 hlx.data.insert("database".to_string(), HashMap::new());
598 hlx.index_mut("database")
599 .unwrap()
600 .insert(
601 "host".to_string(),
602 crate::dna::atp::value::Value::String("localhost".to_string()),
603 );
604 hlx.index_mut("database")
605 .unwrap()
606 .insert("port".to_string(), crate::dna::atp::value::Value::Number(5432.0));
607 assert_eq!(
608 hlx.get("database", "host"), Some(& crate::dna::atp::value::Value::String("localhost"
609 .to_string()))
610 );
611 assert_eq!(hlx.get("database", "port"), Some(& Value::Number(5432.0)));
612 let sections = hlx.sections();
613 assert!(sections.iter().any(| s | * s == "database"));
614 let keys = hlx.keys("database").unwrap();
615 assert!(keys.iter().any(| k | * k == "host"));
616 }
617 #[tokio::test]
618 async fn test_operator_execution() {
619 let hlx = Hlx::new().await.unwrap();
620 let result = hlx.execute_operator("date", "{\"format\":\"Y-m-d\"}").await;
621 println!("Direct operator execution result: {:?}", result);
622 assert!(result.is_ok());
623 let result = hlx.execute_operator("uuid", "").await;
624 println!("UUID operator execution result: {:?}", result);
625 assert!(result.is_ok());
626 let result = hlx.execute_operator("nonexistent", "{}").await;
627 println!("Invalid operator result: {:?}", result);
628 assert!(result.is_err());
629 }
630 #[tokio::test]
631 async fn test_operator_integration() {
632 use crate::ops::OperatorParser;
633 let mut ops_parser = OperatorParser::new().await;
634 let result = ops_parser.parse_value("@date(\"Y-m-d\")").await.unwrap();
635 match result {
636 crate::dna::atp::value::Value::String(date_str) => {
637 assert!(! date_str.is_empty());
638 println!("✅ @date operator working: {}", date_str);
639 }
640 _ => panic!("Expected string result from @date"),
641 }
642 let result = ops_parser.parse_value("@uuid()").await.unwrap();
643 match result {
644 crate::dna::atp::value::Value::String(uuid_str) => {
645 assert!(! uuid_str.is_empty());
646 println!(
647 "✅ @uuid operator working: {} (length: {})", uuid_str, uuid_str
648 .len()
649 );
650 }
651 _ => panic!("Expected string result from @uuid"),
652 }
653 use dna::ops::OperatorEngine;
654 let operator_engine = OperatorEngine::new().await.unwrap();
655 let result = operator_engine
656 .execute_operator("date", "{\"format\":\"%Y-%m-%d\"}")
657 .await
658 .unwrap();
659 match result {
660 crate::dna::atp::value::Value::String(date_str) => {
661 assert!(! date_str.is_empty());
662 println!("✅ Direct date operator working: {}", date_str);
663 }
664 _ => panic!("Expected string result from direct date operator"),
665 }
666 let result = operator_engine.execute_operator("uuid", "").await.unwrap();
667 match result {
668 crate::dna::atp::value::Value::String(uuid_str) => {
669 assert!(! uuid_str.is_empty());
670 println!(
671 "✅ Direct uuid operator working: {} (length: {})", uuid_str,
672 uuid_str.len()
673 );
674 }
675 _ => panic!("Expected string result from direct uuid operator"),
676 }
677 println!("✅ ops.rs and operators/ integration fully working!");
678 }
679 #[tokio::test]
680 async fn test_comprehensive_operator_testing() {
681 assert!(true);
682 }
683}