1#![doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/README.md"))]
2
3use hex;
4use libc;
5use log::{info, debug};
6use std::collections::HashMap;
7use std::sync::Mutex;
8use once_cell::sync::Lazy;
9pub use crate::config::Config;
10pub use crate::persistor::{Word, SIZE};
11use crate::persistor::{PERSISTOR, MemoryPersistor, Persistor, PersistorAccessError};
12use crate::evaluator::{Evaluator, Primitive, Type};
13use std::thread;
14
15use sha2::{Sha256, Digest};
16use evaluator as s7;
17use std::ffi::{CString, CStr};
18
19mod config;
20mod evaluator;
21mod persistor;
22
23pub static JOURNAL: Lazy<Journal> = Lazy::new(|| Journal::new());
24
25const SYNC_PAIR_TAG: i64 = 0;
26
27const GENESIS_STR: &str = "(lambda (*sync-state* query) (cons (eval query) *sync-state*))";
28
29pub const NULL: Word = [
30 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
31];
32
33struct Session {
34 record: Word,
35 persistor: MemoryPersistor,
36}
37
38impl Session {
39 fn new(record: Word, persistor: MemoryPersistor) -> Self{
40 Self { record, persistor }
41 }
42}
43
44static SESSIONS: Lazy<Mutex<HashMap<usize, Session>>> = Lazy::new(|| {
45 Mutex::new(HashMap::new())
46});
47
48#[derive(Debug)]
49pub struct JournalAccessError(pub Word);
50
51static LOCK: Mutex<()> = Mutex::new(());
52
53pub struct Journal {}
74
75impl Journal {
76 fn new() -> Self {
77 match PERSISTOR.root_new(
78 NULL,
79 PERSISTOR.branch_set(
80 PERSISTOR.leaf_set(GENESIS_STR.as_bytes().to_vec()).unwrap(),
81 NULL,
82 ).unwrap(),
83 ) {
84 Ok(_) => Self {},
85 Err(_) => panic!("Journal already instantiated"),
86 }
87 }
88
89 pub fn evaluate(&self, query: &str) -> String {
105 self.evaluate_record(NULL, query)
106 }
107
108 fn evaluate_record(&self, record: Word, query: &str) -> String {
109 loop {
110 let genesis_func = PERSISTOR.leaf_get(
111 PERSISTOR.branch_get(
112 PERSISTOR.root_get(record).unwrap()
113 ).unwrap().0
114 ).unwrap().to_vec();
115
116 let genesis_str = String::from_utf8_lossy(&genesis_func);
117
118 let state_old = PERSISTOR.root_get(record).unwrap();
119
120 let record_temp = PERSISTOR.root_temp(state_old).unwrap();
121
122 let state_str = format!(
123 "#u({})",
124 state_old.iter().map(|&num| num.to_string()).collect::<Vec<String>>().join(" "),
125 );
126
127 let evaluator = Evaluator::new(
128 vec![
129 (SYNC_PAIR_TAG, type_s7_sync_pair()),
130 ].into_iter().collect(),
131 vec![
132 primitive_s7_sync_hash(),
133 primitive_s7_sync_pair(),
134 primitive_s7_sync_is_pair(),
135 primitive_s7_sync_null(),
136 primitive_s7_sync_is_null(),
137 primitive_s7_sync_pair_to_bytes(),
138 primitive_s7_sync_cons(),
139 primitive_s7_sync_car(),
140 primitive_s7_sync_cdr(),
141 primitive_s7_sync_create(),
142 primitive_s7_sync_delete(),
143 primitive_s7_sync_all(),
144 primitive_s7_sync_call(),
145 primitive_s7_sync_remote(),
146 primitive_s7_sync_http(),
147 ],
148 );
149
150 SESSIONS.lock().unwrap().insert(
151 evaluator.sc as usize,
152 Session::new(record, MemoryPersistor::new()),
153 );
154
155 let expr = format!("((eval {}) (sync-pair {}) (quote {}))", genesis_str, state_str, query);
156
157 debug!("Evaluating expression: {}", expr.as_str());
158
159 let result = evaluator.evaluate(expr.as_str());
160
161 let persistor = {
162 let mut session = SESSIONS.lock().unwrap();
163 &session.remove(&(evaluator.sc as usize)).unwrap().persistor.clone()
164 };
165
166 let (output, state_new) = match result.starts_with("(error '") {
167 true => (result, state_old),
168 false => {
169 match result.rfind('.') {
170 Some(index) => match *&result[(index + 16)..(result.len() - 3)]
171 .split(' ').collect::<Vec<&str>>()
172 .iter().map(|x| x.parse::<u8>().unwrap()).collect::<Vec<u8>>()
173 .try_into() {
174 Ok(state_new) => (
175 String::from(&result[1..(index - 1)]),
176 state_new,
177 ),
178 Err(_) => (
179 String::from("(error 'sync-format \"Invalid return format\")"),
180 state_old,
181 )
182 },
183 None => (
184 String::from("(error 'sync-format \"Invalid return format\")"),
185 state_old,
186 ),
187 }
188 },
189 };
190
191 match state_old == state_new {
192 true => {
193 PERSISTOR.root_delete(record_temp).unwrap();
194 return output
195 },
196 false => match state_old == PERSISTOR.root_get(record).unwrap() {
197 true => {
198 fn recurse(source: &MemoryPersistor, node: Word) -> Result<(), PersistorAccessError> {
199 if node == NULL {
200 Ok(())
201 } else if let Ok(_) = PERSISTOR.leaf_get(node) {
202 Ok(())
203 } else if let Ok(_) = PERSISTOR.branch_get(node) {
204 Ok(())
205 } else if let Ok(content) = source.leaf_get(node) {
206 PERSISTOR.leaf_set(content).unwrap();
207 Ok(())
208 } else if let Ok((left, right)) = source.branch_get(node) {
209 PERSISTOR.branch_set(left, right).unwrap();
210 match recurse(&source, left) {
211 Ok(_) => recurse(&source, right),
212 err => err,
213 }
214 } else {
215 Err(PersistorAccessError(format!("Dangling branch {:?}", node)))
216 }
217 }
218
219 {
220 let lock = LOCK.lock().unwrap();
221 match recurse(&persistor, state_new) {
222 Ok(_) => {
223 match PERSISTOR.root_set(record, state_old, state_new) {
224 Ok(_) => {
225 PERSISTOR.root_delete(record_temp).unwrap();
226 return output
227 },
228 Err(_) => {
229 info!("Rerunning query due to concurrency collision: {}", query);
230 drop(lock);
231 continue
232 }
233 }
234 },
235 Err(err) => {
236 panic!("{:?}", err);
237 }
238 }
239 }
240 },
241 false => {
242 info!("Rerunning query due to concurrency collision: {}", query);
243 continue
244 }
245 }
246 }
247 }
248 }
249}
250
251unsafe fn sync_error(sc: *mut s7::s7_scheme) -> s7::s7_pointer {
252 s7::s7_error(
253 sc,
254 s7::s7_make_symbol(sc, c"sync-web-error".as_ptr()),
255 s7::s7_list(sc, 1, s7::s7_make_string(
256 sc,
257 c"journal encountered unexpected error".as_ptr(),
258 )),
259 )
260}
261
262fn type_s7_sync_pair() -> Type {
263 unsafe extern "C" fn free(_sc: *mut s7::s7_scheme, obj: s7::s7_pointer) -> s7::s7_pointer {
264 sync_heap_free(s7::s7_c_object_value(obj));
265 std::ptr::null_mut()
266 }
267
268 unsafe extern "C" fn mark(_sc: *mut s7::s7_scheme, _obj: s7::s7_pointer) -> s7::s7_pointer {
269 std::ptr::null_mut()
270 }
271
272 unsafe extern "C" fn is_equal(sc: *mut s7::s7_scheme, args: s7::s7_pointer) -> s7::s7_pointer {
273 match sync_is_pair(s7::s7_cadr(args)) {
274 true => {
275 let word1 = sync_heap_read(s7::s7_c_object_value(s7::s7_car(args)));
276 let word2 = sync_heap_read(s7::s7_c_object_value(s7::s7_cadr(args)));
277 s7::s7_make_boolean(sc, word1 == word2)
278 },
279 false => {
280 s7::s7_wrong_type_arg_error(
281 sc, c"equal?".as_ptr(), 2, s7::s7_cadr(args),
282 c"a sync-pair".as_ptr(),
283 )
284 }
285 }
286 }
287
288 unsafe extern "C" fn to_string(sc: *mut s7::s7_scheme, args: s7::s7_pointer) -> s7::s7_pointer {
289 string_to_s7(sc, format!(
290 "(sync-pair #u({}))",
291 sync_heap_read(s7::s7_c_object_value(s7::s7_car(args)))
292 .iter()
293 .map(|&byte| byte.to_string())
294 .collect::<Vec<String>>().join(" "),
295 ).as_str())
296 }
297
298 Type::new(
299 c"sync-pair",
300 free,
301 mark,
302 is_equal,
303 to_string,
304 )
305}
306
307fn primitive_s7_sync_hash() -> Primitive {
308 unsafe extern "C" fn code(sc: *mut s7::s7_scheme, args: s7::s7_pointer) -> s7::s7_pointer {
309 let data_bv = s7::s7_car(args);
310
311 if !s7::s7_is_byte_vector(data_bv) {
313 return s7::s7_wrong_type_arg_error(
314 sc, c"sync-hash".as_ptr(), 1, data_bv,
315 c"a byte-vector".as_ptr(),
316 )
317 }
318
319 let mut data = vec![];
321 for i in 0..s7::s7_vector_length(data_bv) {
322 data.push(s7::s7_byte_vector_ref(data_bv, i as i64))
323 }
324
325 let digest = Sha256::digest(data).to_vec();
326 let digest_bv = s7::s7_make_byte_vector(sc, SIZE as i64, 1, std::ptr::null_mut());
327 for i in 0..SIZE { s7::s7_byte_vector_set(digest_bv, i as i64, digest[i]); }
328 digest_bv
329 }
330
331 Primitive::new(
332 code,
333 c"sync-hash",
334 c"(sync-hash bv) compute the SHA-256 digest of a byte vector",
335 1, 0, false,
336 )
337}
338
339fn primitive_s7_sync_pair() -> Primitive {
340 unsafe extern "C" fn code(sc: *mut s7::s7_scheme, args: s7::s7_pointer) -> s7::s7_pointer {
341 let digest = s7::s7_car(args);
342
343 if !s7::s7_is_byte_vector(digest) || s7::s7_vector_length(digest) as usize != SIZE {
344 return s7::s7_wrong_type_arg_error(
345 sc, c"sync-pair".as_ptr(), 1, digest,
346 c"a hash-sized byte-vector".as_ptr(),
347 )
348 }
349
350 let mut word = [0 as u8; SIZE];
351 for i in 0..SIZE { word[i] = s7::s7_byte_vector_ref(digest, i as i64); }
352
353 let persistor = {
354 let session = SESSIONS.lock().unwrap();
355 &session.get(&(sc as usize)).unwrap().persistor.clone()
356 };
357
358 if word == NULL || persistor.branch_get(word).is_ok() || PERSISTOR.branch_get(word).is_ok() {
359 s7::s7_make_c_object(sc, SYNC_PAIR_TAG, sync_heap_make(word))
360 } else {
361 sync_error(sc)
362 }
363 }
364
365 Primitive::new(
366 code,
367 c"sync-pair",
368 c"(sync-pair digest) returns the sync pair defined by the digest",
369 1, 0, false,
370 )
371}
372
373fn primitive_s7_sync_is_pair() -> Primitive {
374 unsafe extern "C" fn code(sc: *mut s7::s7_scheme, args: s7::s7_pointer) -> s7::s7_pointer {
375 s7::s7_make_boolean(sc, sync_is_pair(s7::s7_car(args)))
376 }
377
378 Primitive::new(
379 code,
380 c"sync-pair?",
381 c"(sync-pair?) returns whether the object is a sync pair",
382 1, 0, false,
383 )
384}
385
386fn primitive_s7_sync_null() -> Primitive {
387 unsafe extern "C" fn code(sc: *mut s7::s7_scheme, _args: s7::s7_pointer) -> s7::s7_pointer {
388 s7::s7_make_c_object(sc, SYNC_PAIR_TAG, sync_heap_make(NULL))
389 }
390
391 Primitive::new(
392 code,
393 c"sync-null",
394 c"(sync-null) returns the null synchronic pair",
395 0, 0, false,
396 )
397}
398
399fn primitive_s7_sync_is_null() -> Primitive {
400 unsafe extern "C" fn code(sc: *mut s7::s7_scheme, args: s7::s7_pointer) -> s7::s7_pointer {
401 match sync_is_pair(s7::s7_car(args)) {
402 false => s7::s7_wrong_type_arg_error(
403 sc, c"sync-null?".as_ptr(), 1, s7::s7_car(args),
404 c"a sync-pair".as_ptr(),
405 ),
406 true => {
407 let word = sync_heap_read(s7::s7_c_object_value(s7::s7_car(args)));
408 for i in 0..SIZE {
409 if word[i] != 0 {
410 return s7::s7_make_boolean(sc, false);
411 }
412 }
413 s7::s7_make_boolean(sc, true)
414 },
415 }
416 }
417
418 Primitive::new(
419 code,
420 c"sync-null?",
421 c"(sync-null? sp) returns a boolean indicating whether sp is equal to sync-null",
422 1, 0, false,
423 )
424}
425
426fn primitive_s7_sync_pair_to_bytes() -> Primitive {
427 unsafe extern "C" fn code(sc: *mut s7::s7_scheme, args: s7::s7_pointer) -> s7::s7_pointer {
428 match sync_is_pair(s7::s7_car(args)) {
429 false => {
430 s7::s7_wrong_type_arg_error(
431 sc, c"sync-pair->byte-vector".as_ptr(), 1, s7::s7_car(args),
432 c"a sync-pair".as_ptr(),
433 )
434 }
435 true => {
436 let word = sync_heap_read(s7::s7_c_object_value(s7::s7_car(args)));
437 let bv = s7::s7_make_byte_vector(sc, SIZE as i64, 1, std::ptr::null_mut());
438 for i in 0..SIZE { s7::s7_byte_vector_set(bv, i as i64, word[i]); }
439 bv
440 },
441 }
442 }
443
444 Primitive::new(
445 code,
446 c"sync-pair->byte-vector",
447 c"(sync-pair->byte-vector sp) returns the byte-vector digest of a sync pair)",
448 1, 0, false,
449 )
450}
451
452fn primitive_s7_sync_cons() -> Primitive {
453 unsafe extern "C" fn code(sc: *mut s7::s7_scheme, args: s7::s7_pointer) -> s7::s7_pointer {
454 let persistor = {
455 let session = SESSIONS.lock().unwrap();
456 &session.get(&(sc as usize)).unwrap().persistor.clone()
457 };
458
459 let handle_arg = | obj, number | {
460 if sync_is_pair(obj) {
461 Ok(sync_heap_read(s7::s7_c_object_value(obj)))
462 } else if s7::s7_is_byte_vector(obj) {
463 let mut content = vec![];
464 for i in 0..s7::s7_vector_length(obj) {
465 content.push(s7::s7_byte_vector_ref(obj, i as i64))
466 }
467 match persistor.leaf_set(content) {
468 Ok(atom) => Ok(atom),
469 Err(_) => Err(sync_error(sc)),
470 }
471 } else {
472 Err(s7::s7_wrong_type_arg_error(
473 sc, c"sync-cons".as_ptr(), number, obj,
474 c"a byte vector or a sync pair".as_ptr(),
475 ))
476 }
477 };
478
479 match (handle_arg(s7::s7_car(args), 1), handle_arg(s7::s7_cadr(args), 2)) {
480 (Ok(left), Ok(right)) => match persistor.branch_set(left, right) {
481 Ok(pair) => s7::s7_make_c_object(sc, SYNC_PAIR_TAG, sync_heap_make(pair)),
482 Err(_) => sync_error(sc),
483 },
484 (Err(left), Ok(_)) => left,
485 (Ok(_), Err(right)) => right,
486 (Err(left), Err(_)) => left,
487 }
488 }
489
490 Primitive::new(
491 code,
492 c"sync-cons",
493 c"(sync-cons first rest) construct a lisp pair",
494 2, 0, false,
495 )
496}
497
498fn primitive_s7_sync_car() -> Primitive {
499 unsafe extern "C" fn code(sc: *mut s7::s7_scheme, args: s7::s7_pointer) -> s7::s7_pointer {
500 if !sync_is_pair(s7::s7_car(args)) {
501 return s7::s7_wrong_type_arg_error(
502 sc, c"sync-car".as_ptr(), 1, s7::s7_car(args),
503 c"a sync-pair".as_ptr(),
504 )
505 }
506 sync_cxr(sc, args, c"sync-car", | children | { children.0 })
507 }
508
509 Primitive::new(
510 code,
511 c"sync-car",
512 c"(sync-car pair) retrieve the first element of a pair",
513 1, 0, false,
514 )
515}
516
517fn primitive_s7_sync_cdr() -> Primitive {
518 unsafe extern "C" fn code(sc: *mut s7::s7_scheme, args: s7::s7_pointer) -> s7::s7_pointer {
519 if !sync_is_pair(s7::s7_car(args)) {
520 return s7::s7_wrong_type_arg_error(
521 sc, c"sync-cdr".as_ptr(), 1, s7::s7_car(args),
522 c"a sync-pair".as_ptr(),
523 )
524 }
525 sync_cxr(sc, args, c"sync-cdr", | children | { children.1 })
526 }
527
528 Primitive::new(
529 code,
530 c"sync-cdr",
531 c"(sync-car pair) retrieve the second element of a pair",
532 1, 0, false,
533 )
534}
535
536fn primitive_s7_sync_create() -> Primitive {
537 unsafe extern "C" fn code(sc: *mut s7::s7_scheme, args: s7::s7_pointer) -> s7::s7_pointer {
538 let id = s7::s7_car(args);
539
540 if !s7::s7_is_byte_vector(id) || s7::s7_vector_length(id) as usize != SIZE {
541 return s7::s7_wrong_type_arg_error(
542 sc, c"sync-create".as_ptr(), 1, id,
543 c"a hash-sized byte-vector".as_ptr(),
544 )
545 }
546
547 let mut record: Word = [0 as u8; SIZE];
548
549 for i in 0..SIZE {
550 record[i as usize] = s7::s7_byte_vector_ref(id, i as i64)
551 }
552
553 debug!("Adding record: {}", hex::encode(record));
554
555 match PERSISTOR.root_new(
556 record,
557 PERSISTOR.branch_set(
558 PERSISTOR.leaf_set(GENESIS_STR.as_bytes().to_vec()).unwrap(),
559 NULL,
560 ).unwrap(),
561 ) {
562 Ok(_) => {
563 s7::s7_make_boolean(sc, true)
564 },
565 Err(_) => s7::s7_error(
566 sc,
567 s7::s7_make_symbol(sc, c"sync-web-error".as_ptr()),
568 s7::s7_list(sc, 1, s7::s7_make_string(
569 sc,
570 c"record ID is already in use".as_ptr(),
571 )),
572 ),
573 }
574 }
575
576 Primitive::new(
577 code,
578 c"sync-create",
579 c"(sync-create id) create a new synchronic record with the given 32-byte ID",
580 1, 0, false,
581 )
582}
583
584fn primitive_s7_sync_delete() -> Primitive {
585 unsafe extern "C" fn code(sc: *mut s7::s7_scheme, args: s7::s7_pointer) -> s7::s7_pointer {
586 let id = s7::s7_car(args);
587
588 if !s7::s7_is_byte_vector(id) || s7::s7_vector_length(id) as usize != SIZE {
589 return s7::s7_wrong_type_arg_error(
590 sc, c"sync-delete".as_ptr(), 1, id,
591 c"a hash-sized byte-vector".as_ptr(),
592 )
593 }
594
595 let mut record: Word = [0 as u8; SIZE];
596
597 for i in 0..s7::s7_vector_length(id) {
598 record[i as usize] = s7::s7_byte_vector_ref(id, i as i64)
599 }
600
601 if record == NULL {
602 return s7::s7_error(
603 sc,
604 s7::s7_make_symbol(sc, c"sync-web-error".as_ptr()),
605 s7::s7_list(sc, 1, s7::s7_make_string(
606 sc,
607 c"cannot delete the root record".as_ptr(),
608 )),
609 )
610 }
611
612 debug!("Deleting record: {}", hex::encode(record));
613
614 match PERSISTOR.root_delete(record) {
615 Ok(_) => {
616 s7::s7_make_boolean(sc, true)
617 },
618 Err(_) => s7::s7_error(
619 sc,
620 s7::s7_make_symbol(sc, c"sync-web-error".as_ptr()),
621 s7::s7_list(sc, 1, s7::s7_make_string(
622 sc,
623 c"record ID does not exist".as_ptr(),
624 )),
625 ),
626 }
627 }
628
629 Primitive::new(
630 code,
631 c"sync-delete",
632 c"(sync-delete id) delete the synchronic record with the given 32-byte ID",
633 1, 0, false,
634 )
635}
636
637fn primitive_s7_sync_all() -> Primitive {
638 unsafe extern "C" fn code(sc: *mut s7::s7_scheme, _args: s7::s7_pointer) -> s7::s7_pointer {
639 let mut list = s7::s7_list(sc, 0);
640
641 for record in PERSISTOR.root_list().into_iter().rev() {
642 let bv = s7::s7_make_byte_vector(sc, SIZE as i64, 1, std::ptr::null_mut());
643 for i in 0..SIZE { s7::s7_byte_vector_set(bv, i as i64, record[i]); }
644
645 list = s7::s7_cons(sc, bv, list)
646 }
647
648 list
649 }
650
651 Primitive::new(
652 code,
653 c"sync-all",
654 c"(sync-all) list all synchronic record IDs in ascending order",
655 0, 0, false,
656 )
657}
658
659fn primitive_s7_sync_call() -> Primitive {
660 unsafe extern "C" fn code(sc: *mut s7::s7_scheme, args: s7::s7_pointer) -> s7::s7_pointer {
661 let message_expr = s7::s7_car(args);
662 let blocking = s7::s7_cadr(args);
663
664 if !s7::s7_is_boolean(blocking) {
665 return s7::s7_wrong_type_arg_error(
666 sc, c"sync-call".as_ptr(), 2, blocking,
667 c"a boolean".as_ptr(),
668 )
669 }
670
671 let record = match s7::s7_is_null(sc, s7::s7_cddr(args)) {
672 true => {
673 let session = SESSIONS.lock().unwrap();
674 session.get(&(sc as usize)).unwrap().record
675 },
676 false => {
677 let bv = s7::s7_caddr(args);
678 if !s7::s7_is_byte_vector(bv) || s7::s7_vector_length(bv) as usize != SIZE {
680 return s7::s7_wrong_type_arg_error(
681 sc, c"sync-call".as_ptr(), 3, bv,
682 c"a hash-sized byte-vector".as_ptr(),
683 )
684 }
685
686 if !s7::s7_is_byte_vector(bv) || s7::s7_vector_length(bv) as usize != SIZE {
687 return s7::s7_wrong_type_arg_error(
688 sc, c"sync-call".as_ptr(), 2, bv,
689 c"a hash-sized byte-vector".as_ptr(),
690 )
691 }
692
693 let mut record = [0 as u8; SIZE];
694 for i in 0..SIZE { record[i] = s7::s7_byte_vector_ref(bv, i as i64); }
695 record
696 }
697 };
698
699 match PERSISTOR.root_get(record) {
700 Ok(_) => {
701 let message = CStr::from_ptr(s7::s7_object_to_c_string(sc, message_expr))
702 .to_str().unwrap().to_owned();
703 if s7::s7_boolean(sc, blocking) {
704 let result = JOURNAL.evaluate_record(record, message.as_str());
705 let c_result = CString::new(format!("(quote {})", result)).unwrap();
706 s7::s7_eval_c_string(sc, c_result.as_ptr())
707 } else {
708 thread::spawn(move || {
709 JOURNAL.evaluate_record(record, message.as_str());
710 });
711 s7::s7_make_boolean(sc, true)
712 }
713 }
714 Err(_) => {
715 s7::s7_error(
716 sc,
717 s7::s7_make_symbol(sc, c"sync-web-error".as_ptr()),
718 s7::s7_list(
719 sc, 1,
720 s7::s7_make_string(sc, c"record ID does not exist".as_ptr()),
721 ),
722 )
723 }
724 }
725 }
726
727 Primitive::new(
728 code,
729 c"sync-call",
730 c"(sync-call query blocking? id) query the provided record ID or self if ID not provided",
731 2, 1, false,
732 )
733}
734
735fn primitive_s7_sync_http() -> Primitive {
736 unsafe extern "C" fn code(sc: *mut s7::s7_scheme, args: s7::s7_pointer) -> s7::s7_pointer {
737 let obj2str = | obj | {
738 CStr::from_ptr(s7::s7_object_to_c_string(sc, obj)).to_str().unwrap().to_owned()
739 };
740
741 let method = obj2str(s7::s7_car(args));
742 let url = obj2str(s7::s7_cadr(args));
743
744 let body = if s7::s7_list_length(sc, args) >= 3 {
745 obj2str(s7::s7_caddr(args))
746 } else {
747 String::from("")
748 };
749
750 match thread::spawn(move || {
751 match method.to_lowercase() {
752 method if method == "get" => {
753 reqwest::blocking::get(&url[1..url.len() -1])
754 .unwrap().bytes().unwrap()
755 }
756 method if method == "post" => {
757 reqwest::blocking::Client::new()
758 .post(&url[1..url.len() -1])
759 .body(String::from(&body[1..body.len() -1]))
760 .send().unwrap().bytes().unwrap()
761 }
762 _ => {
763 panic!()
764 }
765 }
766 }).join() {
767 Ok(vector) => {
768 let bv = s7::s7_make_byte_vector(sc, vector.len() as i64, 1, std::ptr::null_mut());
769 for i in 0..vector.len() { s7::s7_byte_vector_set(bv, i as i64, vector[i]); }
770 bv
771 }
772 Err(_) => sync_error(sc),
773 }
774 }
775
776 Primitive::new(
777 code,
778 c"sync-http",
779 c"(sync-http method url . data) make an http request where method is 'get or 'post",
780 2, 2, false,
781 )
782}
783
784fn primitive_s7_sync_remote() -> Primitive {
785 unsafe extern "C" fn code(sc: *mut s7::s7_scheme, args: s7::s7_pointer) -> s7::s7_pointer {
786 let obj2str = | obj | {
787 CStr::from_ptr(s7::s7_object_to_c_string(sc, obj)).to_str().unwrap().to_owned()
788 };
789
790 let url = obj2str(s7::s7_car(args));
791
792 let body = obj2str(s7::s7_cadr(args));
793
794 match thread::spawn(move || {
795 reqwest::blocking::Client::new()
796 .post(&url[1..url.len() -1])
797 .body(body)
798 .send().unwrap().bytes().unwrap()
799 }).join() {
800 Ok(bytes) => {
801 let mut vector = bytes.to_vec();
802 vector.insert(0, 39); vector.push(0);
804 let c_string = CString::from_vec_with_nul(vector).unwrap();
805 s7::s7_eval_c_string(sc, c_string.as_ptr())
806 }
807 Err(_) => sync_error(sc),
808 }
809 }
810
811 Primitive::new(
812 code,
813 c"sync-remote",
814 c"(sync-remote url data) make a post http request with the data payload)",
815 2, 0, false,
816 )
817}
818
819unsafe fn string_to_s7(sc: *mut s7::s7_scheme, string: &str) -> s7::s7_pointer {
820 let c_string = CString::new(string).unwrap();
821 let s7_string = s7::s7_make_string(sc, c_string.as_ptr());
822 s7::s7_object_to_string(sc, s7_string, false)
823}
824
825unsafe fn sync_heap_make(word: Word) -> *mut libc::c_void {
826 let ptr = libc::malloc(SIZE);
827 let array: &mut [u8] = std::slice::from_raw_parts_mut(ptr as *mut u8, SIZE);
828 for i in 0..SIZE { array[i] = word[i] as u8; }
829 ptr
830}
831
832unsafe fn sync_heap_read(ptr: *mut libc::c_void) -> Word {
833 std::slice::from_raw_parts_mut(ptr as *mut u8, SIZE).try_into().unwrap()
834}
835
836unsafe fn sync_heap_free(ptr: *mut libc::c_void) {
837 libc::free(ptr);
838}
839
840unsafe fn sync_is_pair(obj: s7::s7_pointer) -> bool {
841 s7::s7_is_c_object(obj) && s7::s7_c_object_type(obj) == SYNC_PAIR_TAG
842}
843
844unsafe fn sync_cxr(
845 sc: *mut s7::s7_scheme,
846 args: s7::s7_pointer,
847 name: &CStr,
848 selector: fn((Word, Word)) -> Word,
849) -> s7::s7_pointer {
850 let pair = s7::s7_car(args);
851 let word = sync_heap_read(s7::s7_c_object_value(pair));
852
853 let persistor = {
854 let session = SESSIONS.lock().unwrap();
855 &session.get(&(sc as usize)).unwrap().persistor.clone()
856 };
857
858 let child_return = | word | {
859 let pair_return = | word | {
860 s7::s7_make_c_object(sc, SYNC_PAIR_TAG, sync_heap_make(word))
861 };
862
863 let vector_return = | vector: Vec<u8> | {
864 let bv = s7::s7_make_byte_vector(sc, vector.len() as i64, 1, std::ptr::null_mut());
865 for i in 0..vector.len() { s7::s7_byte_vector_set(bv, i as i64, vector[i]); }
866 bv
867 };
868
869 if word == NULL {
870 pair_return(word)
871 } else if let Ok(_) = persistor.branch_get(word) {
872 pair_return(word)
873 } else if let Ok(_) = PERSISTOR.branch_get(word) {
874 pair_return(word)
875 } else if let Ok(content) = persistor.leaf_get(word) {
876 vector_return(content)
877 } else if let Ok(content) = PERSISTOR.leaf_get(word) {
878 vector_return(content)
879 } else {
880 sync_error(sc)
881 }
882 };
883
884 match sync_is_pair(pair) {
885 true => match persistor.branch_get(word) {
886 Ok(children) => child_return(selector(children)),
887 Err(_) => match PERSISTOR.branch_get(word) {
888 Ok(children) => child_return(selector(children)),
889 Err(_) => sync_error(sc),
890 }
891 },
892 false => s7::s7_wrong_type_arg_error(
893 sc, name.as_ptr(), 1, pair,
894 c"a sync-pair".as_ptr(),
895 )
896 }
897}
898