changelog shortlog graph tags branches files raw help

Mercurial > demo / changeset: refactor 2 (wip)

changeset 27: 529419ac94f3
parent 26: 2015d7277629
child 28: 242002f9f098
author: ellis <ellis@rwest.io>
date: Tue, 06 Jun 2023 18:55:17 -0400
files: demo.asd readme.org src/build.rs src/cbindgen.toml src/db/db.lisp src/gen.rs src/lib.rs src/package.lisp src/packages.lisp src/tk/rs.lisp src/tk/tk.lisp src/ui/ui.lisp src/utils/rs.lisp src/utils/utils.lisp tools/deps.sh
description: refactor 2 (wip)
     1.1--- a/demo.asd	Mon Jun 05 19:59:26 2023 -0400
     1.2+++ b/demo.asd	Tue Jun 06 18:55:17 2023 -0400
     1.3@@ -1,41 +1,25 @@
     1.4 ;;; demo.asd
     1.5 (in-package #:asdf-user)
     1.6 
     1.7+(defsystem "demo/sys"
     1.8+  :components ((:file "src/package")))
     1.9+
    1.10 (defsystem "demo"
    1.11   :version "0.1.0"
    1.12   :author "ellis <ellis@rwest.io>"
    1.13   :maintainer "ellis <ellis@rwest.io>"
    1.14   :description ""
    1.15   :homepage "https://rwest.io/p/demo"
    1.16-  :bug-tracker "https://gitlab.rwest.io/ellis/demo/issues"
    1.17-  :source-control (:hg "https://gitlab.rwest.io/ellis/demo")
    1.18+  :bug-tracker "https://lab.rwest.io/otom8/demo/issues"
    1.19+  :source-control (:hg "https://lab.rwest.io/otom8/demo")
    1.20   :license "WTFPL"
    1.21-  :depends-on ("demo/sys" "demo/db" "demo/ui" "demo/cli")
    1.22+  :depends-on ("demo/sys" :cl-dbi :sxql :log4cl :verbose :bordeaux-threads :clingon :clog)
    1.23   :in-order-to ((test-op (test-op "src/test")))
    1.24   :build-pathname "demo")
    1.25 
    1.26-(defsystem "demo/sys"
    1.27-  :depends-on (:sxql :log4cl)
    1.28-  :components ((:file "src/packages")
    1.29-	       (:module "tk"
    1.30-		:pathname "src/tk"
    1.31-		:serial t
    1.32-		:components ((:file "tk")
    1.33-			     (:file "rs" :depends-on ("tk"))))))
    1.34-
    1.35 (defmethod perform :after ((op load-op) (c (eql (find-system :demo))))
    1.36   (pushnew :demo *features*))
    1.37 
    1.38-(defsystem "demo/cli"
    1.39-  :depends-on (:clingon "demo/sys" "demo/ui" "demo/db")
    1.40-  :components ((:file "src/cli")))
    1.41-(defsystem "demo/ui"
    1.42-  :depends-on (:clog "demo/sys" "demo/db")
    1.43-  :components ((:file "src/ui")))
    1.44-(defsystem "demo/db"
    1.45-  :depends-on (:cl-dbi "demo/sys")
    1.46-  :components ((:file "src/db")))			     			     
    1.47-
    1.48 (defsystem "demo/tests"
    1.49   :depends-on ("demo" "fiveam")
    1.50   :components ((:module "src/tests"
     2.1--- a/readme.org	Mon Jun 05 19:59:26 2023 -0400
     2.2+++ b/readme.org	Tue Jun 06 18:55:17 2023 -0400
     2.3@@ -6,11 +6,12 @@
     2.4 * How it works
     2.5 The backend services are written in Rust and controlled by a simple
     2.6 messaging protocol. Services provide common runtime capabilities known
     2.7-as the /core protocol/ but are specialized on a unique /service type/
     2.8-which may in turn register their own /custom protocols/ (via core).
     2.9+as the /service protocol/ but are specialized on a unique /service
    2.10+type/ which may in turn register their own /custom protocols/ (via
    2.11+core).
    2.12 
    2.13 Services are capable of dispatching data directly to clients, or
    2.14-storing data in the /database/ (TBD).
    2.15+storing data in the /database/ (sqlite, postgres, mysql).
    2.16 
    2.17 The frontend clients are pre-dominantly written in Common Lisp and
    2.18 come in many shapes and sizes. There is a cli-client, web-client
    2.19@@ -20,6 +21,9 @@
    2.20 * Guide
    2.21 ** Build
    2.22 - *install dependencies*
    2.23+  #+begin_src bash
    2.24+    ./tools/deps.sh
    2.25+  #+end_src
    2.26   - Rust =curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh=
    2.27   - Common Lisp
    2.28     - on Linux ::
     3.1--- a/src/build.rs	Mon Jun 05 19:59:26 2023 -0400
     3.2+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.3@@ -1,17 +0,0 @@
     3.4-use std::env;
     3.5-use std::fs::create_dir;
     3.6-use std::path::PathBuf;
     3.7-fn main() {
     3.8-  let crate_dir: PathBuf = env::var("CARGO_MANIFEST_DIR")
     3.9-    .expect("CARGO_MANIFEST_DIR env var is not defined")
    3.10-    .into();
    3.11-  // let mpk_py = "build.py";
    3.12-  let build_dir = crate_dir.join("ffi/");
    3.13-  if !build_dir.exists() {
    3.14-    create_dir(&build_dir).unwrap();
    3.15-  }
    3.16-  cbindgen::generate(crate_dir)
    3.17-    .expect("Unable to find cbindgen.toml configuration file")
    3.18-    .write_to_file(build_dir.join("demo.h"));
    3.19-  
    3.20-}
     4.1--- a/src/cbindgen.toml	Mon Jun 05 19:59:26 2023 -0400
     4.2+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.3@@ -1,14 +0,0 @@
     4.4-include_guard = "demo_h"
     4.5-autogen_warning = "/* DO NOT TOUCH */"
     4.6-include_version = true
     4.7-language = "C"
     4.8-cpp_compat = true
     4.9-line_length = 88
    4.10-documentation = true	
    4.11-[parse]
    4.12-parse_deps = true
    4.13-include = ["obj","fig","libc"]
    4.14-extra_bindings = ["obj","fig","libc"]
    4.15-#expand = ["demo","obj"]
    4.16-#[parse.expand]
    4.17-#crates = ["demo"]
    4.18\ No newline at end of file
     5.1--- a/src/db/db.lisp	Mon Jun 05 19:59:26 2023 -0400
     5.2+++ b/src/db/db.lisp	Tue Jun 06 18:55:17 2023 -0400
     5.3@@ -1,197 +1,1 @@
     5.4-(in-package :demo)
     5.5-
     5.6-(define-foreign-library rocksdb
     5.7-  (:win32 "rocksdb")
     5.8-  (t (:default "librocksdb")))
     5.9-
    5.10-(use-foreign-library rocksdb)
    5.11-
    5.12-(defcfun ("rocksdb_options_create" create-options) :pointer)
    5.13-(defcfun ("rocksdb_options_destroy" destroy-options) :void (options :pointer))
    5.14-(defcfun ("rocksdb_options_increase_parallelism" increase-parallelism) :void (opt :pointer) (total-threads :int))
    5.15-(defcfun ("rocksdb_options_optimize_level_style_compaction" optimize-level-style-compaction) :void (opt :pointer) (memtable_memory_budget :uint64))
    5.16-(defcfun ("rocksdb_options_set_create_if_missing" set-create-if-missing) :void (opt :pointer) (val :boolean))
    5.17-
    5.18-(defcfun ("rocksdb_writeoptions_create" create-writeoptions) :pointer)
    5.19-(defcfun ("rocksdb_writeoptions_destroy" destroy-writeoptions) :void (opt :pointer))
    5.20-(defcfun ("rocksdb_readoptions_create" create-readoptions) :pointer)
    5.21-(defcfun ("rocksdb_readoptions_destroy" destroy-readoptions) :void (opt :pointer))
    5.22-
    5.23-(defcfun ("rocksdb_open" open-db*) :pointer (opt :pointer) (name :string) (errptr :pointer))
    5.24-(defcfun ("rocksdb_close" close-db) :void (opt :pointer))
    5.25-(defcfun ("rocksdb_cancel_all_background_work" cancel-all-background-work) :void (db :pointer) (wait :boolean))
    5.26-
    5.27-(defcfun ("rocksdb_put" put*) :void (db :pointer) (options :pointer) (key :pointer) (keylen :unsigned-int) (val :pointer) (vallen :unsigned-int) (errptr :pointer))
    5.28-(defcfun ("rocksdb_get" get*) :pointer (db :pointer) (options :pointer) (key :pointer) (keylen :unsigned-int) (vallen :pointer) (errptr :pointer))
    5.29-
    5.30-(defcfun ("rocksdb_create_iterator" create-iter*) :pointer (db :pointer) (opt :pointer))
    5.31-(defcfun ("rocksdb_iter_destroy" destroy-iter) :void (iter :pointer))
    5.32-(defcfun ("rocksdb_iter_seek_to_first" move-iter-to-first) :void (iter :pointer))
    5.33-(defcfun ("rocksdb_iter_valid" valid-iter-p) :boolean (iter :pointer))
    5.34-(defcfun ("rocksdb_iter_next" move-iter-forward) :void (iter :pointer))
    5.35-(defcfun ("rocksdb_iter_prev" move-iter-backward) :void (iter :pointer))
    5.36-(defcfun ("rocksdb_iter_key" iter-key*) :pointer (iter :pointer) (klen-ptr :pointer))
    5.37-(defcfun ("rocksdb_iter_value" iter-value*) :pointer (iter :pointer) (vlen-ptr :pointer))
    5.38-
    5.39-(define-condition unable-to-open-db (error)
    5.40-  ((db-path :initarg :db-path
    5.41-            :reader db-path)
    5.42-   (error-message :initarg :error-message
    5.43-                  :reader error-message)))
    5.44-
    5.45-(defmethod print-object ((obj unable-to-open-db) stream)
    5.46-  (print-unreadable-object (obj stream :type t :identity t)
    5.47-    (format stream "error-message=~A" (error-message obj))))
    5.48-
    5.49-(define-condition unable-to-put-key-value-to-db (error)
    5.50-  ((db :initarg :db
    5.51-       :reader db)
    5.52-   (key :initarg :key
    5.53-        :reader key)
    5.54-   (val :initarg :val
    5.55-        :reader val)
    5.56-   (error-message :initarg :error-message
    5.57-                  :reader error-message)))
    5.58-
    5.59-(define-condition unable-to-get-value-to-db (error)
    5.60-  ((db :initarg :db
    5.61-       :reader db)
    5.62-   (key :initarg :key
    5.63-        :reader key)
    5.64-   (error-message :initarg :error-message
    5.65-                  :reader error-message)))
    5.66-
    5.67-(defun open-db (db-path &optional opt)
    5.68-  (unless opt
    5.69-    (setq opt (create-options)))
    5.70-  (let ((errptr (foreign-alloc :pointer)))
    5.71-    (setf (mem-ref errptr :pointer) (null-pointer))
    5.72-    (let* ((db-path (if (pathnamep db-path)
    5.73-                        (namestring db-path)
    5.74-                        db-path))
    5.75-           (db (open-db* opt db-path errptr))
    5.76-           (err (mem-ref errptr :pointer)))
    5.77-      (unless (null-pointer-p err)
    5.78-        (error 'unable-to-open-db
    5.79-               :db-path db-path
    5.80-               :error-message (foreign-string-to-lisp err)))
    5.81-      db)))
    5.82-
    5.83-(defmacro clone-octets-to-foreign (lisp-array foreign-array)
    5.84-  (let ((i (gensym)))
    5.85-    `(loop for ,i from 0 below (length ,lisp-array)
    5.86-           do (setf (mem-aref ,foreign-array :unsigned-char ,i)
    5.87-                    (aref ,lisp-array ,i)))))
    5.88-
    5.89-(defmacro clone-octets-from-foreign (foreign-array lisp-array len)
    5.90-  (let ((i (gensym)))
    5.91-    `(loop for ,i from 0 below ,len
    5.92-           do (setf (aref ,lisp-array ,i)
    5.93-                    (mem-aref ,foreign-array :unsigned-char ,i)))))
    5.94-
    5.95-(defun put-kv (db key val &optional opt)
    5.96-  (unless opt
    5.97-    (setq opt (create-writeoptions)))
    5.98-  (with-foreign-objects ((errptr :pointer)
    5.99-                         (key* :unsigned-char (length key))
   5.100-                         (val* :unsigned-char (length val)))
   5.101-    (clone-octets-to-foreign key key*)
   5.102-    (clone-octets-to-foreign val val*)
   5.103-    (setf (mem-ref errptr :pointer) (null-pointer))
   5.104-    (put* db
   5.105-          opt
   5.106-          key*
   5.107-          (length key)
   5.108-          val*
   5.109-          (length val)
   5.110-          errptr)
   5.111-    (let ((err (mem-ref errptr :pointer)))
   5.112-      (unless (null-pointer-p err)
   5.113-        (error 'unable-to-put-key-value-to-db
   5.114-               :db db
   5.115-               :key key
   5.116-               :val val
   5.117-               :error-message (foreign-string-to-lisp err))))))
   5.118-
   5.119-(defun put-kv-str (db key val &optional opt)
   5.120-  (let ((key-octets (babel:string-to-octets key))
   5.121-        (val-octets (babel:string-to-octets val)))
   5.122-    (put-kv db key-octets val-octets opt)))
   5.123-
   5.124-(defun get-kv (db key &optional opt)
   5.125-  (unless opt
   5.126-    (setq opt (create-readoptions)))
   5.127-
   5.128-  (with-foreign-objects ((val-len-ptr :unsigned-int)
   5.129-                         (errptr :pointer)
   5.130-                         (key* :unsigned-char (length key)))
   5.131-    (clone-octets-to-foreign key key*)
   5.132-    (setf (mem-ref errptr :pointer) (null-pointer))
   5.133-    (let ((val (get* db
   5.134-                     opt
   5.135-                     key*
   5.136-                     (length key)
   5.137-                     val-len-ptr
   5.138-                     errptr)))
   5.139-      (let ((err (mem-ref errptr :pointer)))
   5.140-        (unless (null-pointer-p err)
   5.141-          (error 'unable-to-get-value-to-db
   5.142-                 :db db
   5.143-                 :key key
   5.144-                 :error-message (foreign-string-to-lisp err)))
   5.145-        
   5.146-        (unless (null-pointer-p val)
   5.147-          (let* ((val-len (mem-ref val-len-ptr :unsigned-int))
   5.148-                 (val* (make-array val-len
   5.149-                                      :element-type '(unsigned-byte 8))))
   5.150-            (clone-octets-from-foreign val val* val-len)
   5.151-            val*))))))
   5.152-
   5.153-(defun get-kv-str (db key &optional opt)
   5.154-  (let ((key-octets (babel:string-to-octets key)))
   5.155-    (let ((#1=val-octets (get-kv db key-octets opt)))
   5.156-      (when #1#
   5.157-        (babel:octets-to-string #1#)))))
   5.158-
   5.159-(defun create-iter (db &optional opt)
   5.160-  (unless opt
   5.161-    (setq opt (create-readoptions)))
   5.162-  (create-iter* db opt))
   5.163-
   5.164-(defun iter-key (iter)
   5.165-  (with-foreign-objects ((klen-ptr :unsigned-int))
   5.166-    (setf (mem-ref klen-ptr :unsigned-int) 0)
   5.167-    (let* ((key-ptr (iter-key* iter klen-ptr))
   5.168-           (klen (mem-ref klen-ptr :unsigned-int))
   5.169-           (key (make-array klen :element-type '(unsigned-byte 8))))
   5.170-      (clone-octets-from-foreign key-ptr key klen)
   5.171-      key)))
   5.172-
   5.173-(defun iter-key-str (iter)
   5.174-  (let ((#1=key-octets (iter-key iter)))
   5.175-    (when #1#
   5.176-      (babel:octets-to-string #1#))))
   5.177-
   5.178-(defun iter-value (iter)
   5.179-  (with-foreign-objects ((len-ptr :unsigned-int))
   5.180-    (setf (mem-ref len-ptr :unsigned-int) 0)
   5.181-    (let* ((value-ptr (iter-value* iter len-ptr))
   5.182-           (vlen (mem-ref len-ptr :unsigned-int))
   5.183-           (value* (make-array vlen :element-type '(unsigned-byte 8))))
   5.184-      (clone-octets-from-foreign value-ptr value* vlen)
   5.185-      value*)))
   5.186-
   5.187-(defun iter-value-str (iter)
   5.188-  (let ((#1=val-octets (iter-value iter)))
   5.189-    (when #1#
   5.190-      (babel:octets-to-string #1#))))
   5.191-
   5.192-(defmacro with-open-db ((db-var db-path &optional opt) &body body)
   5.193-  `(let ((,db-var (open-db ,db-path ,opt)))
   5.194-     (unwind-protect (progn ,@body)
   5.195-       (close-db ,db-var))))
   5.196-
   5.197-(defmacro with-iter ((iter-var db &optional opt) &body body)
   5.198-  `(let ((,iter-var (create-iter ,db ,opt)))
   5.199-     (unwind-protect (progn ,@body)
   5.200-       (destroy-iter ,iter-var))))
   5.201+(in-package :demo-db)
     6.1--- a/src/gen.rs	Mon Jun 05 19:59:26 2023 -0400
     6.2+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     6.3@@ -1,84 +0,0 @@
     6.4-//! demo
     6.5-pub use fig::*;
     6.6-pub use obj::*;
     6.7-use std::ffi::{CStr, CString}; //OsStr,Path
     6.8-                               //use std::os::unix::ffi::OsStrExt;
     6.9-use std::slice;
    6.10-use libc::{c_char,size_t};
    6.11-
    6.12-#[macro_export]
    6.13-macro_rules! cdefn {
    6.14-  (free $t:tt $n:tt) => {
    6.15-    #[no_mangle]
    6.16-    pub unsafe extern "C" fn $n(ptr: *mut $t) {
    6.17-      if ptr.is_null() {
    6.18-        return;
    6.19-      }
    6.20-      let _ = Box::from_raw(ptr);
    6.21-    }
    6.22-  };
    6.23-  (from_string $t:tt $n:tt) => {
    6.24-    #[no_mangle]
    6.25-    pub unsafe extern "C" fn $n(ptr: *const c_char) -> *mut $t {
    6.26-      assert!(!ptr.is_null());
    6.27-      let p = CStr::from_ptr(ptr).to_str().unwrap();
    6.28-      Box::into_raw(Box::new(p.into()))
    6.29-    }
    6.30-  };
    6.31-  (json_string $t:tt $r:tt $w:tt) => {
    6.32-    #[no_mangle]
    6.33-    pub unsafe extern "C" fn $r(ptr: *const c_char) -> *mut $t {
    6.34-      assert!(!ptr.is_null());
    6.35-      let s = CStr::from_ptr(ptr);
    6.36-      Box::into_raw(Box::new($t::from_json_str(&s.to_str().unwrap()).unwrap()))
    6.37-    }
    6.38-
    6.39-    #[no_mangle]
    6.40-    pub unsafe extern "C" fn $w(ptr: *const $t) -> *mut c_char {
    6.41-      let p = &*ptr;
    6.42-      let x = p.to_json_string().unwrap();
    6.43-      CString::new(x.as_str().as_bytes()).unwrap().into_raw()
    6.44-    }
    6.45-  };
    6.46-  (ron_string $t:tt $r:tt $w:tt) => {
    6.47-    #[no_mangle]
    6.48-    pub unsafe extern "C" fn $r(ptr: *const c_char) -> *mut $t {
    6.49-      assert!(!ptr.is_null());
    6.50-      let s = CStr::from_ptr(ptr);
    6.51-      Box::into_raw(Box::new($t::from_ron_str(&s.to_str().unwrap()).unwrap()))
    6.52-    }
    6.53-
    6.54-    #[no_mangle]
    6.55-    pub unsafe extern "C" fn $w(ptr: *const $t) -> *mut c_char {
    6.56-      let p = &*ptr;
    6.57-      let x = p.to_ron_string().unwrap();
    6.58-      CString::new(x.as_str().as_bytes()).unwrap().into_raw()
    6.59-    }
    6.60-  };
    6.61-  (bytes $t:tt $r:tt $w:tt) => {
    6.62-    #[no_mangle]
    6.63-    pub unsafe extern "C" fn $r(ptr: *const u8, len: size_t) -> *mut $t {
    6.64-      Box::into_raw(Box::new($t::decode(slice::from_raw_parts(ptr,len)).unwrap()))
    6.65-    }
    6.66-
    6.67-    #[no_mangle]
    6.68-    pub unsafe extern "C" fn $w(ptr: *const $t) -> *mut u8 {
    6.69-      let p = &*ptr;
    6.70-      let mut x = p.encode().unwrap();
    6.71-      let r = x.as_mut_ptr();
    6.72-      std::mem::forget(x);
    6.73-      r
    6.74-    }
    6.75-  }
    6.76-}
    6.77-
    6.78-cdefn!(free Service free_service);
    6.79-cdefn!(from_string Service service_from_string);
    6.80-cdefn!(json_string Service service_from_json_string service_to_json_string);
    6.81-cdefn!(ron_string Service service_from_ron_string service_to_ron_string);
    6.82-cdefn!(bytes Service service_decode service_encode);
    6.83-cdefn!(free CustomService free_custom_service);
    6.84-cdefn!(from_string CustomService custom_service_from_string);
    6.85-cdefn!(json_string CustomService custom_service_from_json_string custom_service_to_json_string);
    6.86-cdefn!(ron_string CustomService custom_service_from_ron_string custom_service_to_ron_string);
    6.87-cdefn!(bytes CustomService custom_service_decode custom_service_encode);
     7.1--- a/src/lib.rs	Mon Jun 05 19:59:26 2023 -0400
     7.2+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     7.3@@ -1,117 +0,0 @@
     7.4-//! demo/lib.rs --- generated by DEMO:RS-MACROEXPAND
     7.5-extern crate libc;
     7.6-extern crate obj;
     7.7-use libc::{c_char, size_t};
     7.8-use obj::{CustomService, Objective, Service};
     7.9-use std::ffi::{CStr, CString};
    7.10-use std::slice;
    7.11-#[no_mangle]
    7.12-pub unsafe extern "C" fn free_service(ptr: *mut Service) {
    7.13-  if ptr.is_null() {
    7.14-    return;
    7.15-  }
    7.16-  let _ = Box::from_raw(ptr);
    7.17-}
    7.18-#[no_mangle]
    7.19-pub unsafe extern "C" fn service_from_string(ptr: *const c_char) -> *mut Service {
    7.20-  assert!(!ptr.is_null());
    7.21-  let p = CStr::from_ptr(ptr).to_str().unwrap();
    7.22-  Box::into_raw(Box::new(p.into()))
    7.23-}
    7.24-#[no_mangle]
    7.25-pub unsafe extern "C" fn service_from_json_string(ptr: *const c_char) -> *mut Service {
    7.26-  assert!(!ptr.is_null());
    7.27-  let s = CStr::from_ptr(ptr);
    7.28-  Box::into_raw(Box::new(
    7.29-    Service::from_json_str(&s.to_str().unwrap()).unwrap(),
    7.30-  ))
    7.31-}
    7.32-#[no_mangle]
    7.33-pub unsafe extern "C" fn service_to_json_string(ptr: *const Service) -> *mut c_char {
    7.34-  let p = &*ptr;
    7.35-  let x = p.to_json_string().unwrap();
    7.36-  CString::new(x.as_str().as_bytes()).unwrap().into_raw()
    7.37-}
    7.38-#[no_mangle]
    7.39-pub unsafe extern "C" fn service_from_ron_string(ptr: *const c_char) -> *mut Service {
    7.40-  assert!(!ptr.is_null());
    7.41-  let s = CStr::from_ptr(ptr);
    7.42-  Box::into_raw(Box::new(
    7.43-    Service::from_ron_str(&s.to_str().unwrap()).unwrap(),
    7.44-  ))
    7.45-}
    7.46-#[no_mangle]
    7.47-pub unsafe extern "C" fn service_to_ron_string(ptr: *const Service) -> *mut c_char {
    7.48-  let p = &*ptr;
    7.49-  let x = p.to_ron_string().unwrap();
    7.50-  CString::new(x.as_str().as_bytes()).unwrap().into_raw()
    7.51-}
    7.52-#[no_mangle]
    7.53-pub unsafe extern "C" fn service_decode(ptr: *const u8, len: size_t) -> *mut Service {
    7.54-  Box::into_raw(Box::new(
    7.55-    Service::decode(slice::from_raw_parts(ptr, len)).unwrap(),
    7.56-  ))
    7.57-}
    7.58-#[no_mangle]
    7.59-pub unsafe extern "C" fn service_encode(ptr: *const Service) -> *mut u8 {
    7.60-  let p = &*ptr;
    7.61-  let mut x = p.encode().unwrap();
    7.62-  let r = x.as_mut_ptr();
    7.63-  std::mem::forget(x);
    7.64-  r
    7.65-}
    7.66-#[no_mangle]
    7.67-pub unsafe extern "C" fn free_custom_service(ptr: *mut CustomService) {
    7.68-  if ptr.is_null() {
    7.69-    return;
    7.70-  }
    7.71-  let _ = Box::from_raw(ptr);
    7.72-}
    7.73-#[no_mangle]
    7.74-pub unsafe extern "C" fn custom_service_from_string(ptr: *const c_char) -> *mut CustomService {
    7.75-  assert!(!ptr.is_null());
    7.76-  let p = CStr::from_ptr(ptr).to_str().unwrap();
    7.77-  Box::into_raw(Box::new(p.into()))
    7.78-}
    7.79-#[no_mangle]
    7.80-pub unsafe extern "C" fn custom_service_from_json_string(ptr: *const c_char) -> *mut CustomService {
    7.81-  assert!(!ptr.is_null());
    7.82-  let s = CStr::from_ptr(ptr);
    7.83-  Box::into_raw(Box::new(
    7.84-    CustomService::from_json_str(&s.to_str().unwrap()).unwrap(),
    7.85-  ))
    7.86-}
    7.87-#[no_mangle]
    7.88-pub unsafe extern "C" fn custom_service_to_json_string(ptr: *const CustomService) -> *mut c_char {
    7.89-  let p = &*ptr;
    7.90-  let x = p.to_json_string().unwrap();
    7.91-  CString::new(x.as_str().as_bytes()).unwrap().into_raw()
    7.92-}
    7.93-#[no_mangle]
    7.94-pub unsafe extern "C" fn custom_service_from_ron_string(ptr: *const c_char) -> *mut CustomService {
    7.95-  assert!(!ptr.is_null());
    7.96-  let s = CStr::from_ptr(ptr);
    7.97-  Box::into_raw(Box::new(
    7.98-    CustomService::from_ron_str(&s.to_str().unwrap()).unwrap(),
    7.99-  ))
   7.100-}
   7.101-#[no_mangle]
   7.102-pub unsafe extern "C" fn custom_service_to_ron_string(ptr: *const CustomService) -> *mut c_char {
   7.103-  let p = &*ptr;
   7.104-  let x = p.to_ron_string().unwrap();
   7.105-  CString::new(x.as_str().as_bytes()).unwrap().into_raw()
   7.106-}
   7.107-#[no_mangle]
   7.108-pub unsafe extern "C" fn custom_service_decode(ptr: *const u8, len: size_t) -> *mut CustomService {
   7.109-  Box::into_raw(Box::new(
   7.110-    CustomService::decode(slice::from_raw_parts(ptr, len)).unwrap(),
   7.111-  ))
   7.112-}
   7.113-#[no_mangle]
   7.114-pub unsafe extern "C" fn custom_service_encode(ptr: *const CustomService) -> *mut u8 {
   7.115-  let p = &*ptr;
   7.116-  let mut x = p.encode().unwrap();
   7.117-  let r = x.as_mut_ptr();
   7.118-  std::mem::forget(x);
   7.119-  r
   7.120-}
     8.1--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     8.2+++ b/src/package.lisp	Tue Jun 06 18:55:17 2023 -0400
     8.3@@ -0,0 +1,44 @@
     8.4+;; demo packages.lisp
     8.5+(defpackage :demo-sys
     8.6+  (:nicknames :ds))
     8.7+(defpackage :demo-utils
     8.8+  (:use :demo-sys)
     8.9+  (:nicknames :dutils)
    8.10+  (:export
    8.11+   #:source-dir
    8.12+   #:random-id
    8.13+   #:scan-dir)
    8.14+  (:export
    8.15+   #:*cargo-target*
    8.16+   #:*rs-macros*
    8.17+   #:rs-defmacro
    8.18+   #:rs-macroexpand-1
    8.19+   #:rs-macroexpand))
    8.20+(defpackage :demo-db
    8.21+  (:use :demo-sys)
    8.22+  (:nicknames :ddb))
    8.23+(defpackage :demo-ui
    8.24+  (:use :demo-sys)
    8.25+  (:nicknames :dui)
    8.26+  (:export
    8.27+   #:on-new-window
    8.28+   #:start-ui))
    8.29+(defpackage :demo-cli
    8.30+  (:use :demo-sys)
    8.31+  (:nicknames :dcli)
    8.32+  (:export
    8.33+   #:run-cli
    8.34+   #:demo-path
    8.35+   #:db-path
    8.36+   #:cli-opts
    8.37+   #:cli-handler
    8.38+   #:cli-cmd))
    8.39+(defpackage :demo
    8.40+  (:use #:cl #:demo-sys #:demo-utils #:demo-db #:demo-ui #:demo-cli)
    8.41+  (:nicknames :d)
    8.42+  (:local-nicknames
    8.43+   (#:v #:org.shirakumo.verbose)
    8.44+   (#:bt #:bordeaux-threads)
    8.45+   (#:cli #:clingon)))
    8.46+(defpackage :demo-user
    8.47+  (:use :demo #:cl-user))
     9.1--- a/src/packages.lisp	Mon Jun 05 19:59:26 2023 -0400
     9.2+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     9.3@@ -1,43 +0,0 @@
     9.4-;; demo packages.lisp
     9.5-(defpackage :demo-user
     9.6-  (:use :demo))
     9.7-
     9.8-(defpackage :demo
     9.9-  (:use #:cl #:demo-ui #:demo-cli #:demo-tk #:demo-db)
    9.10-  (:local-nicknames
    9.11-   (#:v #:org.shirakumo.verbose)
    9.12-   (#:bt #:bordeaux-threads)
    9.13-   (#:cli #:clingon)))
    9.14-
    9.15-(defpackage :demo-ui
    9.16-  (:use)
    9.17-  (:export
    9.18-   #:on-new-window
    9.19-   #:start-ui))
    9.20-(defpackage :demo-tk
    9.21-  (:use)
    9.22-  (:export
    9.23-   #:source-dir
    9.24-   #:random-id
    9.25-   #:scan-dir
    9.26-   #:mkstr
    9.27-   #:symb
    9.28-   #:sbq-reader)
    9.29-  (:export
    9.30-   #:*cargo-target*
    9.31-   #:*rs-macros*
    9.32-   #:rs-defmacro
    9.33-   #:rs-macroexpand-1
    9.34-   #:rs-macroexpand))
    9.35-(defpackage :demo-cli
    9.36-  (:use)
    9.37-  (:local-nick
    9.38-  (:export
    9.39-   #:run-cli
    9.40-   #:demo-path
    9.41-   #:db-path
    9.42-   #:cli-opts
    9.43-   #:cli-handler
    9.44-   #:cli-cmd))
    9.45-(defpackage :demo-db
    9.46-  (:use))
    10.1--- a/src/tk/rs.lisp	Mon Jun 05 19:59:26 2023 -0400
    10.2+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    10.3@@ -1,78 +0,0 @@
    10.4-;;; RUST DSL
    10.5-
    10.6-;; So basically, this was born out of personal frustration with how
    10.7-;; cbindgen and Rust macros work (they don't). Rust macros in general
    10.8-;; are something of a pain in my opinion, so I thought why not just
    10.9-;; generate Rust code from Lisp instead?
   10.10-
   10.11-(in-package :demo)
   10.12-
   10.13-(defvar *cargo-target* #p"/Users/ellis/dev/otom8/demo/target/")
   10.14-(defvar *rs-macros* nil)
   10.15-
   10.16-;; TODO gensyms
   10.17-(defmacro rs-defmacro (name args &body body)
   10.18-  "Define a macro which can be used within the body of a 'with-rs' form."
   10.19-  `(prog1
   10.20-       (defmacro ,name ,@(mapcar #`(,a1) args) ,@body)
   10.21-     (push ',name *rs-macros*)))
   10.22-
   10.23-(defun rs-mod-form (crate &optional mods pub)
   10.24-  "Generate a basic mod form (CRATE . [MODS] [PUB])"
   10.25-    `(,crate ,mods ,pub))
   10.26-
   10.27-(defmacro with-rs-env (imports &body body)
   10.28-  "Generate an environment for use within a Rust generator macro."
   10.29-  `(let ((imports ,(mapcar #'rs-mod-form imports)))
   10.30-     (format nil "~A~&~A" imports ',body)))
   10.31-
   10.32-(defun rs-use (crate &optional mods pub)
   10.33-  "Generate a single Rust use statement."
   10.34-  (concatenate
   10.35-   'string
   10.36-   (if pub "pub " "")
   10.37-   "use " crate "::{"
   10.38-   (cond
   10.39-     ((consp mods)
   10.40-      (reduce
   10.41-       (lambda (x y) (format nil "~A,~A" x y))
   10.42-       mods))
   10.43-     (t mods))
   10.44-   "};"))
   10.45-
   10.46-(defun rs-mod (mod &optional pub)
   10.47-  "Generate a single Rust mod statement."
   10.48-  (concatenate
   10.49-   'string
   10.50-   (if pub "pub " "")
   10.51-   "mod " mod ";"))
   10.52-
   10.53-(defun rs-imports (&rest imports)
   10.54-  "Generate a string of Rust 'use' statements."
   10.55-  (cond
   10.56-    ((consp imports)
   10.57-     (mapcar (lambda (x) (apply #'rs-use (apply #'rs-mod-form x))) imports))
   10.58-    (t imports)))
   10.59-
   10.60-(defmacro rs-extern-c-fn (name args &optional pub unsafe no-mangle &body body)
   10.61-  "Generate a Rust extern 'C' fn."
   10.62-  `(concatenate
   10.63-   'string
   10.64-   ,(when no-mangle (format nil "#[no_mangle]~&"))
   10.65-   ,(when pub "pub ")
   10.66-   ,(when unsafe "unsafe ")
   10.67-   "extern \"C\" fn " ,name "("
   10.68-   ,(cond
   10.69-      ((consp args) (reduce (lambda (x y) (format nil "~A,~A" x y)) args))
   10.70-      (t args))
   10.71-   ")" "{" ,@body "}"))
   10.72-
   10.73-(defun rs-obj-impl (obj)
   10.74-  "Implement Objective for give OBJ."
   10.75-  (format nil "impl Objective for ~A {};" obj))
   10.76-
   10.77-;; (defun rs-macroexpand-1 (form &optional env))
   10.78-
   10.79-;; (defun rs-macroexpand (env &rest body)
   10.80-
   10.81-;;; 
    11.1--- a/src/tk/tk.lisp	Mon Jun 05 19:59:26 2023 -0400
    11.2+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    11.3@@ -1,28 +0,0 @@
    11.4-(in-package :demo)
    11.5-
    11.6-(defun mkstr (&rest args)
    11.7-  (with-output-to-string (s)
    11.8-    (dolist (a args) (princ a s))))
    11.9-
   11.10-(defun symb (&rest args)
   11.11-  (values (intern (apply #'mkstr args))))
   11.12-
   11.13-(defun random-id ()
   11.14-  (format NIL "~8,'0x-~8,'0x" (random #xFFFFFFFF) (get-universal-time)))
   11.15-
   11.16-(defun scan-dir (dir filename callback)
   11.17-  (dolist (path (directory (merge-pathnames (merge-pathnames filename "**/") dir)))
   11.18-    (funcall callback path)))
   11.19-
   11.20-(defun sbq-reader (stream sub-char numarg)
   11.21-  "The anaphoric sharp-backquote reader: #`((,a1))"
   11.22-  (declare (ignore sub-char))
   11.23-  (unless numarg (setq numarg 1))
   11.24-  `(lambda ,(loop for i from 1 to numarg
   11.25-		  collect (symb 'a i))
   11.26-     ,(funcall
   11.27-       (get-macro-character #\`) stream nil)))
   11.28-
   11.29-(eval-when (:load-toplevel)
   11.30-  (set-dispatch-macro-character
   11.31-   #\# #\` #'demo:sbq-reader))
    12.1--- a/src/ui/ui.lisp	Mon Jun 05 19:59:26 2023 -0400
    12.2+++ b/src/ui/ui.lisp	Tue Jun 06 18:55:17 2023 -0400
    12.3@@ -1,4 +1,4 @@
    12.4-(in-package :demo)
    12.5+(in-package :demo-ui)
    12.6 
    12.7 (defparameter ui-server-port 8080)
    12.8 (defparameter ui-server-host "0.0.0.0")
    13.1--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    13.2+++ b/src/utils/rs.lisp	Tue Jun 06 18:55:17 2023 -0400
    13.3@@ -0,0 +1,78 @@
    13.4+;;; RUST DSL
    13.5+
    13.6+;; So basically, this was born out of personal frustration with how
    13.7+;; cbindgen and Rust macros work (they don't). Rust macros in general
    13.8+;; are something of a pain in my opinion, so I thought why not just
    13.9+;; generate Rust code from Lisp instead?
   13.10+
   13.11+(in-package :demo-utils)
   13.12+
   13.13+(defvar *cargo-target* #p"/Users/ellis/dev/otom8/demo/target/")
   13.14+(defvar *rs-macros* nil)
   13.15+
   13.16+;; TODO gensyms
   13.17+(defmacro rs-defmacro (name args &body body)
   13.18+  "Define a macro which can be used within the body of a 'with-rs' form."
   13.19+  `(prog1
   13.20+       (defmacro ,name ,@(mapcar #`(,a1) args) ,@body)
   13.21+     (push ',name *rs-macros*)))
   13.22+
   13.23+(defun rs-mod-form (crate &optional mods pub)
   13.24+  "Generate a basic mod form (CRATE . [MODS] [PUB])"
   13.25+    `(,crate ,mods ,pub))
   13.26+
   13.27+(defmacro with-rs-env (imports &body body)
   13.28+  "Generate an environment for use within a Rust generator macro."
   13.29+  `(let ((imports ,(mapcar #'rs-mod-form imports)))
   13.30+     (format nil "~A~&~A" imports ',body)))
   13.31+
   13.32+(defun rs-use (crate &optional mods pub)
   13.33+  "Generate a single Rust use statement."
   13.34+  (concatenate
   13.35+   'string
   13.36+   (if pub "pub " "")
   13.37+   "use " crate "::{"
   13.38+   (cond
   13.39+     ((consp mods)
   13.40+      (reduce
   13.41+       (lambda (x y) (format nil "~A,~A" x y))
   13.42+       mods))
   13.43+     (t mods))
   13.44+   "};"))
   13.45+
   13.46+(defun rs-mod (mod &optional pub)
   13.47+  "Generate a single Rust mod statement."
   13.48+  (concatenate
   13.49+   'string
   13.50+   (if pub "pub " "")
   13.51+   "mod " mod ";"))
   13.52+
   13.53+(defun rs-imports (&rest imports)
   13.54+  "Generate a string of Rust 'use' statements."
   13.55+  (cond
   13.56+    ((consp imports)
   13.57+     (mapcar (lambda (x) (apply #'rs-use (apply #'rs-mod-form x))) imports))
   13.58+    (t imports)))
   13.59+
   13.60+(defmacro rs-extern-c-fn (name args &optional pub unsafe no-mangle &body body)
   13.61+  "Generate a Rust extern 'C' fn."
   13.62+  `(concatenate
   13.63+   'string
   13.64+   ,(when no-mangle (format nil "#[no_mangle]~&"))
   13.65+   ,(when pub "pub ")
   13.66+   ,(when unsafe "unsafe ")
   13.67+   "extern \"C\" fn " ,name "("
   13.68+   ,(cond
   13.69+      ((consp args) (reduce (lambda (x y) (format nil "~A,~A" x y)) args))
   13.70+      (t args))
   13.71+   ")" "{" ,@body "}"))
   13.72+
   13.73+(defun rs-obj-impl (obj)
   13.74+  "Implement Objective for give OBJ."
   13.75+  (format nil "impl Objective for ~A {};" obj))
   13.76+
   13.77+;; (defun rs-macroexpand-1 (form &optional env))
   13.78+
   13.79+;; (defun rs-macroexpand (env &rest body)
   13.80+
   13.81+;;; 
    14.1--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    14.2+++ b/src/utils/utils.lisp	Tue Jun 06 18:55:17 2023 -0400
    14.3@@ -0,0 +1,28 @@
    14.4+(in-package :demo-utils)
    14.5+
    14.6+(defun mkstr (&rest args)
    14.7+  (with-output-to-string (s)
    14.8+    (dolist (a args) (princ a s))))
    14.9+
   14.10+(defun symb (&rest args)
   14.11+  (values (intern (apply #'mkstr args))))
   14.12+
   14.13+(defun random-id ()
   14.14+  (format NIL "~8,'0x-~8,'0x" (random #xFFFFFFFF) (get-universal-time)))
   14.15+
   14.16+(defun scan-dir (dir filename callback)
   14.17+  (dolist (path (directory (merge-pathnames (merge-pathnames filename "**/") dir)))
   14.18+    (funcall callback path)))
   14.19+
   14.20+(defun sbq-reader (stream sub-char numarg)
   14.21+  "The anaphoric sharp-backquote reader: #`((,a1))"
   14.22+  (declare (ignore sub-char))
   14.23+  (unless numarg (setq numarg 1))
   14.24+  `(lambda ,(loop for i from 1 to numarg
   14.25+		  collect (symb 'a i))
   14.26+     ,(funcall
   14.27+       (get-macro-character #\`) stream nil)))
   14.28+
   14.29+(eval-when (:load-toplevel)
   14.30+  (set-dispatch-macro-character
   14.31+   #\# #\` #'sbq-reader))
    15.1--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    15.2+++ b/tools/deps.sh	Tue Jun 06 18:55:17 2023 -0400
    15.3@@ -0,0 +1,309 @@
    15.4+#!/usr/bin/sh
    15.5+# install demo build dependencies
    15.6+set -u
    15.7+PKG_URL_ROOT="${PKG_URL_ROOT:-https://rwest.io/otom8/packy/bundle}"
    15.8+PKG_NAME="demo_build_deps"
    15.9+say() {printf 'babel-installer: %s\n' "$1"}
   15.10+err() {say "$1" >&2; exit 1}
   15.11+need_cmd() {
   15.12+    if ! check_cmd "$1"; then
   15.13+        err "need '$1' (command not found)"
   15.14+    fi}
   15.15+check_cmd() {command -v "$1" > /dev/null 2>&1}
   15.16+ensure() {if ! "$@"; then err "command failed: $*"; fi}
   15.17+ignore() {"$@"}
   15.18+
   15.19+main () {
   15.20+    need_cmd chmod
   15.21+    need_cmd mkdir
   15.22+    need_cmd rm
   15.23+
   15.24+    get_architecture || return 1
   15.25+    local _arch="$RETVAL"
   15.26+    assert_nz "$_arch" "arch"
   15.27+
   15.28+    # no extension unless on windows
   15.29+    local _ext=""
   15.30+    case "$_arch" in
   15.31+        *windows*)
   15.32+            _ext=".exe"
   15.33+            ;;
   15.34+    esac
   15.35+
   15.36+  local _url="${PKG_URL_ROOT}/bin/dist/${_arch}/${PKG_NAME}${_ext}"
   15.37+
   15.38+    local _dir
   15.39+    _dir="$(ensure mktemp -d)"
   15.40+    local _file="${_dir}/${PKG_NAME}${_ext}"
   15.41+
   15.42+    local _ansi_escapes_are_valid=false
   15.43+    if [ -t 2 ]; then
   15.44+        if [ "${TERM+set}" = 'set' ]; then
   15.45+            case "$TERM" in
   15.46+                xterm*|rxvt*|urxvt*|linux*|vt*)
   15.47+                    _ansi_escapes_are_valid=true
   15.48+                ;;
   15.49+            esac
   15.50+        fi
   15.51+    fi
   15.52+
   15.53+    # check if we have to use /dev/tty to prompt the user
   15.54+    local need_tty=yes
   15.55+    for arg in "$@"; do
   15.56+        case "$arg" in
   15.57+            q)
   15.58+                # user wants to skip the prompt --
   15.59+                # we don't need /dev/tty
   15.60+                need_tty=no
   15.61+                ;;
   15.62+            *)
   15.63+                ;;
   15.64+        esac
   15.65+    done
   15.66+
   15.67+    if $_ansi_escapes_are_valid; then
   15.68+        printf "\33[1minfo:\33[0m downloading $PKG_NAME\n" 1>&2
   15.69+    else
   15.70+        printf '%s\n' 'info: downloading $PKG_NAME' 1>&2
   15.71+    fi
   15.72+
   15.73+    ensure mkdir -p "$_dir"
   15.74+    ensure downloader "$_url" "$_file" "$_arch"
   15.75+    ensure chmod u+x "$_file"
   15.76+    if [ ! -x "$_file" ]; then
   15.77+        printf '%s\n' "Cannot execute $_file (likely because of mounting /tmp as noexec)." 1>&2
   15.78+        printf '%s\n' "Please copy the file to a location where you can execute binaries and run ./${PKG_NAME}${_ext}." 1>&2
   15.79+        exit 1
   15.80+    fi
   15.81+
   15.82+    if [ "$need_tty" = "yes" ]; then
   15.83+        # The installer is going to want to ask for confirmation by
   15.84+        # reading stdin.  This script was piped into `sh` though and
   15.85+        # doesn't have stdin to pass to its children. Instead we're going
   15.86+        # to explicitly connect /dev/tty to the installer's stdin.
   15.87+        if [ ! -t 1 ]; then
   15.88+            err "Unable to run interactively. Run with -y to accept defaults"
   15.89+        fi
   15.90+
   15.91+        ignore "$_file" "$@" < /dev/tty
   15.92+    else
   15.93+        ignore "$_file" "$@"
   15.94+    fi
   15.95+
   15.96+    local _retval=$?
   15.97+
   15.98+    ignore rm "$_file"
   15.99+    ignore rmdir "$_dir"
  15.100+
  15.101+    return "$_retval"
  15.102+}
  15.103+
  15.104+dl() { # curl || wget
  15.105+    local _dld
  15.106+    local _ciphersuites
  15.107+    local _err
  15.108+    local _status
  15.109+    if check_cmd curl; then
  15.110+        _dld=curl
  15.111+    elif check_cmd wget; then
  15.112+        _dld=wget
  15.113+    else
  15.114+        _dld='curl or wget' # to be used in error message of need_cmd
  15.115+    fi
  15.116+
  15.117+    if [ "$1" = --check ]; then
  15.118+        need_cmd "$_dld"
  15.119+    elif [ "$_dld" = curl ]; then
  15.120+        get_ciphersuites_for_curl
  15.121+        _ciphersuites="$RETVAL"
  15.122+        if [ -n "$_ciphersuites" ]; then
  15.123+            _err=$(curl --proto '=https' --tlsv1.2 --ciphers "$_ciphersuites" --silent --show-error --fail --location "$1" --output "$2" 2>&1)
  15.124+            _status=$?
  15.125+        else
  15.126+            echo "Warning: Not enforcing strong cipher suites for TLS, this is potentially less secure"
  15.127+            if ! check_help_for "$3" curl --proto --tlsv1.2; then
  15.128+                echo "Warning: Not enforcing TLS v1.2, this is potentially less secure"
  15.129+                _err=$(curl --silent --show-error --fail --location "$1" --output "$2" 2>&1)
  15.130+                _status=$?
  15.131+            else
  15.132+                _err=$(curl --proto '=https' --tlsv1.2 --silent --show-error --fail --location "$1" --output "$2" 2>&1)
  15.133+                _status=$?
  15.134+            fi
  15.135+        fi
  15.136+        if [ -n "$_err" ]; then
  15.137+            echo "$_err" >&2
  15.138+            if echo "$_err" | grep -q 404$; then
  15.139+                err "installer for platform '$3' not found 8^C - ask ellis to support your platform"
  15.140+            fi
  15.141+        fi
  15.142+        return $_status
  15.143+    elif [ "$_dld" = wget ]; then
  15.144+        get_ciphersuites_for_wget
  15.145+        _ciphersuites="$RETVAL"
  15.146+        if [ -n "$_ciphersuites" ]; then
  15.147+            _err=$(wget --https-only --secure-protocol=TLSv1_2 --ciphers "$_ciphersuites" "$1" -O "$2" 2>&1)
  15.148+            _status=$?
  15.149+        else
  15.150+            echo "Warning: Not enforcing strong cipher suites for TLS, this is potentially less secure"
  15.151+            if ! check_help_for "$3" wget --https-only --secure-protocol; then
  15.152+                echo "Warning: Not enforcing TLS v1.2, this is potentially less secure"
  15.153+                _err=$(wget "$1" -O "$2" 2>&1)
  15.154+                _status=$?
  15.155+            else
  15.156+                _err=$(wget --https-only --secure-protocol=TLSv1_2 "$1" -O "$2" 2>&1)
  15.157+                _status=$?
  15.158+            fi
  15.159+        fi
  15.160+        if [ -n "$_err" ]; then
  15.161+            echo "$_err" >&2
  15.162+            if echo "$_err" | grep -q ' 404 Not Found$'; then
  15.163+                err "installer for platform '$3' not found!"
  15.164+            fi
  15.165+        fi
  15.166+        return $_status
  15.167+    else
  15.168+        err "Unknown downloader"   # should not reach here
  15.169+    fi
  15.170+}
  15.171+
  15.172+check_help_for() {
  15.173+    local _arch
  15.174+    local _cmd
  15.175+    local _arg
  15.176+    _arch="$1"
  15.177+    shift
  15.178+    _cmd="$1"
  15.179+    shift
  15.180+
  15.181+    local _category
  15.182+    if "$_cmd" --help | grep -q 'For all options use the manual or "--help all".'; then
  15.183+      _category="all"
  15.184+    else
  15.185+      _category=""
  15.186+    fi
  15.187+
  15.188+    case "$_arch" in
  15.189+
  15.190+        *darwin*)
  15.191+        if check_cmd sw_vers; then
  15.192+            case $(sw_vers -productVersion) in
  15.193+                10.*)
  15.194+                    # If we're running on macOS, older than 10.13, then we always
  15.195+                    # fail to find these options to force fallback
  15.196+                    if [ "$(sw_vers -productVersion | cut -d. -f2)" -lt 13 ]; then
  15.197+                        # Older than 10.13
  15.198+                        echo "Warning: Detected macOS platform older than 10.13"
  15.199+                        return 1
  15.200+                    fi
  15.201+                    ;;
  15.202+                11.*)
  15.203+                    # We assume Big Sur will be OK for now
  15.204+                    ;;
  15.205+                *)
  15.206+                    # Unknown product version, warn and continue
  15.207+                    echo "Warning: Detected unknown macOS major version: $(sw_vers -productVersion)"
  15.208+                    echo "Warning TLS capabilities detection may fail"
  15.209+                    ;;
  15.210+            esac
  15.211+        fi
  15.212+        ;;
  15.213+
  15.214+    esac
  15.215+
  15.216+    for _arg in "$@"; do
  15.217+        if ! "$_cmd" --help $_category | grep -q -- "$_arg"; then
  15.218+            return 1
  15.219+        fi
  15.220+    done
  15.221+
  15.222+    true # not strictly needed
  15.223+}
  15.224+
  15.225+# Return cipher suite string specified by user, otherwise return strong TLS 1.2-1.3 cipher suites
  15.226+# if support by local tools is detected. Detection currently supports these curl backends: 
  15.227+# GnuTLS and OpenSSL (possibly also LibreSSL and BoringSSL). Return value can be empty.
  15.228+get_ciphersuites_for_curl() {
  15.229+    if [ -n "${BABEL_TLS_CIPHERSUITES-}" ]; then
  15.230+        # user specified custom cipher suites, assume they know what they're doing
  15.231+        RETVAL="$BABEL_TLS_CIPHERSUITES"
  15.232+        return
  15.233+    fi
  15.234+
  15.235+    local _openssl_syntax="no"
  15.236+    local _gnutls_syntax="no"
  15.237+    local _backend_supported="yes"
  15.238+    if curl -V | grep -q ' OpenSSL/'; then
  15.239+        _openssl_syntax="yes"
  15.240+    elif curl -V | grep -iq ' LibreSSL/'; then
  15.241+        _openssl_syntax="yes"
  15.242+    elif curl -V | grep -iq ' BoringSSL/'; then
  15.243+        _openssl_syntax="yes"
  15.244+    elif curl -V | grep -iq ' GnuTLS/'; then
  15.245+        _gnutls_syntax="yes"
  15.246+    else
  15.247+        _backend_supported="no"
  15.248+    fi
  15.249+
  15.250+    local _args_supported="no"
  15.251+    if [ "$_backend_supported" = "yes" ]; then
  15.252+        # "unspecified" is for arch, allows for possibility old OS using macports, homebrew, etc.
  15.253+        if check_help_for "notspecified" "curl" "--tlsv1.2" "--ciphers" "--proto"; then
  15.254+            _args_supported="yes"
  15.255+        fi
  15.256+    fi
  15.257+
  15.258+    local _cs=""
  15.259+    if [ "$_args_supported" = "yes" ]; then
  15.260+        if [ "$_openssl_syntax" = "yes" ]; then
  15.261+            _cs=$(get_strong_ciphersuites_for "openssl")
  15.262+        elif [ "$_gnutls_syntax" = "yes" ]; then
  15.263+            _cs=$(get_strong_ciphersuites_for "gnutls")
  15.264+        fi
  15.265+    fi
  15.266+
  15.267+    RETVAL="$_cs"
  15.268+}
  15.269+
  15.270+# Return cipher suite string specified by user, otherwise return strong TLS 1.2-1.3 cipher suites
  15.271+# if support by local tools is detected. Detection currently supports these wget backends: 
  15.272+# GnuTLS and OpenSSL (possibly also LibreSSL and BoringSSL). Return value can be empty.
  15.273+get_ciphersuites_for_wget() {
  15.274+    if [ -n "${BABEL_TLS_CIPHERSUITES-}" ]; then
  15.275+        # user specified custom cipher suites, assume they know what they're doing
  15.276+        RETVAL="$BABEL_TLS_CIPHERSUITES"
  15.277+        return
  15.278+    fi
  15.279+
  15.280+    local _cs=""
  15.281+    if wget -V | grep -q '\-DHAVE_LIBSSL'; then
  15.282+        # "unspecified" is for arch, allows for possibility old OS using macports, homebrew, etc.
  15.283+        if check_help_for "notspecified" "wget" "TLSv1_2" "--ciphers" "--https-only" "--secure-protocol"; then
  15.284+            _cs=$(get_strong_ciphersuites_for "openssl")
  15.285+        fi
  15.286+    elif wget -V | grep -q '\-DHAVE_LIBGNUTLS'; then
  15.287+        # "unspecified" is for arch, allows for possibility old OS using macports, homebrew, etc.
  15.288+        if check_help_for "notspecified" "wget" "TLSv1_2" "--ciphers" "--https-only" "--secure-protocol"; then
  15.289+            _cs=$(get_strong_ciphersuites_for "gnutls")
  15.290+        fi
  15.291+    fi
  15.292+
  15.293+    RETVAL="$_cs"
  15.294+}
  15.295+
  15.296+# Return strong TLS 1.2-1.3 cipher suites in OpenSSL or GnuTLS syntax. TLS 1.2 
  15.297+# excludes non-ECDHE and non-AEAD cipher suites. DHE is excluded due to bad 
  15.298+# DH params often found on servers (see RFC 7919). Sequence matches or is
  15.299+# similar to Firefox 68 ESR with weak cipher suites disabled via about:config.  
  15.300+# $1 must be openssl or gnutls.
  15.301+get_strong_ciphersuites_for() {
  15.302+    if [ "$1" = "openssl" ]; then
  15.303+        # OpenSSL is forgiving of unknown values, no problems with TLS 1.3 values on versions that don't support it yet.
  15.304+        echo "TLS_AES_128_GCM_SHA256:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_256_GCM_SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384"
  15.305+    elif [ "$1" = "gnutls" ]; then
  15.306+        # GnuTLS isn't forgiving of unknown values, so this may require a GnuTLS version that supports TLS 1.3 even if wget doesn't.
  15.307+        # Begin with SECURE128 (and higher) then remove/add to build cipher suites. Produces same 9 cipher suites as OpenSSL but in slightly different order.
  15.308+        echo "SECURE128:-VERS-SSL3.0:-VERS-TLS1.0:-VERS-TLS1.1:-VERS-DTLS-ALL:-CIPHER-ALL:-MAC-ALL:-KX-ALL:+AEAD:+ECDHE-ECDSA:+ECDHE-RSA:+AES-128-GCM:+CHACHA20-POLY1305:+AES-256-GCM"
  15.309+    fi 
  15.310+}
  15.311+
  15.312+main "$@" || exit 1