1#![deny(clippy::all)]
6
7mod runtime;
8
9use napi::bindgen_prelude::*;
10use napi_derive::napi;
11use std::sync::{Arc, Mutex};
12
13use stateset_nsr::nsr::{
14 GroundedInput, NSRConfig, NSRMachine, NSRMachineBuilder,
15 Program, Primitive, SemanticValue, TrainingExample,
16};
17use stateset_nsr::nsr::machine::presets;
18
19use crate::runtime::get_runtime;
20
21#[napi(js_name = "GroundedInput")]
27pub struct JsGroundedInput {
28 inner: GroundedInput,
29}
30
31#[napi]
32impl JsGroundedInput {
33 #[napi(factory)]
35 pub fn text(s: String) -> Self {
36 Self {
37 inner: GroundedInput::Text(s),
38 }
39 }
40
41 #[napi(factory)]
43 pub fn number(n: f64) -> Self {
44 Self {
45 inner: GroundedInput::Number(n),
46 }
47 }
48
49 #[napi(factory)]
51 pub fn image(data: Vec<f64>, width: u32, height: u32, channels: Option<u32>) -> Self {
52 let data_f32: Vec<f32> = data.into_iter().map(|x| x as f32).collect();
53 Self {
54 inner: GroundedInput::ImageWithDims {
55 data: data_f32,
56 width: width as usize,
57 height: height as usize,
58 channels: channels.unwrap_or(3) as usize,
59 },
60 }
61 }
62
63 #[napi(factory)]
65 pub fn embedding(data: Vec<f64>) -> Self {
66 let data_f32: Vec<f32> = data.into_iter().map(|x| x as f32).collect();
67 Self {
68 inner: GroundedInput::Embedding(data_f32),
69 }
70 }
71
72 #[napi(factory)]
74 pub fn nil() -> Self {
75 Self {
76 inner: GroundedInput::Nil,
77 }
78 }
79
80 #[napi]
82 pub fn is_text(&self) -> bool {
83 matches!(self.inner, GroundedInput::Text(_))
84 }
85
86 #[napi]
88 pub fn is_number(&self) -> bool {
89 matches!(self.inner, GroundedInput::Number(_))
90 }
91
92 #[napi]
94 pub fn is_nil(&self) -> bool {
95 matches!(self.inner, GroundedInput::Nil)
96 }
97
98 #[napi]
100 pub fn as_text(&self) -> Option<String> {
101 if let GroundedInput::Text(s) = &self.inner {
102 Some(s.clone())
103 } else {
104 None
105 }
106 }
107
108 #[napi]
110 pub fn as_number(&self) -> Option<f64> {
111 if let GroundedInput::Number(n) = &self.inner {
112 Some(*n)
113 } else {
114 None
115 }
116 }
117
118 #[napi]
119 pub fn to_string(&self) -> String {
120 format!("{}", self.inner)
121 }
122}
123
124#[napi(js_name = "SemanticValue")]
130pub struct JsSemanticValue {
131 inner: SemanticValue,
132}
133
134#[napi]
135impl JsSemanticValue {
136 #[napi(factory)]
138 pub fn integer(n: i64) -> Self {
139 Self {
140 inner: SemanticValue::Integer(n),
141 }
142 }
143
144 #[napi(factory)]
146 pub fn float(n: f64) -> Self {
147 Self {
148 inner: SemanticValue::Float(n),
149 }
150 }
151
152 #[napi(factory)]
154 pub fn boolean(b: bool) -> Self {
155 Self {
156 inner: SemanticValue::Boolean(b),
157 }
158 }
159
160 #[napi(factory)]
162 pub fn string(s: String) -> Self {
163 Self {
164 inner: SemanticValue::String(s),
165 }
166 }
167
168 #[napi(factory)]
170 pub fn symbol(s: String) -> Self {
171 Self {
172 inner: SemanticValue::Symbol(s),
173 }
174 }
175
176 #[napi(factory)]
178 pub fn actions(actions: Vec<String>) -> Self {
179 Self {
180 inner: SemanticValue::ActionSequence(actions),
181 }
182 }
183
184 #[napi(factory)]
186 pub fn list(items: Vec<&JsSemanticValue>) -> Self {
187 Self {
188 inner: SemanticValue::List(items.into_iter().map(|v| v.inner.clone()).collect()),
189 }
190 }
191
192 #[napi(factory)]
194 pub fn null() -> Self {
195 Self {
196 inner: SemanticValue::Null,
197 }
198 }
199
200 #[napi]
202 pub fn as_integer(&self) -> Option<i64> {
203 self.inner.as_integer()
204 }
205
206 #[napi]
208 pub fn as_float(&self) -> Option<f64> {
209 self.inner.as_float()
210 }
211
212 #[napi]
214 pub fn as_string(&self) -> Option<String> {
215 self.inner.as_string().map(|s| s.to_string())
216 }
217
218 #[napi]
220 pub fn as_actions(&self) -> Option<Vec<String>> {
221 self.inner.as_actions().map(|a| a.to_vec())
222 }
223
224 #[napi]
226 pub fn is_error(&self) -> bool {
227 self.inner.is_error()
228 }
229
230 #[napi]
232 pub fn is_null(&self) -> bool {
233 matches!(self.inner, SemanticValue::Null)
234 }
235
236 #[napi]
237 pub fn to_string(&self) -> String {
238 format!("{}", self.inner)
239 }
240}
241
242#[napi(js_name = "Primitive")]
248pub struct JsPrimitive {
249 inner: Primitive,
250}
251
252#[napi]
253impl JsPrimitive {
254 #[napi(factory)]
255 pub fn add() -> Self {
256 Self { inner: Primitive::Add }
257 }
258
259 #[napi(factory)]
260 pub fn sub() -> Self {
261 Self { inner: Primitive::Sub }
262 }
263
264 #[napi(factory)]
265 pub fn mul() -> Self {
266 Self { inner: Primitive::Mul }
267 }
268
269 #[napi(factory)]
270 pub fn div() -> Self {
271 Self { inner: Primitive::Div }
272 }
273
274 #[napi(factory)]
275 pub fn eq() -> Self {
276 Self { inner: Primitive::Eq }
277 }
278
279 #[napi(factory)]
280 pub fn lt() -> Self {
281 Self { inner: Primitive::Lt }
282 }
283
284 #[napi(factory)]
285 pub fn gt() -> Self {
286 Self { inner: Primitive::Gt }
287 }
288
289 #[napi(factory)]
290 pub fn and() -> Self {
291 Self { inner: Primitive::And }
292 }
293
294 #[napi(factory)]
295 pub fn or() -> Self {
296 Self { inner: Primitive::Or }
297 }
298
299 #[napi(factory)]
300 pub fn not() -> Self {
301 Self { inner: Primitive::Not }
302 }
303
304 #[napi(factory)]
305 pub fn cons() -> Self {
306 Self { inner: Primitive::Cons }
307 }
308
309 #[napi(factory)]
310 pub fn car() -> Self {
311 Self { inner: Primitive::Car }
312 }
313
314 #[napi(factory)]
315 pub fn cdr() -> Self {
316 Self { inner: Primitive::Cdr }
317 }
318
319 #[napi(factory)]
320 pub fn identity() -> Self {
321 Self { inner: Primitive::Identity }
322 }
323
324 #[napi]
326 pub fn arity(&self) -> u32 {
327 self.inner.arity() as u32
328 }
329
330 #[napi]
332 pub fn name(&self) -> String {
333 format!("{:?}", self.inner)
334 }
335}
336
337#[napi(js_name = "Program")]
343pub struct JsProgram {
344 inner: Program,
345}
346
347#[napi]
348impl JsProgram {
349 #[napi(factory)]
351 pub fn constant(value: &JsSemanticValue) -> Self {
352 Self {
353 inner: Program::constant(value.inner.clone()),
354 }
355 }
356
357 #[napi(factory)]
359 pub fn var(index: u32) -> Self {
360 Self {
361 inner: Program::var(index as usize),
362 }
363 }
364
365 #[napi(factory)]
367 pub fn child(index: u32) -> Self {
368 Self {
369 inner: Program::child(index as usize),
370 }
371 }
372
373 #[napi(factory)]
375 pub fn primitive(prim: &JsPrimitive, args: Vec<&JsProgram>) -> Self {
376 Self {
377 inner: Program::primitive(
378 prim.inner.clone(),
379 args.into_iter().map(|p| p.inner.clone()).collect(),
380 ),
381 }
382 }
383
384 #[napi(factory)]
386 pub fn lambda(arity: u32, body: &JsProgram) -> Self {
387 Self {
388 inner: Program::lambda(arity as usize, body.inner.clone()),
389 }
390 }
391
392 #[napi(factory)]
394 pub fn apply(func: &JsProgram, args: Vec<&JsProgram>) -> Self {
395 Self {
396 inner: Program::apply(
397 func.inner.clone(),
398 args.into_iter().map(|p| p.inner.clone()).collect(),
399 ),
400 }
401 }
402
403 #[napi(factory)]
405 pub fn if_then_else(cond: &JsProgram, then_branch: &JsProgram, else_branch: &JsProgram) -> Self {
406 Self {
407 inner: Program::if_then_else(
408 cond.inner.clone(),
409 then_branch.inner.clone(),
410 else_branch.inner.clone(),
411 ),
412 }
413 }
414
415 #[napi]
417 pub fn depth(&self) -> u32 {
418 self.inner.depth() as u32
419 }
420
421 #[napi]
423 pub fn size(&self) -> u32 {
424 self.inner.size() as u32
425 }
426
427 #[napi]
429 pub fn is_constant(&self) -> bool {
430 self.inner.is_constant()
431 }
432}
433
434#[napi(object)]
440#[derive(Clone)]
441pub struct JsNSRConfig {
442 #[napi(js_name = "embeddingDim")]
443 pub embedding_dim: u32,
444 #[napi(js_name = "hiddenSize")]
445 pub hidden_size: u32,
446 #[napi(js_name = "maxSeqLen")]
447 pub max_seq_len: u32,
448 #[napi(js_name = "beamWidth")]
449 pub beam_width: u32,
450 #[napi(js_name = "enableSynthesis")]
451 pub enable_synthesis: bool,
452 #[napi(js_name = "maxProgramDepth")]
453 pub max_program_depth: u32,
454}
455
456impl Default for JsNSRConfig {
457 fn default() -> Self {
458 let config = NSRConfig::default();
459 Self {
460 embedding_dim: config.embedding_dim as u32,
461 hidden_size: config.hidden_size as u32,
462 max_seq_len: config.max_seq_len as u32,
463 beam_width: config.beam_width as u32,
464 enable_synthesis: config.enable_synthesis,
465 max_program_depth: config.max_program_depth as u32,
466 }
467 }
468}
469
470impl From<JsNSRConfig> for NSRConfig {
471 fn from(js: JsNSRConfig) -> Self {
472 NSRConfig {
473 embedding_dim: js.embedding_dim as usize,
474 hidden_size: js.hidden_size as usize,
475 max_seq_len: js.max_seq_len as usize,
476 beam_width: js.beam_width as usize,
477 enable_synthesis: js.enable_synthesis,
478 max_program_depth: js.max_program_depth as usize,
479 ..Default::default()
480 }
481 }
482}
483
484#[napi(js_name = "TrainingExample")]
490pub struct JsTrainingExample {
491 inner: TrainingExample,
492}
493
494#[napi]
495impl JsTrainingExample {
496 #[napi(constructor)]
498 pub fn new(
499 inputs: Vec<&JsGroundedInput>,
500 output: &JsSemanticValue,
501 difficulty: Option<f64>,
502 ) -> Self {
503 Self {
504 inner: TrainingExample::new(
505 inputs.into_iter().map(|i| i.inner.clone()).collect(),
506 output.inner.clone(),
507 )
508 .with_difficulty(difficulty.unwrap_or(0.0) as f32),
509 }
510 }
511
512 #[napi(factory)]
514 pub fn from_text(text: String, output: &JsSemanticValue) -> Self {
515 Self {
516 inner: TrainingExample::new(
517 vec![GroundedInput::Text(text)],
518 output.inner.clone(),
519 ),
520 }
521 }
522
523 #[napi(factory)]
525 pub fn from_tokens(tokens: Vec<String>, output: &JsSemanticValue) -> Self {
526 Self {
527 inner: TrainingExample::new(
528 tokens.into_iter().map(GroundedInput::Text).collect(),
529 output.inner.clone(),
530 ),
531 }
532 }
533
534 #[napi(getter)]
536 pub fn difficulty(&self) -> f64 {
537 self.inner.difficulty as f64
538 }
539
540 #[napi(getter)]
542 pub fn input_count(&self) -> u32 {
543 self.inner.inputs.len() as u32
544 }
545
546 #[napi]
547 pub fn to_string(&self) -> String {
548 format!(
549 "TrainingExample(inputs={}, difficulty={:.2})",
550 self.inner.inputs.len(),
551 self.inner.difficulty
552 )
553 }
554}
555
556#[napi(object)]
562pub struct JsTrainingStats {
563 #[napi(js_name = "totalExamples")]
564 pub total_examples: u32,
565 #[napi(js_name = "successfulAbductions")]
566 pub successful_abductions: u32,
567 #[napi(js_name = "trainingTimeMs")]
568 pub training_time_ms: u32,
569}
570
571#[napi(js_name = "InferenceResult")]
577pub struct JsInferenceResult {
578 output_str: Option<String>,
579 confidence_val: f64,
580 symbols_vec: Vec<u32>,
581 node_count_val: u32,
582 log_prob_val: f64,
583}
584
585#[napi]
586impl JsInferenceResult {
587 #[napi]
589 pub fn output(&self) -> Option<String> {
590 self.output_str.clone()
591 }
592
593 #[napi]
595 pub fn confidence(&self) -> f64 {
596 self.confidence_val
597 }
598
599 #[napi]
601 pub fn symbols(&self) -> Vec<u32> {
602 self.symbols_vec.clone()
603 }
604
605 #[napi]
607 pub fn node_count(&self) -> u32 {
608 self.node_count_val
609 }
610
611 #[napi]
613 pub fn log_probability(&self) -> f64 {
614 self.log_prob_val
615 }
616
617 #[napi]
618 pub fn to_string(&self) -> String {
619 format!(
620 "InferenceResult(output={:?}, confidence={:.4}, symbols={})",
621 self.output_str,
622 self.confidence_val,
623 self.symbols_vec.len()
624 )
625 }
626}
627
628#[napi(object)]
634pub struct JsEvaluationResult {
635 pub accuracy: f64,
636 pub correct: u32,
637 pub total: u32,
638}
639
640#[napi(object)]
646pub struct JsNSRStats {
647 #[napi(js_name = "trainingExamples")]
648 pub training_examples: u32,
649 #[napi(js_name = "successfulInferences")]
650 pub successful_inferences: u32,
651 #[napi(js_name = "programsLearned")]
652 pub programs_learned: u32,
653 #[napi(js_name = "vocabularySize")]
654 pub vocabulary_size: u32,
655}
656
657#[napi(js_name = "NSRMachineBuilder")]
663pub struct JsNSRMachineBuilder {
664 inner: Option<NSRMachineBuilder>,
665}
666
667#[napi]
668impl JsNSRMachineBuilder {
669 #[napi(constructor)]
670 pub fn new() -> Self {
671 Self {
672 inner: Some(NSRMachineBuilder::new()),
673 }
674 }
675
676 #[napi]
678 pub fn embedding_dim(&mut self, dim: u32) -> &Self {
679 if let Some(builder) = self.inner.take() {
680 self.inner = Some(builder.embedding_dim(dim as usize));
681 }
682 self
683 }
684
685 #[napi]
687 pub fn hidden_size(&mut self, size: u32) -> &Self {
688 if let Some(builder) = self.inner.take() {
689 self.inner = Some(builder.hidden_size(size as usize));
690 }
691 self
692 }
693
694 #[napi]
696 pub fn max_seq_len(&mut self, len: u32) -> &Self {
697 if let Some(builder) = self.inner.take() {
698 self.inner = Some(builder.max_seq_len(len as usize));
699 }
700 self
701 }
702
703 #[napi]
705 pub fn beam_width(&mut self, width: u32) -> &Self {
706 if let Some(builder) = self.inner.take() {
707 self.inner = Some(builder.beam_width(width as usize));
708 }
709 self
710 }
711
712 #[napi]
714 pub fn add_symbol(&mut self, name: String) -> &Self {
715 if let Some(builder) = self.inner.take() {
716 self.inner = Some(builder.add_symbol(name));
717 }
718 self
719 }
720
721 #[napi]
723 pub fn enable_synthesis(&mut self, enable: bool) -> &Self {
724 if let Some(builder) = self.inner.take() {
725 self.inner = Some(builder.enable_synthesis(enable));
726 }
727 self
728 }
729
730 #[napi]
732 pub fn with_explainability(&mut self) -> &Self {
733 if let Some(builder) = self.inner.take() {
734 self.inner = Some(builder.with_explainability());
735 }
736 self
737 }
738
739 #[napi]
741 pub fn build(&mut self) -> Result<JsNSRMachine> {
742 let builder = self.inner.take().ok_or_else(|| {
743 Error::from_reason("Builder already consumed")
744 })?;
745 Ok(JsNSRMachine {
746 inner: Arc::new(Mutex::new(builder.build())),
747 })
748 }
749}
750
751#[napi(js_name = "NSRMachine")]
757pub struct JsNSRMachine {
758 inner: Arc<Mutex<NSRMachine>>,
759}
760
761#[napi]
762impl JsNSRMachine {
763 #[napi(constructor)]
765 pub fn new() -> Self {
766 Self {
767 inner: Arc::new(Mutex::new(NSRMachine::default())),
768 }
769 }
770
771 #[napi(factory)]
773 pub fn with_config(config: JsNSRConfig) -> Self {
774 Self {
775 inner: Arc::new(Mutex::new(NSRMachine::with_config(config.into()))),
776 }
777 }
778
779 #[napi]
781 pub fn infer(&self, inputs: Vec<&JsGroundedInput>) -> Result<JsInferenceResult> {
782 let rust_inputs: Vec<GroundedInput> = inputs.into_iter().map(|i| i.inner.clone()).collect();
783 let machine = self.inner.clone();
784
785 let runtime = get_runtime();
786 runtime.block_on(async {
787 let mut m = machine.lock().map_err(|e| Error::from_reason(e.to_string()))?;
788 let result = m.infer(&rust_inputs)
789 .await
790 .map_err(|e| Error::from_reason(e.to_string()))?;
791
792 Ok(JsInferenceResult {
793 output_str: result.output().map(|v| format!("{}", v)),
794 confidence_val: result.confidence(),
795 symbols_vec: result.symbols().into_iter().map(|s| s as u32).collect(),
796 node_count_val: result.gss.nodes.len() as u32,
797 log_prob_val: result.gss.log_probability(),
798 })
799 })
800 }
801
802 #[napi]
804 pub fn train(&self, examples: Vec<&JsTrainingExample>) -> Result<JsTrainingStats> {
805 let rust_examples: Vec<TrainingExample> =
806 examples.into_iter().map(|e| e.inner.clone()).collect();
807 let machine = self.inner.clone();
808 let total = rust_examples.len() as u32;
809
810 let runtime = get_runtime();
811 let start = std::time::Instant::now();
812
813 runtime.block_on(async {
814 let mut m = machine.lock().map_err(|e| Error::from_reason(e.to_string()))?;
815 let stats = m.train(&rust_examples)
816 .await
817 .map_err(|e| Error::from_reason(e.to_string()))?;
818
819 Ok(JsTrainingStats {
820 total_examples: total,
821 successful_abductions: stats.successful_abductions as u32,
822 training_time_ms: start.elapsed().as_millis() as u32,
823 })
824 })
825 }
826
827 #[napi]
829 pub fn evaluate(&self, examples: Vec<&JsTrainingExample>) -> Result<JsEvaluationResult> {
830 let rust_examples: Vec<TrainingExample> =
831 examples.into_iter().map(|e| e.inner.clone()).collect();
832 let machine = self.inner.clone();
833 let total = rust_examples.len() as u32;
834
835 let runtime = get_runtime();
836 runtime.block_on(async {
837 let mut m = machine.lock().map_err(|e| Error::from_reason(e.to_string()))?;
838 let mut correct = 0u32;
839
840 for example in &rust_examples {
841 if let Ok(result) = m.infer(&example.inputs).await {
842 if let Some(output) = result.output() {
843 if output == &example.output {
844 correct += 1;
845 }
846 }
847 }
848 }
849
850 Ok(JsEvaluationResult {
851 accuracy: if total > 0 { correct as f64 / total as f64 } else { 0.0 },
852 correct,
853 total,
854 })
855 })
856 }
857
858 #[napi]
860 pub fn add_symbol(&self, name: String) -> Result<u32> {
861 let mut m = self.inner.lock().map_err(|e| Error::from_reason(e.to_string()))?;
862 Ok(m.add_symbol(&name) as u32)
863 }
864
865 #[napi]
867 pub fn add_symbols(&self, names: Vec<String>) -> Result<Vec<u32>> {
868 let mut m = self.inner.lock().map_err(|e| Error::from_reason(e.to_string()))?;
869 Ok(names.iter().map(|n| m.add_symbol(n) as u32).collect())
870 }
871
872 #[napi]
874 pub fn get_symbol_name(&self, symbol_id: u32) -> Result<Option<String>> {
875 let m = self.inner.lock().map_err(|e| Error::from_reason(e.to_string()))?;
876 Ok(m.vocabulary().get_name(symbol_id as usize).map(|s| s.to_string()))
877 }
878
879 #[napi]
881 pub fn get_symbol_id(&self, name: String) -> Result<Option<u32>> {
882 let m = self.inner.lock().map_err(|e| Error::from_reason(e.to_string()))?;
883 Ok(m.vocabulary().get_by_name(&name).map(|id| id as u32))
884 }
885
886 #[napi]
888 pub fn get_all_symbols(&self) -> Result<Vec<String>> {
889 let m = self.inner.lock().map_err(|e| Error::from_reason(e.to_string()))?;
890 let vocab = m.vocabulary();
891 let mut names = Vec::new();
892 for i in 0..vocab.len() {
893 if let Some(name) = vocab.get_name(i) {
894 names.push(name.to_string());
895 }
896 }
897 Ok(names)
898 }
899
900 #[napi(getter)]
902 pub fn vocabulary_size(&self) -> Result<u32> {
903 let m = self.inner.lock().map_err(|e| Error::from_reason(e.to_string()))?;
904 Ok(m.vocabulary().len() as u32)
905 }
906
907 #[napi(getter)]
909 pub fn statistics(&self) -> Result<JsNSRStats> {
910 let m = self.inner.lock().map_err(|e| Error::from_reason(e.to_string()))?;
911 let stats = m.statistics();
912 Ok(JsNSRStats {
913 training_examples: stats.training_examples as u32,
914 successful_inferences: stats.successful_inferences as u32,
915 programs_learned: stats.programs_learned as u32,
916 vocabulary_size: stats.vocabulary_size as u32,
917 })
918 }
919
920 #[napi(getter)]
922 pub fn config(&self) -> Result<JsNSRConfig> {
923 let m = self.inner.lock().map_err(|e| Error::from_reason(e.to_string()))?;
924 let cfg = m.config();
925 Ok(JsNSRConfig {
926 embedding_dim: cfg.embedding_dim as u32,
927 hidden_size: cfg.hidden_size as u32,
928 max_seq_len: cfg.max_seq_len as u32,
929 beam_width: cfg.beam_width as u32,
930 enable_synthesis: cfg.enable_synthesis,
931 max_program_depth: cfg.max_program_depth as u32,
932 })
933 }
934
935 #[napi]
938 pub fn set_program(&self, symbol_id: u32, program: &JsProgram) -> Result<()> {
939 let mut m = self.inner.lock().map_err(|e| Error::from_reason(e.to_string()))?;
940 m.set_symbol_program(symbol_id as usize, program.inner.clone());
941 Ok(())
942 }
943
944 #[napi]
947 pub fn set_constant_program(&self, symbol_id: u32, value: &JsSemanticValue) -> Result<()> {
948 let mut m = self.inner.lock().map_err(|e| Error::from_reason(e.to_string()))?;
949 m.set_symbol_program(symbol_id as usize, Program::constant(value.inner.clone()));
950 Ok(())
951 }
952
953 #[napi]
957 pub fn setup_classification_programs(&self) -> Result<()> {
958 let mut m = self.inner.lock().map_err(|e| Error::from_reason(e.to_string()))?;
959 let vocab_len = m.vocabulary().len();
960 for symbol_id in 0..vocab_len {
961 if let Some(name) = m.vocabulary().get_name(symbol_id) {
962 let program = Program::constant(SemanticValue::Symbol(name.to_string()));
963 m.set_symbol_program(symbol_id, program);
964 }
965 }
966 Ok(())
967 }
968
969 #[napi]
971 pub fn get_program(&self, symbol_id: u32) -> Result<Option<JsProgram>> {
972 let m = self.inner.lock().map_err(|e| Error::from_reason(e.to_string()))?;
973 Ok(m.get_symbol_program(symbol_id as usize).map(|p| JsProgram {
974 inner: p.clone(),
975 }))
976 }
977}
978
979#[napi]
985pub fn scan_machine() -> JsNSRMachine {
986 JsNSRMachine {
987 inner: Arc::new(Mutex::new(presets::scan_machine())),
988 }
989}
990
991#[napi]
993pub fn pcfg_machine() -> JsNSRMachine {
994 JsNSRMachine {
995 inner: Arc::new(Mutex::new(presets::pcfg_machine())),
996 }
997}
998
999#[napi]
1001pub fn hint_machine() -> JsNSRMachine {
1002 JsNSRMachine {
1003 inner: Arc::new(Mutex::new(presets::hint_machine())),
1004 }
1005}
1006
1007#[napi]
1009pub fn cogs_machine() -> JsNSRMachine {
1010 JsNSRMachine {
1011 inner: Arc::new(Mutex::new(presets::cogs_machine())),
1012 }
1013}
1014
1015#[napi]
1017pub fn version() -> String {
1018 env!("CARGO_PKG_VERSION").to_string()
1019}