journal_sdk/
lib.rs

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
53/// Journals are the primary way that application developers
54/// interact with the synchronic web.
55///
56/// Conceptually, a Journal is a
57/// service that interacts with users and other Journals (nodes) to
58/// persist synchronic web state. Behind the schemes, it is
59/// responsible for two capabilities:
60///
61/// * __Persistence__: managing bytes on the global hash graph
62/// 
63/// * __Evaluation__: executing code in the global Lisp environment
64///
65/// __Records__ are the primary way that developers interface with
66/// Journals. A Record is a mapping between a constant identifier and
67/// mutable state. Both identifiers and state are represented as
68/// fixed-size __Words__ that the outputs of a cryptographic hash
69/// function. When a new record is created, the Journal returns a
70/// record secret that is the second hash preimage of the identifier.
71/// This is intended to be used so that applications can bootstrap
72/// records into increasingly sophisticated notions of identity.
73pub 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    /// Evaluate a Lisp expression within a Record
90    ///
91    /// # Examples
92    /// ```
93    /// use journal_sdk::JOURNAL;
94    ///
95    /// // Simple expression
96    /// let output = JOURNAL.evaluate("(+ 1 2)");
97    /// assert!(output == "3");
98    ///
99    /// // Complex expression
100    /// let output = JOURNAL.evaluate(
101    ///     "(begin (define (add2 x) (+ x 2)) (add2 1))",
102    /// );
103    /// assert!(output == "3");
104    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        // check the input arguments
312        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        // convert to rust data types
320        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                // check the input arguments
679                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); // add quote character so that it evaluates correctly
803                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