# HG changeset patch # User ellis # Date 1685846926 14400 # Node ID ba323d8c0f939cd0be024436ba0e42c2ecabc3e9 # Parent 1059e7b52e471a9b0c572b3d0584eaceca425bb5 refactor1 diff -r 1059e7b52e47 -r ba323d8c0f93 Cargo.toml --- a/Cargo.toml Sat Jun 03 19:57:46 2023 -0400 +++ b/Cargo.toml Sat Jun 03 22:48:46 2023 -0400 @@ -1,14 +1,2 @@ -[package] -name = "demo" -version = "0.1.0" -build = "build.rs" -[lib] -path = "lib.rs" -crate-type = ["rlib","cdylib"] [workspace] -members = ["obj","ui"] -[dependencies] -libc = "0.2" -obj = {version = "0.1.0",path = "obj"} -[build-dependencies] -cbindgen = "0.24.3" \ No newline at end of file +members = ["src/crates/obj","src/crates/ui"] diff -r 1059e7b52e47 -r ba323d8c0f93 Dockerfile --- a/Dockerfile Sat Jun 03 19:57:46 2023 -0400 +++ b/Dockerfile Sat Jun 03 22:48:46 2023 -0400 @@ -1,38 +1,10 @@ -ARG ROSWELL_IMAGE=fukamachi/roswell -ARG ROSWELL_VERSION -ARG OS=debian -FROM fukamachi/roswell:latest-$OS AS build-env -ARG OS +ARG OS=archlinux ARG VERSION ADD https://github.com/sbcl/sbcl/archive/sbcl-$VERSION.tar.gz sbcl.tar.gz RUN set -x; \ - arch="$(case $(uname -m) in amd64|x86_64) echo x86-64;; aarch64) echo arm64;; *) uname -m ;; esac)"; \ - if [ "$OS" = "alpine" ]; then \ - apk add --update build-base linux-headers zstd-dev; \ - else \ - apt-get update && apt-get -y install --no-install-recommends \ - build-essential \ - zlib1g-dev \ - libzstd-dev \ - time; \ - fi; \ - ros install sbcl-bin/2.2.7 && \ tar xvfz sbcl.tar.gz && rm sbcl.tar.gz && cd "sbcl-sbcl-${VERSION}" && \ - echo "\"$VERSION\"" > version.lisp-expr && \ - (sh make.sh \ - --with-sb-core-compression \ - "--xc-host=ros -L sbcl-bin without-roswell=t --no-rc run" \ - "--prefix=$HOME/.roswell/impls/$arch/linux/sbcl-bin/$VERSION/" || true) \ - && sh install.sh && \ - rm -f "/root/.roswell/impls/$arch/linux/sbcl-bin/$VERSION/lib/sbcl/sbcl.core.old" \ - && rm -f "/root/.roswell/impls/$arch/linux/sbcl-bin/$VERSION/bin/sbcl.old" \ - && find "/root/.roswell/impls/$arch/linux/sbcl-bin" -maxdepth 1 -mindepth 1 | grep -v "/sbcl-bin/$VERSION$" | xargs rm -rf || true \ - && rm -rf "/root/.roswell/impls/log" - -FROM $ROSWELL_IMAGE:$ROSWELL_VERSION-$OS -# hadolint ignore=DL3010 -COPY --from=build-env /root/.roswell/impls /root/.roswell/impls + echo "\"$VERSION\"" > version.s && \ ARG BUILD_DATE ARG VCS_REF @@ -41,25 +13,7 @@ LABEL org.label-schema.build-date=$BUILD_DATE \ org.label-schema.vcs-ref=$VCS_REF \ - org.label-schema.vcs-url="https://github.com/fukamachi/dockerfiles" \ org.label-schema.version=$VERSION \ org.label-schema.schema-version="1.0" -# hadolint ignore=DL3018 -RUN set -x; \ - if [ "$OS" = "alpine" ]; then \ - apk add --update --no-cache zstd-libs; \ - fi; \ - printf "setup.time\t0\t%s\n" "$(( $(date +%s) + 2208988800 ))" > ~/.roswell/config && \ - printf "sbcl-bin.version\t0\t%s\n" "$VERSION" >> ~/.roswell/config && \ - printf "default.lisp\t0\tsbcl-bin\n" >> ~/.roswell/config && \ - ros setup && \ - ros -e '(mapc (function ql-dist:uninstall) (ql-dist:installed-releases t))' \ - && rm -f /root/.roswell/lisp/quicklisp/tmp/quicklisp.tar \ - && rm -rf /root/.roswell/archives/* /root/.roswell/src/sbcl-* /root/.cache/common-lisp/sbcl-*/root/.roswell/lisp/quicklisp/dists/quicklisp/software - -RUN set -x; \ - printf '#!/bin/sh\nexec ros run -- "$@"\n' > /usr/local/bin/sbcl \ - && chmod u+x /usr/local/bin/sbcl - ENTRYPOINT ["/usr/local/bin/sbcl"] \ No newline at end of file diff -r 1059e7b52e47 -r ba323d8c0f93 build.rs --- a/build.rs Sat Jun 03 19:57:46 2023 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,17 +0,0 @@ -use std::env; -use std::fs::create_dir; -use std::path::PathBuf; -fn main() { - let crate_dir: PathBuf = env::var("CARGO_MANIFEST_DIR") - .expect("CARGO_MANIFEST_DIR env var is not defined") - .into(); - // let mpk_py = "build.py"; - let build_dir = crate_dir.join("ffi/"); - if !build_dir.exists() { - create_dir(&build_dir).unwrap(); - } - cbindgen::generate(crate_dir) - .expect("Unable to find cbindgen.toml configuration file") - .write_to_file(build_dir.join("demo.h")); - -} diff -r 1059e7b52e47 -r ba323d8c0f93 cbindgen.toml --- a/cbindgen.toml Sat Jun 03 19:57:46 2023 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,14 +0,0 @@ -include_guard = "demo_h" -autogen_warning = "/* DO NOT TOUCH */" -include_version = true -language = "C" -cpp_compat = true -line_length = 88 -documentation = true -[parse] -parse_deps = true -include = ["obj","fig","libc"] -extra_bindings = ["obj","fig","libc"] -#expand = ["demo","obj"] -#[parse.expand] -#crates = ["demo"] \ No newline at end of file diff -r 1059e7b52e47 -r ba323d8c0f93 cfg.lisp diff -r 1059e7b52e47 -r ba323d8c0f93 db.lisp --- a/db.lisp Sat Jun 03 19:57:46 2023 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,197 +0,0 @@ -(in-package :demo) - -(define-foreign-library rocksdb - (:win32 "rocksdb") - (t (:default "librocksdb"))) - -(use-foreign-library rocksdb) - -(defcfun ("rocksdb_options_create" create-options) :pointer) -(defcfun ("rocksdb_options_destroy" destroy-options) :void (options :pointer)) -(defcfun ("rocksdb_options_increase_parallelism" increase-parallelism) :void (opt :pointer) (total-threads :int)) -(defcfun ("rocksdb_options_optimize_level_style_compaction" optimize-level-style-compaction) :void (opt :pointer) (memtable_memory_budget :uint64)) -(defcfun ("rocksdb_options_set_create_if_missing" set-create-if-missing) :void (opt :pointer) (val :boolean)) - -(defcfun ("rocksdb_writeoptions_create" create-writeoptions) :pointer) -(defcfun ("rocksdb_writeoptions_destroy" destroy-writeoptions) :void (opt :pointer)) -(defcfun ("rocksdb_readoptions_create" create-readoptions) :pointer) -(defcfun ("rocksdb_readoptions_destroy" destroy-readoptions) :void (opt :pointer)) - -(defcfun ("rocksdb_open" open-db*) :pointer (opt :pointer) (name :string) (errptr :pointer)) -(defcfun ("rocksdb_close" close-db) :void (opt :pointer)) -(defcfun ("rocksdb_cancel_all_background_work" cancel-all-background-work) :void (db :pointer) (wait :boolean)) - -(defcfun ("rocksdb_put" put*) :void (db :pointer) (options :pointer) (key :pointer) (keylen :unsigned-int) (val :pointer) (vallen :unsigned-int) (errptr :pointer)) -(defcfun ("rocksdb_get" get*) :pointer (db :pointer) (options :pointer) (key :pointer) (keylen :unsigned-int) (vallen :pointer) (errptr :pointer)) - -(defcfun ("rocksdb_create_iterator" create-iter*) :pointer (db :pointer) (opt :pointer)) -(defcfun ("rocksdb_iter_destroy" destroy-iter) :void (iter :pointer)) -(defcfun ("rocksdb_iter_seek_to_first" move-iter-to-first) :void (iter :pointer)) -(defcfun ("rocksdb_iter_valid" valid-iter-p) :boolean (iter :pointer)) -(defcfun ("rocksdb_iter_next" move-iter-forward) :void (iter :pointer)) -(defcfun ("rocksdb_iter_prev" move-iter-backward) :void (iter :pointer)) -(defcfun ("rocksdb_iter_key" iter-key*) :pointer (iter :pointer) (klen-ptr :pointer)) -(defcfun ("rocksdb_iter_value" iter-value*) :pointer (iter :pointer) (vlen-ptr :pointer)) - -(define-condition unable-to-open-db (error) - ((db-path :initarg :db-path - :reader db-path) - (error-message :initarg :error-message - :reader error-message))) - -(defmethod print-object ((obj unable-to-open-db) stream) - (print-unreadable-object (obj stream :type t :identity t) - (format stream "error-message=~A" (error-message obj)))) - -(define-condition unable-to-put-key-value-to-db (error) - ((db :initarg :db - :reader db) - (key :initarg :key - :reader key) - (val :initarg :val - :reader val) - (error-message :initarg :error-message - :reader error-message))) - -(define-condition unable-to-get-value-to-db (error) - ((db :initarg :db - :reader db) - (key :initarg :key - :reader key) - (error-message :initarg :error-message - :reader error-message))) - -(defun open-db (db-path &optional opt) - (unless opt - (setq opt (create-options))) - (let ((errptr (foreign-alloc :pointer))) - (setf (mem-ref errptr :pointer) (null-pointer)) - (let* ((db-path (if (pathnamep db-path) - (namestring db-path) - db-path)) - (db (open-db* opt db-path errptr)) - (err (mem-ref errptr :pointer))) - (unless (null-pointer-p err) - (error 'unable-to-open-db - :db-path db-path - :error-message (foreign-string-to-lisp err))) - db))) - -(defmacro clone-octets-to-foreign (lisp-array foreign-array) - (let ((i (gensym))) - `(loop for ,i from 0 below (length ,lisp-array) - do (setf (mem-aref ,foreign-array :unsigned-char ,i) - (aref ,lisp-array ,i))))) - -(defmacro clone-octets-from-foreign (foreign-array lisp-array len) - (let ((i (gensym))) - `(loop for ,i from 0 below ,len - do (setf (aref ,lisp-array ,i) - (mem-aref ,foreign-array :unsigned-char ,i))))) - -(defun put-kv (db key val &optional opt) - (unless opt - (setq opt (create-writeoptions))) - (with-foreign-objects ((errptr :pointer) - (key* :unsigned-char (length key)) - (val* :unsigned-char (length val))) - (clone-octets-to-foreign key key*) - (clone-octets-to-foreign val val*) - (setf (mem-ref errptr :pointer) (null-pointer)) - (put* db - opt - key* - (length key) - val* - (length val) - errptr) - (let ((err (mem-ref errptr :pointer))) - (unless (null-pointer-p err) - (error 'unable-to-put-key-value-to-db - :db db - :key key - :val val - :error-message (foreign-string-to-lisp err)))))) - -(defun put-kv-str (db key val &optional opt) - (let ((key-octets (babel:string-to-octets key)) - (val-octets (babel:string-to-octets val))) - (put-kv db key-octets val-octets opt))) - -(defun get-kv (db key &optional opt) - (unless opt - (setq opt (create-readoptions))) - - (with-foreign-objects ((val-len-ptr :unsigned-int) - (errptr :pointer) - (key* :unsigned-char (length key))) - (clone-octets-to-foreign key key*) - (setf (mem-ref errptr :pointer) (null-pointer)) - (let ((val (get* db - opt - key* - (length key) - val-len-ptr - errptr))) - (let ((err (mem-ref errptr :pointer))) - (unless (null-pointer-p err) - (error 'unable-to-get-value-to-db - :db db - :key key - :error-message (foreign-string-to-lisp err))) - - (unless (null-pointer-p val) - (let* ((val-len (mem-ref val-len-ptr :unsigned-int)) - (val* (make-array val-len - :element-type '(unsigned-byte 8)))) - (clone-octets-from-foreign val val* val-len) - val*)))))) - -(defun get-kv-str (db key &optional opt) - (let ((key-octets (babel:string-to-octets key))) - (let ((#1=val-octets (get-kv db key-octets opt))) - (when #1# - (babel:octets-to-string #1#))))) - -(defun create-iter (db &optional opt) - (unless opt - (setq opt (create-readoptions))) - (create-iter* db opt)) - -(defun iter-key (iter) - (with-foreign-objects ((klen-ptr :unsigned-int)) - (setf (mem-ref klen-ptr :unsigned-int) 0) - (let* ((key-ptr (iter-key* iter klen-ptr)) - (klen (mem-ref klen-ptr :unsigned-int)) - (key (make-array klen :element-type '(unsigned-byte 8)))) - (clone-octets-from-foreign key-ptr key klen) - key))) - -(defun iter-key-str (iter) - (let ((#1=key-octets (iter-key iter))) - (when #1# - (babel:octets-to-string #1#)))) - -(defun iter-value (iter) - (with-foreign-objects ((len-ptr :unsigned-int)) - (setf (mem-ref len-ptr :unsigned-int) 0) - (let* ((value-ptr (iter-value* iter len-ptr)) - (vlen (mem-ref len-ptr :unsigned-int)) - (value* (make-array vlen :element-type '(unsigned-byte 8)))) - (clone-octets-from-foreign value-ptr value* vlen) - value*))) - -(defun iter-value-str (iter) - (let ((#1=val-octets (iter-value iter))) - (when #1# - (babel:octets-to-string #1#)))) - -(defmacro with-open-db ((db-var db-path &optional opt) &body body) - `(let ((,db-var (open-db ,db-path ,opt))) - (unwind-protect (progn ,@body) - (close-db ,db-var)))) - -(defmacro with-iter ((iter-var db &optional opt) &body body) - `(let ((,iter-var (create-iter ,db ,opt))) - (unwind-protect (progn ,@body) - (destroy-iter ,iter-var)))) diff -r 1059e7b52e47 -r ba323d8c0f93 default.cfg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/default.cfg Sat Jun 03 22:48:46 2023 -0400 @@ -0,0 +1,11 @@ +;;; default-config.se --- Default demo configuration -*- mode: lisp-data -*- + +;; The configuration for this demo is specified in S-Expressions. +;; Check 'docs/config.org' for available options. +(:service "weather" + :host "localhost" + :port 8888 + :client (:name "guest" + :type "docker" + :mode "release" + :theme "dark")) diff -r 1059e7b52e47 -r ba323d8c0f93 demo.asd --- a/demo.asd Sat Jun 03 19:57:46 2023 -0400 +++ b/demo.asd Sat Jun 03 22:48:46 2023 -0400 @@ -31,13 +31,8 @@ :build-pathname "demo" :entry-point "demo:main") -;; (asdf:defsystem "cl-demo:tests" - ;; :depends-on ("cl-demo" "fiveam") +;; (asdf:defsystem "demo.tests" + ;; :depends-on ("demo" "fiveam") ;; :components ((:file "tests")) - ;; :perform (test-op (o c) (symbol-call :fiveam '#:run! :cl-demo)) + ;; :perform (test-op (o c) (symbol-call :fiveam '#:run! "demo:main")) ;; ) - -;; (deploy:define-library cl+ssl::libssl -;; :path "/usr/local/Cellar/openssl@3/3.1.1/lib/libssl.dylib") -;; (deploy:define-library cl+ssl::libcrypto -;; :path "/usr/local/Cellar/openssl@3/3.1.1/lib/libcrypto.dylib") diff -r 1059e7b52e47 -r ba323d8c0f93 demo.lisp --- a/demo.lisp Sat Jun 03 19:57:46 2023 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,45 +0,0 @@ -;; demo.lisp -(in-package :demo) - -(defparameter demo-path (merge-pathnames "cl-demo" (uiop:temporary-directory))) - -(defvar db-path (merge-pathnames "db" demo-path)) - -(defun cli-opts () - "Returns the top-level CLI options." - (list - (cli:make-option - :string - :description "demo app to run" - :short-name #\x - :long-name "app" - :initial-value "client" - :env-vars '("DEMO_APP") - :key :app) - (cli:make-option - :string - :description "path to config" - :short-name #\c - :long-name "config" - :initial-value "$DEMO_PATH/.fig" - :env-vars '("DEMO_CONFIG")))) - -(defun cli-handler (cmd) - "Handler for the `demo' command." - (let ((app (cli:getopt cmd :app))) - (format t "running: ~A!~%" app))) - -(defun cli-cmd () - "Our demo command." - (cli:make-command - :name "demo" - :description "A collection of demos" - :version "1.0.0" - :authors '("ellis ") - :license "WTFPL" - :options (cli-opts) - :handler #'cli-handler)) - -(defun main () - "A demo of some common-lisp functionality." - (cli:run (cli-cmd))) diff -r 1059e7b52e47 -r ba323d8c0f93 ffi.lisp --- a/ffi.lisp Sat Jun 03 19:57:46 2023 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,28 +0,0 @@ -(in-package :demo) -(defparameter quiche-lib-path #p"./ffi/libquiche.dylib") -;;(defparameter rocksdb-lib-path #p"./ffi/librocksdb.dylib") -(defparameter demo-lib-path (find-rs-cdylib "libdemo.dylib")) -(defmacro find-rs-cdylib (name &optional debug) - "Find the rust dll specified by NAME." - (cond - ((uiop:directory-exists-p (merge-pathnames *cargo-target* "release")) - `,(mkstr "./target/release/" name)) - ((uiop:directory-exists-p (merge-pathnames *cargo-target* "debug")) - `,(mkstr "./target/debug/" name)) - (t `(progn - ,(uiop:run-program '("cargo" "build" (unless debug "--release")) :output t) - (find-rs-cdylib ,name ,debug))))) - -(define-foreign-library demo - (:win32 (:default "demo")) - (t (:default "libdemo"))) -(define-foreign-library quiche - (:win32 (:default "quiche")) - (t (:default "libquiche"))) -;; (define-foreign-library rocksdb -;; (:win32 (:default "rocksdb")) -;; (t (:default "librocksdb"))) - -(defun load-libdemo () (load-foreign-library (find-rs-cdylib "libdemo.dylib"))) -(defun install-quiche-lib (&optional path) (load-foreign-library (or path quiche-lib-path))) -;; (defun install-rocksdb-lib (&optional path) (load-foreign-library (or path rocksdb-lib-path))) diff -r 1059e7b52e47 -r ba323d8c0f93 ffi/build.py --- a/ffi/build.py Sat Jun 03 19:57:46 2023 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,48 +0,0 @@ -try: - from cffi import FFI -except ImportError: - print("pip install cffi, included with PyPy") - -import os -import re -import pathlib - - -def parse_header(header): - h = open(header, "r").read().lstrip() - cdef = re.sub( - r"^(#|\s*\/*\*|extern).*[\r\n]|.*\"C\"$|^(?:[\t ]*(?:\r?\n|\r))+", - "", - h, - flags=re.MULTILINE, - ) - return cdef - - -def init_ffi(cdef): - ffi = FFI() - ffi.set_source( - "_demo", - """ - #include "demo.h" - """, -# libraries=["demo"], - library_dirs=["."], - include_dirs=["."], - ) - - ffi.cdef(cdef) - - return ffi - - -def compile(ffi, lib_dir, v): - os.environ["LD_RUN_PATH"] = os.path.abspath(lib_dir) - ffi.compile(verbose=v) - - -if __name__ == "__main__": - build_dir = pathlib.Path(__file__).parent - cdef = parse_header(build_dir / "demo.h") - print(cdef) - compile(init_ffi(cdef), build_dir, True) diff -r 1059e7b52e47 -r ba323d8c0f93 ffi/demo.h --- a/ffi/demo.h Sat Jun 03 19:57:46 2023 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,66 +0,0 @@ -#ifndef demo_h -#define demo_h - -/* Generated with cbindgen:0.24.3 */ - -/* DO NOT TOUCH */ - -#include -#include -#include -#include - -#define KEY_LEN 32 - -#define OUT_LEN 32 - -#define OUT_LEN_HEX (OUT_LEN * 2) - -typedef struct CustomService CustomService; - -/** - * APPLICATION TYPES - */ -typedef struct Service Service; - -#ifdef __cplusplus -extern "C" { -#endif // __cplusplus - -void free_service(struct Service *ptr); - -struct Service *service_from_string(const char *ptr); - -struct Service *service_from_json_string(const char *ptr); - -char *service_to_json_string(const struct Service *ptr); - -struct Service *service_from_ron_string(const char *ptr); - -char *service_to_ron_string(const struct Service *ptr); - -struct Service *service_decode(const uint8_t *ptr, size_t len); - -uint8_t *service_encode(const struct Service *ptr); - -void free_custom_service(struct CustomService *ptr); - -struct CustomService *custom_service_from_string(const char *ptr); - -struct CustomService *custom_service_from_json_string(const char *ptr); - -char *custom_service_to_json_string(const struct CustomService *ptr); - -struct CustomService *custom_service_from_ron_string(const char *ptr); - -char *custom_service_to_ron_string(const struct CustomService *ptr); - -struct CustomService *custom_service_decode(const uint8_t *ptr, size_t len); - -uint8_t *custom_service_encode(const struct CustomService *ptr); - -#ifdef __cplusplus -} // extern "C" -#endif // __cplusplus - -#endif /* demo_h */ diff -r 1059e7b52e47 -r ba323d8c0f93 ffi/quiche.h --- a/ffi/quiche.h Sat Jun 03 19:57:46 2023 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,973 +0,0 @@ -// Copyright (C) 2018-2019, Cloudflare, Inc. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, -// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR -// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#ifndef QUICHE_H -#define QUICHE_H - -#if defined(__cplusplus) -extern "C" { -#endif - -#include -#include -#include - -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) -#include -#include -#include -#else -#include -#include -#endif - -#ifdef __unix__ -#include -#endif -#ifdef _MSC_VER -#include -#define ssize_t SSIZE_T -#endif - -// QUIC transport API. -// - -// The current QUIC wire version. -#define QUICHE_PROTOCOL_VERSION 0x00000001 - -// The maximum length of a connection ID. -#define QUICHE_MAX_CONN_ID_LEN 20 - -// The minimum length of Initial packets sent by a client. -#define QUICHE_MIN_CLIENT_INITIAL_LEN 1200 - -enum quiche_error { - // There is no more work to do. - QUICHE_ERR_DONE = -1, - - // The provided buffer is too short. - QUICHE_ERR_BUFFER_TOO_SHORT = -2, - - // The provided packet cannot be parsed because its version is unknown. - QUICHE_ERR_UNKNOWN_VERSION = -3, - - // The provided packet cannot be parsed because it contains an invalid - // frame. - QUICHE_ERR_INVALID_FRAME = -4, - - // The provided packet cannot be parsed. - QUICHE_ERR_INVALID_PACKET = -5, - - // The operation cannot be completed because the connection is in an - // invalid state. - QUICHE_ERR_INVALID_STATE = -6, - - // The operation cannot be completed because the stream is in an - // invalid state. - QUICHE_ERR_INVALID_STREAM_STATE = -7, - - // The peer's transport params cannot be parsed. - QUICHE_ERR_INVALID_TRANSPORT_PARAM = -8, - - // A cryptographic operation failed. - QUICHE_ERR_CRYPTO_FAIL = -9, - - // The TLS handshake failed. - QUICHE_ERR_TLS_FAIL = -10, - - // The peer violated the local flow control limits. - QUICHE_ERR_FLOW_CONTROL = -11, - - // The peer violated the local stream limits. - QUICHE_ERR_STREAM_LIMIT = -12, - - // The specified stream was stopped by the peer. - QUICHE_ERR_STREAM_STOPPED = -15, - - // The specified stream was reset by the peer. - QUICHE_ERR_STREAM_RESET = -16, - - // The received data exceeds the stream's final size. - QUICHE_ERR_FINAL_SIZE = -13, - - // Error in congestion control. - QUICHE_ERR_CONGESTION_CONTROL = -14, - - // Too many identifiers were provided. - QUICHE_ERR_ID_LIMIT = -17, - - // Not enough available identifiers. - QUICHE_ERR_OUT_OF_IDENTIFIERS = -18, - - // Error in key update. - QUICHE_ERR_KEY_UPDATE = -19, -}; - -// Returns a human readable string with the quiche version number. -const char *quiche_version(void); - -// Enables logging. |cb| will be called with log messages -int quiche_enable_debug_logging(void (*cb)(const char *line, void *argp), - void *argp); - -// Stores configuration shared between multiple connections. -typedef struct quiche_config quiche_config; - -// Creates a config object with the given version. -quiche_config *quiche_config_new(uint32_t version); - -// Configures the given certificate chain. -int quiche_config_load_cert_chain_from_pem_file(quiche_config *config, - const char *path); - -// Configures the given private key. -int quiche_config_load_priv_key_from_pem_file(quiche_config *config, - const char *path); - -// Specifies a file where trusted CA certificates are stored for the purposes of certificate verification. -int quiche_config_load_verify_locations_from_file(quiche_config *config, - const char *path); - -// Specifies a directory where trusted CA certificates are stored for the purposes of certificate verification. -int quiche_config_load_verify_locations_from_directory(quiche_config *config, - const char *path); - -// Configures whether to verify the peer's certificate. -void quiche_config_verify_peer(quiche_config *config, bool v); - -// Configures whether to send GREASE. -void quiche_config_grease(quiche_config *config, bool v); - -// Enables logging of secrets. -void quiche_config_log_keys(quiche_config *config); - -// Enables sending or receiving early data. -void quiche_config_enable_early_data(quiche_config *config); - -// Configures the list of supported application protocols. -int quiche_config_set_application_protos(quiche_config *config, - const uint8_t *protos, - size_t protos_len); - -// Sets the `max_idle_timeout` transport parameter, in milliseconds, default is -// no timeout. -void quiche_config_set_max_idle_timeout(quiche_config *config, uint64_t v); - -// Sets the `max_udp_payload_size transport` parameter. -void quiche_config_set_max_recv_udp_payload_size(quiche_config *config, size_t v); - -// Sets the maximum outgoing UDP payload size. -void quiche_config_set_max_send_udp_payload_size(quiche_config *config, size_t v); - -// Sets the `initial_max_data` transport parameter. -void quiche_config_set_initial_max_data(quiche_config *config, uint64_t v); - -// Sets the `initial_max_stream_data_bidi_local` transport parameter. -void quiche_config_set_initial_max_stream_data_bidi_local(quiche_config *config, uint64_t v); - -// Sets the `initial_max_stream_data_bidi_remote` transport parameter. -void quiche_config_set_initial_max_stream_data_bidi_remote(quiche_config *config, uint64_t v); - -// Sets the `initial_max_stream_data_uni` transport parameter. -void quiche_config_set_initial_max_stream_data_uni(quiche_config *config, uint64_t v); - -// Sets the `initial_max_streams_bidi` transport parameter. -void quiche_config_set_initial_max_streams_bidi(quiche_config *config, uint64_t v); - -// Sets the `initial_max_streams_uni` transport parameter. -void quiche_config_set_initial_max_streams_uni(quiche_config *config, uint64_t v); - -// Sets the `ack_delay_exponent` transport parameter. -void quiche_config_set_ack_delay_exponent(quiche_config *config, uint64_t v); - -// Sets the `max_ack_delay` transport parameter. -void quiche_config_set_max_ack_delay(quiche_config *config, uint64_t v); - -// Sets the `disable_active_migration` transport parameter. -void quiche_config_set_disable_active_migration(quiche_config *config, bool v); - -enum quiche_cc_algorithm { - QUICHE_CC_RENO = 0, - QUICHE_CC_CUBIC = 1, - QUICHE_CC_BBR = 2, -}; - -// Sets the congestion control algorithm used. -void quiche_config_set_cc_algorithm(quiche_config *config, enum quiche_cc_algorithm algo); - -// Configures whether to use HyStart++. -void quiche_config_enable_hystart(quiche_config *config, bool v); - -// Configures whether to enable pacing (enabled by default). -void quiche_config_enable_pacing(quiche_config *config, bool v); - -// Configures max pacing rate to be used. -void quiche_config_set_max_pacing_rate(quiche_config *config, uint64_t v); - -// Configures whether to enable receiving DATAGRAM frames. -void quiche_config_enable_dgram(quiche_config *config, bool enabled, - size_t recv_queue_len, - size_t send_queue_len); - -// Sets the maximum connection window. -void quiche_config_set_max_connection_window(quiche_config *config, uint64_t v); - -// Sets the maximum stream window. -void quiche_config_set_max_stream_window(quiche_config *config, uint64_t v); - -// Sets the limit of active connection IDs. -void quiche_config_set_active_connection_id_limit(quiche_config *config, uint64_t v); - -// Sets the initial stateless reset token. |v| must contain 16 bytes, otherwise the behaviour is undefined. -void quiche_config_set_stateless_reset_token(quiche_config *config, const uint8_t *v); - -// Frees the config object. -void quiche_config_free(quiche_config *config); - -// Extracts version, type, source / destination connection ID and address -// verification token from the packet in |buf|. -int quiche_header_info(const uint8_t *buf, size_t buf_len, size_t dcil, - uint32_t *version, uint8_t *type, - uint8_t *scid, size_t *scid_len, - uint8_t *dcid, size_t *dcid_len, - uint8_t *token, size_t *token_len); - -// A QUIC connection. -typedef struct quiche_conn quiche_conn; - -// Creates a new server-side connection. -quiche_conn *quiche_accept(const uint8_t *scid, size_t scid_len, - const uint8_t *odcid, size_t odcid_len, - const struct sockaddr *local, size_t local_len, - const struct sockaddr *peer, size_t peer_len, - quiche_config *config); - -// Creates a new client-side connection. -quiche_conn *quiche_connect(const char *server_name, - const uint8_t *scid, size_t scid_len, - const struct sockaddr *local, size_t local_len, - const struct sockaddr *peer, size_t peer_len, - quiche_config *config); - -// Writes a version negotiation packet. -ssize_t quiche_negotiate_version(const uint8_t *scid, size_t scid_len, - const uint8_t *dcid, size_t dcid_len, - uint8_t *out, size_t out_len); - -// Writes a retry packet. -ssize_t quiche_retry(const uint8_t *scid, size_t scid_len, - const uint8_t *dcid, size_t dcid_len, - const uint8_t *new_scid, size_t new_scid_len, - const uint8_t *token, size_t token_len, - uint32_t version, uint8_t *out, size_t out_len); - -// Returns true if the given protocol version is supported. -bool quiche_version_is_supported(uint32_t version); - -quiche_conn *quiche_conn_new_with_tls(const uint8_t *scid, size_t scid_len, - const uint8_t *odcid, size_t odcid_len, - const struct sockaddr *local, size_t local_len, - const struct sockaddr *peer, size_t peer_len, - quiche_config *config, void *ssl, - bool is_server); - -// Enables keylog to the specified file path. Returns true on success. -bool quiche_conn_set_keylog_path(quiche_conn *conn, const char *path); - -// Enables keylog to the specified file descriptor. Unix only. -void quiche_conn_set_keylog_fd(quiche_conn *conn, int fd); - -// Enables qlog to the specified file path. Returns true on success. -bool quiche_conn_set_qlog_path(quiche_conn *conn, const char *path, - const char *log_title, const char *log_desc); - -// Enables qlog to the specified file descriptor. Unix only. -void quiche_conn_set_qlog_fd(quiche_conn *conn, int fd, const char *log_title, - const char *log_desc); - -// Configures the given session for resumption. -int quiche_conn_set_session(quiche_conn *conn, const uint8_t *buf, size_t buf_len); - -typedef struct { - // The remote address the packet was received from. - struct sockaddr *from; - socklen_t from_len; - - // The local address the packet was received on. - struct sockaddr *to; - socklen_t to_len; -} quiche_recv_info; - -// Processes QUIC packets received from the peer. -ssize_t quiche_conn_recv(quiche_conn *conn, uint8_t *buf, size_t buf_len, - const quiche_recv_info *info); - -typedef struct { - // The local address the packet should be sent from. - struct sockaddr_storage from; - socklen_t from_len; - - // The remote address the packet should be sent to. - struct sockaddr_storage to; - socklen_t to_len; - - // The time to send the packet out. - struct timespec at; -} quiche_send_info; - -// Writes a single QUIC packet to be sent to the peer. -ssize_t quiche_conn_send(quiche_conn *conn, uint8_t *out, size_t out_len, - quiche_send_info *out_info); - -// Returns the size of the send quantum, in bytes. -size_t quiche_conn_send_quantum(const quiche_conn *conn); - -// Reads contiguous data from a stream. -ssize_t quiche_conn_stream_recv(quiche_conn *conn, uint64_t stream_id, - uint8_t *out, size_t buf_len, bool *fin); - -// Writes data to a stream. -ssize_t quiche_conn_stream_send(quiche_conn *conn, uint64_t stream_id, - const uint8_t *buf, size_t buf_len, bool fin); - -// The side of the stream to be shut down. -enum quiche_shutdown { - QUICHE_SHUTDOWN_READ = 0, - QUICHE_SHUTDOWN_WRITE = 1, -}; - -// Sets the priority for a stream. -int quiche_conn_stream_priority(quiche_conn *conn, uint64_t stream_id, - uint8_t urgency, bool incremental); - -// Shuts down reading or writing from/to the specified stream. -int quiche_conn_stream_shutdown(quiche_conn *conn, uint64_t stream_id, - enum quiche_shutdown direction, uint64_t err); - -// Returns the stream's send capacity in bytes. -ssize_t quiche_conn_stream_capacity(const quiche_conn *conn, uint64_t stream_id); - -// Returns true if the stream has data that can be read. -bool quiche_conn_stream_readable(const quiche_conn *conn, uint64_t stream_id); - -// Returns the next stream that has data to read, or -1 if no such stream is -// available. -int64_t quiche_conn_stream_readable_next(quiche_conn *conn); - -// Returns true if the stream has enough send capacity. -// -// On error a value lower than 0 is returned. -int quiche_conn_stream_writable(quiche_conn *conn, uint64_t stream_id, size_t len); - -// Returns the next stream that can be written to, or -1 if no such stream is -// available. -int64_t quiche_conn_stream_writable_next(quiche_conn *conn); - -// Returns true if all the data has been read from the specified stream. -bool quiche_conn_stream_finished(const quiche_conn *conn, uint64_t stream_id); - -typedef struct quiche_stream_iter quiche_stream_iter; - -// Returns an iterator over streams that have outstanding data to read. -quiche_stream_iter *quiche_conn_readable(const quiche_conn *conn); - -// Returns an iterator over streams that can be written to. -quiche_stream_iter *quiche_conn_writable(const quiche_conn *conn); - -// Returns the maximum possible size of egress UDP payloads. -size_t quiche_conn_max_send_udp_payload_size(const quiche_conn *conn); - -// Returns the amount of time until the next timeout event, in nanoseconds. -uint64_t quiche_conn_timeout_as_nanos(const quiche_conn *conn); - -// Returns the amount of time until the next timeout event, in milliseconds. -uint64_t quiche_conn_timeout_as_millis(const quiche_conn *conn); - -// Processes a timeout event. -void quiche_conn_on_timeout(quiche_conn *conn); - -// Closes the connection with the given error and reason. -int quiche_conn_close(quiche_conn *conn, bool app, uint64_t err, - const uint8_t *reason, size_t reason_len); - -// Returns a string uniquely representing the connection. -void quiche_conn_trace_id(const quiche_conn *conn, const uint8_t **out, size_t *out_len); - -// Returns the source connection ID. -void quiche_conn_source_id(const quiche_conn *conn, const uint8_t **out, size_t *out_len); - -// Returns the destination connection ID. -void quiche_conn_destination_id(const quiche_conn *conn, const uint8_t **out, size_t *out_len); - -// Returns the negotiated ALPN protocol. -void quiche_conn_application_proto(const quiche_conn *conn, const uint8_t **out, - size_t *out_len); - -// Returns the peer's leaf certificate (if any) as a DER-encoded buffer. -void quiche_conn_peer_cert(const quiche_conn *conn, const uint8_t **out, size_t *out_len); - -// Returns the serialized cryptographic session for the connection. -void quiche_conn_session(const quiche_conn *conn, const uint8_t **out, size_t *out_len); - -// Returns true if the connection handshake is complete. -bool quiche_conn_is_established(const quiche_conn *conn); - -// Returns true if the connection has a pending handshake that has progressed -// enough to send or receive early data. -bool quiche_conn_is_in_early_data(const quiche_conn *conn); - -// Returns whether there is stream or DATAGRAM data available to read. -bool quiche_conn_is_readable(const quiche_conn *conn); - -// Returns true if the connection is draining. -bool quiche_conn_is_draining(const quiche_conn *conn); - -// Returns the number of bidirectional streams that can be created -// before the peer's stream count limit is reached. -uint64_t quiche_conn_peer_streams_left_bidi(const quiche_conn *conn); - -// Returns the number of unidirectional streams that can be created -// before the peer's stream count limit is reached. -uint64_t quiche_conn_peer_streams_left_uni(const quiche_conn *conn); - -// Returns true if the connection is closed. -bool quiche_conn_is_closed(const quiche_conn *conn); - -// Returns true if the connection was closed due to the idle timeout. -bool quiche_conn_is_timed_out(const quiche_conn *conn); - -// Returns true if a connection error was received, and updates the provided -// parameters accordingly. -bool quiche_conn_peer_error(const quiche_conn *conn, - bool *is_app, - uint64_t *error_code, - const uint8_t **reason, - size_t *reason_len); - -// Returns true if a connection error was queued or sent, and updates the provided -// parameters accordingly. -bool quiche_conn_local_error(const quiche_conn *conn, - bool *is_app, - uint64_t *error_code, - const uint8_t **reason, - size_t *reason_len); - -// Initializes the stream's application data. -// -// Stream data can only be initialized once. Additional calls to this method -// will fail. -// -// Note that the application is responsible for freeing the data. -int quiche_conn_stream_init_application_data(quiche_conn *conn, - uint64_t stream_id, - void *data); - -// Returns the stream's application data, if any was initialized. -void *quiche_conn_stream_application_data(quiche_conn *conn, uint64_t stream_id); - -// Fetches the next stream from the given iterator. Returns false if there are -// no more elements in the iterator. -bool quiche_stream_iter_next(quiche_stream_iter *iter, uint64_t *stream_id); - -// Frees the given stream iterator object. -void quiche_stream_iter_free(quiche_stream_iter *iter); - -typedef struct { - // The number of QUIC packets received on this connection. - size_t recv; - - // The number of QUIC packets sent on this connection. - size_t sent; - - // The number of QUIC packets that were lost. - size_t lost; - - // The number of sent QUIC packets with retransmitted data. - size_t retrans; - - // The number of sent bytes. - uint64_t sent_bytes; - - // The number of received bytes. - uint64_t recv_bytes; - - // The number of bytes lost. - uint64_t lost_bytes; - - // The number of stream bytes retransmitted. - uint64_t stream_retrans_bytes; - - // The number of known paths for the connection. - size_t paths_count; - - // The maximum idle timeout. - uint64_t peer_max_idle_timeout; - - // The maximum UDP payload size. - uint64_t peer_max_udp_payload_size; - - // The initial flow control maximum data for the connection. - uint64_t peer_initial_max_data; - - // The initial flow control maximum data for local bidirectional streams. - uint64_t peer_initial_max_stream_data_bidi_local; - - // The initial flow control maximum data for remote bidirectional streams. - uint64_t peer_initial_max_stream_data_bidi_remote; - - // The initial flow control maximum data for unidirectional streams. - uint64_t peer_initial_max_stream_data_uni; - - // The initial maximum bidirectional streams. - uint64_t peer_initial_max_streams_bidi; - - // The initial maximum unidirectional streams. - uint64_t peer_initial_max_streams_uni; - - // The ACK delay exponent. - uint64_t peer_ack_delay_exponent; - - // The max ACK delay. - uint64_t peer_max_ack_delay; - - // Whether active migration is disabled. - bool peer_disable_active_migration; - - // The active connection ID limit. - uint64_t peer_active_conn_id_limit; - - // DATAGRAM frame extension parameter, if any. - ssize_t peer_max_datagram_frame_size; -} quiche_stats; - -// Collects and returns statistics about the connection. -void quiche_conn_stats(const quiche_conn *conn, quiche_stats *out); - -typedef struct { - // The local address used by this path. - struct sockaddr_storage local_addr; - socklen_t local_addr_len; - - // The peer address seen by this path. - struct sockaddr_storage peer_addr; - socklen_t peer_addr_len; - - // The validation state of the path. - ssize_t validation_state; - - // Whether this path is active. - bool active; - - // The number of QUIC packets received on this path. - size_t recv; - - // The number of QUIC packets sent on this path. - size_t sent; - - // The number of QUIC packets that were lost on this path. - size_t lost; - - // The number of sent QUIC packets with retransmitted data on this path. - size_t retrans; - - // The estimated round-trip time of the path (in nanoseconds). - uint64_t rtt; - - // The size of the path's congestion window in bytes. - size_t cwnd; - - // The number of sent bytes on this path. - uint64_t sent_bytes; - - // The number of received bytes on this path. - uint64_t recv_bytes; - - // The number of bytes lost on this path. - uint64_t lost_bytes; - - // The number of stream bytes retransmitted on this path. - uint64_t stream_retrans_bytes; - - // The current PMTU for the path. - size_t pmtu; - - // The most recent data delivery rate estimate in bytes/s. - uint64_t delivery_rate; -} quiche_path_stats; - - -// Collects and returns statistics about the specified path for the connection. -// -// The `idx` argument represent the path's index (also see the `paths_count` -// field of `quiche_stats`). -int quiche_conn_path_stats(const quiche_conn *conn, size_t idx, quiche_path_stats *out); - -// Returns whether or not this is a server-side connection. -bool quiche_conn_is_server(const quiche_conn *conn); - -// Returns the maximum DATAGRAM payload that can be sent. -ssize_t quiche_conn_dgram_max_writable_len(const quiche_conn *conn); - -// Returns the length of the first stored DATAGRAM. -ssize_t quiche_conn_dgram_recv_front_len(const quiche_conn *conn); - -// Returns the number of items in the DATAGRAM receive queue. -ssize_t quiche_conn_dgram_recv_queue_len(const quiche_conn *conn); - -// Returns the total size of all items in the DATAGRAM receive queue. -ssize_t quiche_conn_dgram_recv_queue_byte_size(const quiche_conn *conn); - -// Returns the number of items in the DATAGRAM send queue. -ssize_t quiche_conn_dgram_send_queue_len(const quiche_conn *conn); - -// Returns the total size of all items in the DATAGRAM send queue. -ssize_t quiche_conn_dgram_send_queue_byte_size(const quiche_conn *conn); - -// Reads the first received DATAGRAM. -ssize_t quiche_conn_dgram_recv(quiche_conn *conn, uint8_t *buf, - size_t buf_len); - -// Sends data in a DATAGRAM frame. -ssize_t quiche_conn_dgram_send(quiche_conn *conn, const uint8_t *buf, - size_t buf_len); - -// Purges queued outgoing DATAGRAMs matching the predicate. -void quiche_conn_dgram_purge_outgoing(quiche_conn *conn, - bool (*f)(uint8_t *, size_t)); - -// Schedule an ack-eliciting packet on the active path. -ssize_t quiche_conn_send_ack_eliciting(quiche_conn *conn); - -// Schedule an ack-eliciting packet on the specified path. -ssize_t quiche_conn_send_ack_eliciting_on_path(quiche_conn *conn, - const struct sockaddr *local, size_t local_len, - const struct sockaddr *peer, size_t peer_len); - -// Frees the connection object. -void quiche_conn_free(quiche_conn *conn); - - -// HTTP/3 API -// - -// List of ALPN tokens of supported HTTP/3 versions. -#define QUICHE_H3_APPLICATION_PROTOCOL "\x02h3\x05h3-29\x05h3-28\x05h3-27" - -enum quiche_h3_error { - // There is no error or no work to do - QUICHE_H3_ERR_DONE = -1, - - // The provided buffer is too short. - QUICHE_H3_ERR_BUFFER_TOO_SHORT = -2, - - // Internal error in the HTTP/3 stack. - QUICHE_H3_ERR_INTERNAL_ERROR = -3, - - // Endpoint detected that the peer is exhibiting behavior that causes. - // excessive load. - QUICHE_H3_ERR_EXCESSIVE_LOAD = -4, - - // Stream ID or Push ID greater that current maximum was - // used incorrectly, such as exceeding a limit, reducing a limit, - // or being reused. - QUICHE_H3_ERR_ID_ERROR= -5, - - // The endpoint detected that its peer created a stream that it will not - // accept. - QUICHE_H3_ERR_STREAM_CREATION_ERROR = -6, - - // A required critical stream was closed. - QUICHE_H3_ERR_CLOSED_CRITICAL_STREAM = -7, - - // No SETTINGS frame at beginning of control stream. - QUICHE_H3_ERR_MISSING_SETTINGS = -8, - - // A frame was received which is not permitted in the current state. - QUICHE_H3_ERR_FRAME_UNEXPECTED = -9, - - // Frame violated layout or size rules. - QUICHE_H3_ERR_FRAME_ERROR = -10, - - // QPACK Header block decompression failure. - QUICHE_H3_ERR_QPACK_DECOMPRESSION_FAILED = -11, - - // -12 was previously used for TransportError, skip it - - // The underlying QUIC stream (or connection) doesn't have enough capacity - // for the operation to complete. The application should retry later on. - QUICHE_H3_ERR_STREAM_BLOCKED = -13, - - // Error in the payload of a SETTINGS frame. - QUICHE_H3_ERR_SETTINGS_ERROR = -14, - - // Server rejected request. - QUICHE_H3_ERR_REQUEST_REJECTED = -15, - - // Request or its response cancelled. - QUICHE_H3_ERR_REQUEST_CANCELLED = -16, - - // Client's request stream terminated without containing a full-formed - // request. - QUICHE_H3_ERR_REQUEST_INCOMPLETE = -17, - - // An HTTP message was malformed and cannot be processed. - QUICHE_H3_ERR_MESSAGE_ERROR = -18, - - // The TCP connection established in response to a CONNECT request was - // reset or abnormally closed. - QUICHE_H3_ERR_CONNECT_ERROR = -19, - - // The requested operation cannot be served over HTTP/3. Peer should retry - // over HTTP/1.1. - QUICHE_H3_ERR_VERSION_FALLBACK = -20, - - // The following QUICHE_H3_TRANSPORT_ERR_* errors are propagated - // from the QUIC transport layer. - - // See QUICHE_ERR_DONE. - QUICHE_H3_TRANSPORT_ERR_DONE = QUICHE_ERR_DONE - 1000, - - // See QUICHE_ERR_BUFFER_TOO_SHORT. - QUICHE_H3_TRANSPORT_ERR_BUFFER_TOO_SHORT = QUICHE_ERR_BUFFER_TOO_SHORT - 1000, - - // See QUICHE_ERR_UNKNOWN_VERSION. - QUICHE_H3_TRANSPORT_ERR_UNKNOWN_VERSION = QUICHE_ERR_UNKNOWN_VERSION - 1000, - - // See QUICHE_ERR_INVALID_FRAME. - QUICHE_H3_TRANSPORT_ERR_INVALID_FRAME = QUICHE_ERR_INVALID_FRAME - 1000, - - // See QUICHE_ERR_INVALID_PACKET. - QUICHE_H3_TRANSPORT_ERR_INVALID_PACKET = QUICHE_ERR_INVALID_PACKET - 1000, - - // See QUICHE_ERR_INVALID_STATE. - QUICHE_H3_TRANSPORT_ERR_INVALID_STATE = QUICHE_ERR_INVALID_STATE - 1000, - - // See QUICHE_ERR_INVALID_STREAM_STATE. - QUICHE_H3_TRANSPORT_ERR_INVALID_STREAM_STATE = QUICHE_ERR_INVALID_STREAM_STATE - 1000, - - // See QUICHE_ERR_INVALID_TRANSPORT_PARAM. - QUICHE_H3_TRANSPORT_ERR_INVALID_TRANSPORT_PARAM = QUICHE_ERR_INVALID_TRANSPORT_PARAM - 1000, - - // See QUICHE_ERR_CRYPTO_FAIL. - QUICHE_H3_TRANSPORT_ERR_CRYPTO_FAIL = QUICHE_ERR_CRYPTO_FAIL - 1000, - - // See QUICHE_ERR_TLS_FAIL. - QUICHE_H3_TRANSPORT_ERR_TLS_FAIL = QUICHE_ERR_TLS_FAIL - 1000, - - // See QUICHE_ERR_FLOW_CONTROL. - QUICHE_H3_TRANSPORT_ERR_FLOW_CONTROL = QUICHE_ERR_FLOW_CONTROL - 1000, - - // See QUICHE_ERR_STREAM_LIMIT. - QUICHE_H3_TRANSPORT_ERR_STREAM_LIMIT = QUICHE_ERR_STREAM_LIMIT - 1000, - - // See QUICHE_ERR_STREAM_STOPPED. - QUICHE_H3_TRANSPORT_ERR_STREAM_STOPPED = QUICHE_ERR_STREAM_STOPPED - 1000, - - // See QUICHE_ERR_STREAM_RESET. - QUICHE_H3_TRANSPORT_ERR_STREAM_RESET = QUICHE_ERR_STREAM_RESET - 1000, - - // See QUICHE_ERR_FINAL_SIZE. - QUICHE_H3_TRANSPORT_ERR_FINAL_SIZE = QUICHE_ERR_FINAL_SIZE - 1000, - - // See QUICHE_ERR_CONGESTION_CONTROL. - QUICHE_H3_TRANSPORT_ERR_CONGESTION_CONTROL = QUICHE_ERR_CONGESTION_CONTROL - 1000, - - // See QUICHE_ERR_ID_LIMIT. - QUICHE_H3_TRANSPORT_ERR_ID_LIMIT = QUICHE_ERR_ID_LIMIT - 1000, - - // See QUICHE_ERR_OUT_OF_IDENTIFIERS. - QUICHE_H3_TRANSPORT_ERR_OUT_OF_IDENTIFIERS = QUICHE_ERR_OUT_OF_IDENTIFIERS - 1000, - - // See QUICHE_ERR_KEY_UPDATE. - QUICHE_H3_TRANSPORT_ERR_KEY_UPDATE = QUICHE_ERR_KEY_UPDATE - 1000, -}; - -// Stores configuration shared between multiple connections. -typedef struct quiche_h3_config quiche_h3_config; - -// Creates an HTTP/3 config object with default settings values. -quiche_h3_config *quiche_h3_config_new(void); - -// Sets the `SETTINGS_MAX_FIELD_SECTION_SIZE` setting. -void quiche_h3_config_set_max_field_section_size(quiche_h3_config *config, uint64_t v); - -// Sets the `SETTINGS_QPACK_MAX_TABLE_CAPACITY` setting. -void quiche_h3_config_set_qpack_max_table_capacity(quiche_h3_config *config, uint64_t v); - -// Sets the `SETTINGS_QPACK_BLOCKED_STREAMS` setting. -void quiche_h3_config_set_qpack_blocked_streams(quiche_h3_config *config, uint64_t v); - -// Sets the `SETTINGS_ENABLE_CONNECT_PROTOCOL` setting. -void quiche_h3_config_enable_extended_connect(quiche_h3_config *config, bool enabled); - -// Frees the HTTP/3 config object. -void quiche_h3_config_free(quiche_h3_config *config); - -// An HTTP/3 connection. -typedef struct quiche_h3_conn quiche_h3_conn; - -// Creates a new server-side connection. -quiche_h3_conn *quiche_h3_accept(quiche_conn *quiche_conn, - quiche_h3_config *config); - -// Creates a new HTTP/3 connection using the provided QUIC connection. -quiche_h3_conn *quiche_h3_conn_new_with_transport(quiche_conn *quiche_conn, - quiche_h3_config *config); - -enum quiche_h3_event_type { - QUICHE_H3_EVENT_HEADERS, - QUICHE_H3_EVENT_DATA, - QUICHE_H3_EVENT_FINISHED, - QUICHE_H3_EVENT_DATAGRAM, - QUICHE_H3_EVENT_GOAWAY, - QUICHE_H3_EVENT_RESET, - QUICHE_H3_EVENT_PRIORITY_UPDATE, -}; - -typedef struct quiche_h3_event quiche_h3_event; - -// Processes HTTP/3 data received from the peer. -int64_t quiche_h3_conn_poll(quiche_h3_conn *conn, quiche_conn *quic_conn, - quiche_h3_event **ev); - -// Returns the type of the event. -enum quiche_h3_event_type quiche_h3_event_type(quiche_h3_event *ev); - -// Iterates over the headers in the event. -// -// The `cb` callback will be called for each header in `ev`. `cb` should check -// the validity of pseudo-headers and headers. If `cb` returns any value other -// than `0`, processing will be interrupted and the value is returned to the -// caller. -int quiche_h3_event_for_each_header(quiche_h3_event *ev, - int (*cb)(uint8_t *name, size_t name_len, - uint8_t *value, size_t value_len, - void *argp), - void *argp); - -// Iterates over the peer's HTTP/3 settings. -// -// The `cb` callback will be called for each setting in `conn`. -// If `cb` returns any value other than `0`, processing will be interrupted and -// the value is returned to the caller. -int quiche_h3_for_each_setting(quiche_h3_conn *conn, - int (*cb)(uint64_t identifier, - uint64_t value, void *argp), - void *argp); - -// Check whether data will follow the headers on the stream. -bool quiche_h3_event_headers_has_body(quiche_h3_event *ev); - -// Check whether or not extended connection is enabled by the peer -bool quiche_h3_extended_connect_enabled_by_peer(quiche_h3_conn *conn); - -// Frees the HTTP/3 event object. -void quiche_h3_event_free(quiche_h3_event *ev); - -typedef struct { - const uint8_t *name; - size_t name_len; - - const uint8_t *value; - size_t value_len; -} quiche_h3_header; - -// Extensible Priorities parameters. -typedef struct { - uint8_t urgency; - bool incremental; -} quiche_h3_priority; - -// Sends an HTTP/3 request. -int64_t quiche_h3_send_request(quiche_h3_conn *conn, quiche_conn *quic_conn, - quiche_h3_header *headers, size_t headers_len, - bool fin); - -// Sends an HTTP/3 response on the specified stream with default priority. -int quiche_h3_send_response(quiche_h3_conn *conn, quiche_conn *quic_conn, - uint64_t stream_id, quiche_h3_header *headers, - size_t headers_len, bool fin); - -// Sends an HTTP/3 response on the specified stream with specified priority. -int quiche_h3_send_response_with_priority(quiche_h3_conn *conn, - quiche_conn *quic_conn, uint64_t stream_id, - quiche_h3_header *headers, size_t headers_len, - quiche_h3_priority *priority, bool fin); - -// Sends an HTTP/3 body chunk on the given stream. -ssize_t quiche_h3_send_body(quiche_h3_conn *conn, quiche_conn *quic_conn, - uint64_t stream_id, uint8_t *body, size_t body_len, - bool fin); - -// Reads request or response body data into the provided buffer. -ssize_t quiche_h3_recv_body(quiche_h3_conn *conn, quiche_conn *quic_conn, - uint64_t stream_id, uint8_t *out, size_t out_len); - -// Try to parse an Extensible Priority field value. -int quiche_h3_parse_extensible_priority(uint8_t *priority, - size_t priority_len, - quiche_h3_priority *parsed); - -/// Sends a PRIORITY_UPDATE frame on the control stream with specified -/// request stream ID and priority. -int quiche_h3_send_priority_update_for_request(quiche_h3_conn *conn, - quiche_conn *quic_conn, - uint64_t stream_id, - quiche_h3_priority *priority); - -// Take the last received PRIORITY_UPDATE frame for a stream. -// -// The `cb` callback will be called once. `cb` should check the validity of -// priority field value contents. If `cb` returns any value other than `0`, -// processing will be interrupted and the value is returned to the caller. -int quiche_h3_take_last_priority_update(quiche_h3_conn *conn, - uint64_t prioritized_element_id, - int (*cb)(uint8_t *priority_field_value, - uint64_t priority_field_value_len, - void *argp), - void *argp); - -// Returns whether the peer enabled HTTP/3 DATAGRAM frame support. -bool quiche_h3_dgram_enabled_by_peer(quiche_h3_conn *conn, - quiche_conn *quic_conn); - -// Writes data to the DATAGRAM send queue. -ssize_t quiche_h3_send_dgram(quiche_h3_conn *conn, quiche_conn *quic_conn, - uint64_t flow_id, uint8_t *data, size_t data_len); - -// Reads data from the DATAGRAM receive queue. -ssize_t quiche_h3_recv_dgram(quiche_h3_conn *conn, quiche_conn *quic_conn, - uint64_t *flow_id, size_t *flow_id_len, - uint8_t *out, size_t out_len); - -// Frees the HTTP/3 connection object. -void quiche_h3_conn_free(quiche_h3_conn *conn); - -#if defined(__cplusplus) -} // extern C -#endif - -#endif // QUICHE_H diff -r 1059e7b52e47 -r ba323d8c0f93 gen.rs --- a/gen.rs Sat Jun 03 19:57:46 2023 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,84 +0,0 @@ -//! demo -pub use fig::*; -pub use obj::*; -use std::ffi::{CStr, CString}; //OsStr,Path - //use std::os::unix::ffi::OsStrExt; -use std::slice; -use libc::{c_char,size_t}; - -#[macro_export] -macro_rules! cdefn { - (free $t:tt $n:tt) => { - #[no_mangle] - pub unsafe extern "C" fn $n(ptr: *mut $t) { - if ptr.is_null() { - return; - } - let _ = Box::from_raw(ptr); - } - }; - (from_string $t:tt $n:tt) => { - #[no_mangle] - pub unsafe extern "C" fn $n(ptr: *const c_char) -> *mut $t { - assert!(!ptr.is_null()); - let p = CStr::from_ptr(ptr).to_str().unwrap(); - Box::into_raw(Box::new(p.into())) - } - }; - (json_string $t:tt $r:tt $w:tt) => { - #[no_mangle] - pub unsafe extern "C" fn $r(ptr: *const c_char) -> *mut $t { - assert!(!ptr.is_null()); - let s = CStr::from_ptr(ptr); - Box::into_raw(Box::new($t::from_json_str(&s.to_str().unwrap()).unwrap())) - } - - #[no_mangle] - pub unsafe extern "C" fn $w(ptr: *const $t) -> *mut c_char { - let p = &*ptr; - let x = p.to_json_string().unwrap(); - CString::new(x.as_str().as_bytes()).unwrap().into_raw() - } - }; - (ron_string $t:tt $r:tt $w:tt) => { - #[no_mangle] - pub unsafe extern "C" fn $r(ptr: *const c_char) -> *mut $t { - assert!(!ptr.is_null()); - let s = CStr::from_ptr(ptr); - Box::into_raw(Box::new($t::from_ron_str(&s.to_str().unwrap()).unwrap())) - } - - #[no_mangle] - pub unsafe extern "C" fn $w(ptr: *const $t) -> *mut c_char { - let p = &*ptr; - let x = p.to_ron_string().unwrap(); - CString::new(x.as_str().as_bytes()).unwrap().into_raw() - } - }; - (bytes $t:tt $r:tt $w:tt) => { - #[no_mangle] - pub unsafe extern "C" fn $r(ptr: *const u8, len: size_t) -> *mut $t { - Box::into_raw(Box::new($t::decode(slice::from_raw_parts(ptr,len)).unwrap())) - } - - #[no_mangle] - pub unsafe extern "C" fn $w(ptr: *const $t) -> *mut u8 { - let p = &*ptr; - let mut x = p.encode().unwrap(); - let r = x.as_mut_ptr(); - std::mem::forget(x); - r - } - } -} - -cdefn!(free Service free_service); -cdefn!(from_string Service service_from_string); -cdefn!(json_string Service service_from_json_string service_to_json_string); -cdefn!(ron_string Service service_from_ron_string service_to_ron_string); -cdefn!(bytes Service service_decode service_encode); -cdefn!(free CustomService free_custom_service); -cdefn!(from_string CustomService custom_service_from_string); -cdefn!(json_string CustomService custom_service_from_json_string custom_service_to_json_string); -cdefn!(ron_string CustomService custom_service_from_ron_string custom_service_to_ron_string); -cdefn!(bytes CustomService custom_service_decode custom_service_encode); diff -r 1059e7b52e47 -r ba323d8c0f93 install.lisp --- a/install.lisp Sat Jun 03 19:57:46 2023 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,12 +0,0 @@ -#!/usr/local/bin/sbcl --script -(in-package :cl-user) -#-quicklisp -(let ((quicklisp-init (merge-pathnames "quicklisp/setup.lisp" (user-homedir-pathname)))) - (when (probe-file quicklisp-init) - (load quicklisp-init))) - -(asdf:load-asd "cl-demo.asd") -(asdf:loadload "demo.asd") -(ql:quickload :demo) -;; (asdf:make :demo) -(sb-ext:save-lisp-and-die "out/demo" :toplevel #'demo:main :executable t) diff -r 1059e7b52e47 -r ba323d8c0f93 lib.rs --- a/lib.rs Sat Jun 03 19:57:46 2023 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,117 +0,0 @@ -//! demo/lib.rs --- generated by DEMO:RS-MACROEXPAND -extern crate libc; -extern crate obj; -use libc::{c_char, size_t}; -use obj::{CustomService, Objective, Service}; -use std::ffi::{CStr, CString}; -use std::slice; -#[no_mangle] -pub unsafe extern "C" fn free_service(ptr: *mut Service) { - if ptr.is_null() { - return; - } - let _ = Box::from_raw(ptr); -} -#[no_mangle] -pub unsafe extern "C" fn service_from_string(ptr: *const c_char) -> *mut Service { - assert!(!ptr.is_null()); - let p = CStr::from_ptr(ptr).to_str().unwrap(); - Box::into_raw(Box::new(p.into())) -} -#[no_mangle] -pub unsafe extern "C" fn service_from_json_string(ptr: *const c_char) -> *mut Service { - assert!(!ptr.is_null()); - let s = CStr::from_ptr(ptr); - Box::into_raw(Box::new( - Service::from_json_str(&s.to_str().unwrap()).unwrap(), - )) -} -#[no_mangle] -pub unsafe extern "C" fn service_to_json_string(ptr: *const Service) -> *mut c_char { - let p = &*ptr; - let x = p.to_json_string().unwrap(); - CString::new(x.as_str().as_bytes()).unwrap().into_raw() -} -#[no_mangle] -pub unsafe extern "C" fn service_from_ron_string(ptr: *const c_char) -> *mut Service { - assert!(!ptr.is_null()); - let s = CStr::from_ptr(ptr); - Box::into_raw(Box::new( - Service::from_ron_str(&s.to_str().unwrap()).unwrap(), - )) -} -#[no_mangle] -pub unsafe extern "C" fn service_to_ron_string(ptr: *const Service) -> *mut c_char { - let p = &*ptr; - let x = p.to_ron_string().unwrap(); - CString::new(x.as_str().as_bytes()).unwrap().into_raw() -} -#[no_mangle] -pub unsafe extern "C" fn service_decode(ptr: *const u8, len: size_t) -> *mut Service { - Box::into_raw(Box::new( - Service::decode(slice::from_raw_parts(ptr, len)).unwrap(), - )) -} -#[no_mangle] -pub unsafe extern "C" fn service_encode(ptr: *const Service) -> *mut u8 { - let p = &*ptr; - let mut x = p.encode().unwrap(); - let r = x.as_mut_ptr(); - std::mem::forget(x); - r -} -#[no_mangle] -pub unsafe extern "C" fn free_custom_service(ptr: *mut CustomService) { - if ptr.is_null() { - return; - } - let _ = Box::from_raw(ptr); -} -#[no_mangle] -pub unsafe extern "C" fn custom_service_from_string(ptr: *const c_char) -> *mut CustomService { - assert!(!ptr.is_null()); - let p = CStr::from_ptr(ptr).to_str().unwrap(); - Box::into_raw(Box::new(p.into())) -} -#[no_mangle] -pub unsafe extern "C" fn custom_service_from_json_string(ptr: *const c_char) -> *mut CustomService { - assert!(!ptr.is_null()); - let s = CStr::from_ptr(ptr); - Box::into_raw(Box::new( - CustomService::from_json_str(&s.to_str().unwrap()).unwrap(), - )) -} -#[no_mangle] -pub unsafe extern "C" fn custom_service_to_json_string(ptr: *const CustomService) -> *mut c_char { - let p = &*ptr; - let x = p.to_json_string().unwrap(); - CString::new(x.as_str().as_bytes()).unwrap().into_raw() -} -#[no_mangle] -pub unsafe extern "C" fn custom_service_from_ron_string(ptr: *const c_char) -> *mut CustomService { - assert!(!ptr.is_null()); - let s = CStr::from_ptr(ptr); - Box::into_raw(Box::new( - CustomService::from_ron_str(&s.to_str().unwrap()).unwrap(), - )) -} -#[no_mangle] -pub unsafe extern "C" fn custom_service_to_ron_string(ptr: *const CustomService) -> *mut c_char { - let p = &*ptr; - let x = p.to_ron_string().unwrap(); - CString::new(x.as_str().as_bytes()).unwrap().into_raw() -} -#[no_mangle] -pub unsafe extern "C" fn custom_service_decode(ptr: *const u8, len: size_t) -> *mut CustomService { - Box::into_raw(Box::new( - CustomService::decode(slice::from_raw_parts(ptr, len)).unwrap(), - )) -} -#[no_mangle] -pub unsafe extern "C" fn custom_service_encode(ptr: *const CustomService) -> *mut u8 { - let p = &*ptr; - let mut x = p.encode().unwrap(); - let r = x.as_mut_ptr(); - std::mem::forget(x); - r -} diff -r 1059e7b52e47 -r ba323d8c0f93 makefile --- a/makefile Sat Jun 03 19:57:46 2023 -0400 +++ b/makefile Sat Jun 03 22:48:46 2023 -0400 @@ -1,23 +1,22 @@ M?=release L?=sbcl +C?=default.cfg L_C=$(L) --no-userinit +L_D=$(L) --load demo.asd --eval '(ql:quickload "demo")' L_S=$(L) --script -P?=python3 ARCH?= A_C=ifeq ($(ARCH),x86_64) A_C=arch -$(ARCH) endif -.PHONY: -RS:Cargo.toml build.rs lib.rs obj -CL:*.asd *.lisp +.PHONY:build +RS:Cargo.toml rustfmt.toml src/crates/* +CL:*/*.asd */*.lisp deps:; clean:;rm -rf *.fasl;cargo clean fmt:$(RS);cargo fmt -build:$(RS) $(CL);cargo build --$(M);$L --load demo.asd \ - --eval '(ql:quickload :demo)' \ - --eval '(asdf:make :demo)' \ +build:$(RS) $(CL);cargo build --$(M);$(L_D) + --eval '(asdf:make "demo")' \ --eval '(quit)' -ffi:build;cp target/$(M)/libdemo.dylib ffi;cd ffi;$(P) ffi/build.py docs:$(RS);cargo doc -test:$(RS) $(CL);cargo test;$L tests.lisp +test:$(RS) $(CL);cargo test;$(L_D) --eval '(asdf:test "demo")' --eval '(quit)' #pack:;scripts/pack.ros #check:;scripts/check.ros -ci:clean fmt build ffi docs test; +ci:clean fmt build docs test; diff -r 1059e7b52e47 -r ba323d8c0f93 obj/Cargo.toml --- a/obj/Cargo.toml Sat Jun 03 19:57:46 2023 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,27 +0,0 @@ -[package] -name = "obj" -version = "0.1.0" -edition = "2021" -[features] -oauth = ["yup-oauth2"] - -[dependencies] -ron = "0.7.0" -bincode = "1.3.3" -serde_json = "1.0.68" -serde = { version = "1.0.130", features = ["derive"] } -chrono = { version = "0.4.19", features = ["serde"] } -mime = "0.3.16" -regex = "1.5.4" -rusty_ulid = "0.11.0" -uuid = { version = "0.8", features = ["serde"] } -yup-oauth2 = { version = "5.1.0", optional = true } -blake3 = "1.0.0" -hashbrown = "0.11.2" -rand = "0.8.0" -sha2 = "0.9.5" -hex = "0.4.3" -ulid = "1.0.0" - -[target.'cfg(target_arch = "wasm32")'.dependencies] -uuid = { version = "0.8", features = ["wasm-bindgen"] } diff -r 1059e7b52e47 -r ba323d8c0f93 obj/proc_macros/Cargo.toml --- a/obj/proc_macros/Cargo.toml Sat Jun 03 19:57:46 2023 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,10 +0,0 @@ -[package] -name = "proc_macros" -version = "0.1.0" -edition = "2021" -[lib] -proc-macro = true -[dependencies] -quote = "1.0" -proc-macro2 = "1.0" -syn = "1.0" \ No newline at end of file diff -r 1059e7b52e47 -r ba323d8c0f93 obj/proc_macros/src/derive.rs --- a/obj/proc_macros/src/derive.rs Sat Jun 03 19:57:46 2023 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,5 +0,0 @@ -mod derive; -use proc_macro::TokenStream; -pub fn derive_static_type(input: TokenStream) -> TokenStream { - derive::derive_static_type(input) -} diff -r 1059e7b52e47 -r ba323d8c0f93 obj/proc_macros/src/lib.rs --- a/obj/proc_macros/src/lib.rs Sat Jun 03 19:57:46 2023 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ - diff -r 1059e7b52e47 -r ba323d8c0f93 obj/src/auth.rs --- a/obj/src/auth.rs Sat Jun 03 19:57:46 2023 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,72 +0,0 @@ -//! Auth Configs -use serde::{Deserialize, Serialize}; - -#[cfg(feature = "oauth")] -use yup_oauth2::ApplicationSecret; - -#[derive(Serialize, Deserialize, Debug, Default, Hash)] -pub struct AuthConfig { - pub provider: String, - #[cfg(feature = "oauth")] - pub oauth: Option, - pub ssh: Option, - pub pw: Option, -} - -#[derive(Serialize, Deserialize, Default, Debug, Hash)] -pub struct PasswordConfig(String, String); - -#[cfg(feature = "oauth")] -#[derive(Serialize, Deserialize, Hash, Debug, PartialEq, Clone, Default)] -pub struct Oauth2Config { - pub client_id: String, - pub client_secret: String, - pub redirect_uris: Vec, - pub auth_uri: String, - pub token_uri: String, - pub project_id: Option, //for apptoken - pub client_email: Option, - /// The URL of the public x509 certificate, used to verify the signature on - /// JWTs, such as ID tokens, signed by the authentication provider. - pub auth_provider_x509_cert_url: Option, - /// The URL of the public x509 certificate, used to verify JWTs signed by the - /// client. - pub client_x509_cert_url: Option, -} - -#[cfg(feature = "oauth")] -impl From for Oauth2Config { - fn from(shh: ApplicationSecret) -> Self { - Oauth2Config { - client_id: shh.client_id, - client_secret: shh.client_secret, - redirect_uris: shh.redirect_uris, - auth_uri: shh.auth_uri, - token_uri: shh.token_uri, - project_id: shh.project_id, - client_email: shh.client_email, - auth_provider_x509_cert_url: shh.auth_provider_x509_cert_url, - client_x509_cert_url: shh.client_x509_cert_url, - } - } -} - -#[cfg(feature = "oauth")] -impl From for ApplicationSecret { - fn from(cfg: Oauth2Config) -> Self { - ApplicationSecret { - client_id: cfg.client_id, - client_secret: cfg.client_secret, - redirect_uris: cfg.redirect_uris, - auth_uri: cfg.auth_uri, - token_uri: cfg.token_uri, - project_id: cfg.project_id, - client_email: cfg.client_email, - auth_provider_x509_cert_url: cfg.auth_provider_x509_cert_url, - client_x509_cert_url: cfg.client_x509_cert_url, - } - } -} - -#[derive(Serialize, Deserialize, Hash, Debug, PartialEq, Clone, Default)] -pub struct SshConfig {} diff -r 1059e7b52e47 -r ba323d8c0f93 obj/src/cfg.rs --- a/obj/src/cfg.rs Sat Jun 03 19:57:46 2023 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,51 +0,0 @@ -/// common trait for all config modules. This trait provides functions -/// for de/serializing to/from RON, updating fields, and formatting. -use serde::{Serialize, Deserialize}; -use crate::Objective; -use std::collections::HashMap as M; -use std::path::PathBuf; -use std::string::String as S; -use std::error::Error as E; -use std::boxed::Box as B; -type R = std::result::Result>; - -pub trait Configure: Objective { - fn update(&self) -> R<()> { - Ok(()) - } -} - -#[derive(Serialize, Deserialize, Debug, Default)] -pub struct ShellConfig { - pub env: M, - pub cmds: M, - pub shell: ShellType, -} - -impl Objective for ShellConfig {} - -#[derive(Serialize, Deserialize, Debug, Hash, Default)] -pub enum ShellType { - #[default] - Bash, - Zsh, - Sh, -} - -#[derive(Serialize, Deserialize, Debug, Default)] -pub enum EditorType { - #[default] - Emacs, - Vi, - Nano, -} - -#[derive(Serialize, Deserialize, Debug, Default)] -pub struct EditorConfig { - pub editor: EditorType, - pub cmds: M, - pub init_file: PathBuf, -} - -#[cfg(test)] -mod tests; diff -r 1059e7b52e47 -r ba323d8c0f93 obj/src/database.rs --- a/obj/src/database.rs Sat Jun 03 19:57:46 2023 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,18 +0,0 @@ -//! cfg::config::database -//! -//! Database configuration primitives -use serde::{Deserialize, Serialize}; - -#[derive(Serialize, Deserialize, Debug, Hash, PartialEq, Eq)] -pub struct DatabaseConfig { - engine: DatabaseType, - path: String, - cfs: Vec, -} - -#[derive(Serialize, Deserialize, Debug, Hash, PartialEq, Eq)] -pub enum DatabaseType { - RocksDB, - Postgres, - Alch, -} diff -r 1059e7b52e47 -r ba323d8c0f93 obj/src/err.rs --- a/obj/src/err.rs Sat Jun 03 19:57:46 2023 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,81 +0,0 @@ -//! obj errors -use std::{fmt, io}; - -/// obj Result wrapper -pub type Result = std::result::Result; - -/// obj Error type -#[derive(Debug)] -pub enum Error { - Message(String), - Ron(ron::error::Error), - Json(serde_json::error::Error), - Io(io::Error), - Bincode(bincode::Error), - Utf8(std::string::FromUtf8Error), - Parse(std::string::ParseError), -} - -impl serde::ser::Error for Error { - fn custom(msg: T) -> Self { - Error::Message(msg.to_string()) - } -} - -impl serde::de::Error for Error { - fn custom(msg: T) -> Self { - Error::Message(msg.to_string()) - } -} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Error::Message(msg) => f.write_str(msg), - Error::Io(ref err) => write!(f, "obj IO error: {}", err), - Error::Ron(ref err) => write!(f, "obj Ron error: {}", err), - Error::Json(ref err) => write!(f, "obj Json error: {}", err), - Error::Bincode(ref err) => write!(f, "obj Bincode error: {}", err), - Error::Utf8(ref err) => write!(f, "obj Utf8 error: {}", err), - Error::Parse(ref err) => write!(f, "obj Parse error: {}", err), - } - } -} - -impl From for Error { - fn from(e: io::Error) -> Self { - Error::Io(e) - } -} - -impl From for Error { - fn from(e: std::string::ParseError) -> Self { - Error::Parse(e) - } -} - -impl From for Error { - fn from(err: std::string::FromUtf8Error) -> Self { - Error::Utf8(err) - } -} - -impl From for Error { - fn from(e: ron::Error) -> Self { - Error::Ron(e) - } -} - -impl From for Error { - fn from(e: serde_json::Error) -> Self { - Error::Json(e) - } -} - -impl From for Error { - fn from(e: bincode::Error) -> Self { - Error::Bincode(e) - } -} - -impl std::error::Error for Error {} diff -r 1059e7b52e47 -r ba323d8c0f93 obj/src/hash.rs --- a/obj/src/hash.rs Sat Jun 03 19:57:46 2023 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,51 +0,0 @@ -//! hash - wrapper for hash algorithms and types - -pub use blake3::{derive_key, hash, keyed_hash, Hash as B3Hash, Hasher as B3Hasher, OutputReader}; -pub use hex; -pub use sha2::Sha512; - -pub use std::hash::{Hash, Hasher}; - -pub const KEY_LEN: usize = 32; -pub const OUT_LEN: usize = 32; -pub const OUT_LEN_HEX: usize = OUT_LEN * 2; - -#[cfg(test)] -mod tests { - use crate::*; - use super::*; - #[test] - fn id_state_hash() { - let id = id::Id(vec![0; KEY_LEN]); - let hash = id.state_hash(&mut B3Hasher::new()); - assert_eq!(hash, id.state_hash(&mut B3Hasher::new())); - } - - #[test] - fn id_hex() { - let id = id::Id(vec![255; KEY_LEN]); - - assert_eq!( - hex::decode("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap(), - id.0 - ); - } - - #[test] - fn rand_id() { - let id = id::Id::rand(); - let hash = id.state_hash(&mut B3Hasher::new()); - assert_eq!(hash, id.state_hash(&mut B3Hasher::new())); - } - - #[test] - fn random_demon_id_is_valid() { - use id::PeerId; - for _ in 0..5000 { - let did = PeerId::rand(); - let did2 = PeerId::rand(); - assert_eq!(did, did); - assert_ne!(did, did2); - } - } -} diff -r 1059e7b52e47 -r ba323d8c0f93 obj/src/id.rs --- a/obj/src/id.rs Sat Jun 03 19:57:46 2023 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,120 +0,0 @@ -use std::{fmt, str::FromStr}; -use serde::{Serialize, Deserialize}; -pub use uuid::Uuid; -pub use ulid::Ulid; -use rand::Rng; -use crate::hash::{KEY_LEN,OUT_LEN,B3Hasher}; -/// a simple Id abstraction -#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Serialize, Deserialize, Hash)] -pub struct Id(pub Vec); - -impl Id { - pub fn rand() -> Self { - let mut rng = rand::thread_rng(); - let vals: Vec = (0..KEY_LEN).map(|_| rng.gen_range(0..u8::MAX)).collect(); - Id(vals) - } - - pub fn state_hash(&self, state: &mut B3Hasher) -> Self { - let mut output = vec![0; OUT_LEN]; - state.update(&self.0); - let mut res = state.finalize_xof(); - res.fill(&mut output); - Id(output) - } - - pub fn to_hex(&self) -> String { - hex::encode(&self.0) - } -} - -/// PeerId -/// -/// identifies a unique Peer -#[derive(Clone, Eq, Hash, Ord, PartialEq, PartialOrd, Debug)] -pub struct PeerId { - id: [u8; 32], -} - -impl PeerId { - pub fn new() -> Self { - Self::default() - } - - pub fn rand() -> Self { - let pd = rand::thread_rng().gen::<[u8; 32]>(); - Self { id: pd } - } - - pub fn from_bytes(data: &[u8]) -> Self { - let pd = blake3::hash(data); - let hash = pd.as_bytes(); - Self { id: *hash } - } -} - -impl Default for PeerId { - fn default() -> Self { - PeerId { id: [0; 32] } - } -} - -/// Identity trait -/// -/// Defines Identity-related behaviors -pub trait Identity: Sized { - /// return the hashed bytes of an ObjectId - fn id(&self) -> Id; -} - -#[derive(Debug, Copy, Clone, Eq, PartialEq)] -pub struct ObjectId(u128); - -pub struct NameSpace { - pub prefix: Option, - pub capacity: u64, - pub route: Vec, - pub key: Option, -} - -pub struct Domain { - pub ns: NameSpace, - pub id: Id, -} - -impl From for ObjectId { - fn from(uuid: Uuid) -> Self { - ObjectId(uuid.as_u128()) - } -} - -impl From for ObjectId { - fn from(ulid: Ulid) -> Self { - ObjectId(u128::from(ulid)) - } -} - -impl From for ObjectId { - fn from(src: u128) -> Self { - ObjectId(src) - } -} - -impl FromStr for ObjectId { - type Err = (); - fn from_str(input: &str) -> std::result::Result { - match input { - i => Ok(ObjectId(u128::from(Ulid::from_str(i).unwrap()))), - } - } -} - -impl fmt::Display for ObjectId { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - ObjectId(i) => { - write!(f, "{}", Ulid::from(i)) - } - } - } -} diff -r 1059e7b52e47 -r ba323d8c0f93 obj/src/lib.rs --- a/obj/src/lib.rs Sat Jun 03 19:57:46 2023 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,149 +0,0 @@ -//! obj/src/lib.rs --- Objective type library -#![feature(associated_type_bounds)] -mod err; -pub use err::{Error, Result}; -mod types; -pub use types::*; -pub mod id; -pub mod auth; -pub mod hash; -pub mod network; -pub mod database; -pub use bincode; -pub use ron; -use ron::extensions::Extensions; -use serde::{de::DeserializeOwned, Deserialize, Serialize}; -pub use serde_json; -use std::collections::{BTreeMap, HashMap}; -use std::io; - -/// Objective trait -/// Define Object behaviors, implemented by Objects -pub trait Objective { - fn encode(&self) -> Result> - where - Self: Serialize, - { - Ok(bincode::serialize(self)?) - } - - fn encode_into(&self, writer: W) -> Result<()> - where - W: io::Write, - Self: Serialize, - { - Ok(bincode::serialize_into(writer, self)?) - } - - fn decode<'a>(bytes: &'a [u8]) -> Result - where - Self: Deserialize<'a>, - { - Ok(bincode::deserialize(bytes)?) - } - - fn decode_from(&self, rdr: R) -> Result - where - R: io::Read, - Self: DeserializeOwned, - { - Ok(bincode::deserialize_from(rdr)?) - } - - fn to_ron_writer(&self, writer: W) -> Result<()> - where - W: io::Write, - Self: Serialize, - { - Ok(ron::ser::to_writer_pretty( - writer, - &self, - ron::ser::PrettyConfig::new() - .indentor(" ".to_owned()) - .extensions(Extensions::all()), - )?) - } - - fn to_ron_string(&self) -> Result - where - Self: Serialize, - { - Ok(ron::ser::to_string_pretty( - &self, - ron::ser::PrettyConfig::new().indentor(" ".to_owned()), - )?) - } - - fn from_ron_reader(&self, mut rdr: R) -> Result - where - R: io::Read, - Self: DeserializeOwned, - { - let mut bytes = Vec::new(); - rdr.read_to_end(&mut bytes)?; - Ok(ron::de::from_bytes(&bytes)?) - } - - fn from_ron_str<'a>(s: &'a str) -> Result - where - Self: Deserialize<'a>, - { - Ok(ron::de::from_bytes(s.as_bytes())?) - } - - fn to_json_writer(&self, writer: W) -> Result<()> - where - W: io::Write, - Self: Serialize, - { - // let formatter = serde_json::ser::PrettyFormatter::with_indent(b" "); - Ok(serde_json::ser::to_writer_pretty(writer, &self)?) - } - - fn to_json_string(&self) -> Result - where - Self: Serialize, - { - Ok(serde_json::ser::to_string_pretty(&self)?) - } - - fn from_json_reader(&self, mut rdr: R) -> Result - where - R: io::Read, - Self: DeserializeOwned, - { - let mut bytes = Vec::new(); - rdr.read_to_end(&mut bytes)?; - Ok(serde_json::de::from_slice(&bytes)?) - } - - fn from_json_str<'a>(s: &'a str) -> Result - where - Self: Deserialize<'a>, - { - Ok(serde_json::de::from_slice(s.as_bytes())?) - } -} - -impl Objective for Vec {} -impl Objective for HashMap {} -impl Objective for BTreeMap {} -impl Objective for std::path::PathBuf {} -impl Objective for std::path::Path {} -impl Objective for std::string::String {} -impl Objective for std::any::TypeId {} -impl Objective for u8 {} -impl Objective for u16 {} -impl Objective for u32 {} -impl Objective for u64 {} -impl Objective for u128 {} -impl Objective for i8 {} -impl Objective for i16 {} -impl Objective for i32 {} -impl Objective for i64 {} -impl Objective for i128 {} -impl Objective for isize {} -impl Objective for usize {} -impl Objective for f32 {} -impl Objective for f64 {} - diff -r 1059e7b52e47 -r ba323d8c0f93 obj/src/network.rs --- a/obj/src/network.rs Sat Jun 03 19:57:46 2023 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,59 +0,0 @@ -//! cfg::config::network -//! -//! Network configuration primitives -use serde::{Deserialize, Serialize}; -use std::{fmt, net::SocketAddr}; - -/// Network configuration -#[derive(Serialize, Deserialize, Hash, Debug, PartialEq, Clone)] -pub struct NetworkConfig { - /// a socket to bind - pub socket: SocketAddr, - /// a proxy to forward packets from - pub proxy: Option, - /// tunnel to use - pub tunnel: Option, - /// network engine to attach - pub engine: EngineType, - /// peers to register AOT - pub peers: Option>, -} - -impl Default for NetworkConfig { - fn default() -> Self { - NetworkConfig { - socket: "127.0.0.1:0".parse().unwrap(), - proxy: None, - tunnel: None, - engine: EngineType::default(), - peers: None, - } - } -} - -#[derive(Serialize, Deserialize, Hash, Debug, PartialEq, Clone)] -pub enum EngineType { - Quic, - Http, - Dns, - Ssh, - Uds, -} - -impl Default for EngineType { - fn default() -> Self { - Self::Http - } -} - -impl std::fmt::Display for EngineType { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - EngineType::Quic => write!(f, "quic"), - EngineType::Http => write!(f, "http"), - EngineType::Dns => write!(f, "dns"), - EngineType::Ssh => write!(f, "ssh"), - EngineType::Uds => write!(f, "uds"), - } - } -} diff -r 1059e7b52e47 -r ba323d8c0f93 obj/src/types.rs --- a/obj/src/types.rs Sat Jun 03 19:57:46 2023 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,70 +0,0 @@ -//! obj/src/types.rs --- OBJ type descriptions used by our demo -use crate::{Deserialize, Objective, Result, Serialize}; -use std::collections::HashMap; - -/// APPLICATION TYPES -#[derive(Serialize,Deserialize,Default)] -pub enum Service { - Weather, - Stocks, - Dynamic(Vec), - Custom(CustomService), - #[default] - Bench, -} - -impl Objective for Service {} - -impl From<&str> for Service { - fn from(value: &str) -> Self { - match value { - "weather" => Service::Weather, - "stocks" => Service::Stocks, - "bench" => Service::Bench, - s => { - if s.contains(",") { - let x = s.split(","); - Service::Dynamic( - x.map(|y| Service::Custom(y.into())) - .collect::>(), - ) - } else { - Service::Custom(s.into()) - } - } - } - } -} - -#[derive(Serialize,Deserialize,Default)] -pub struct CustomService { - name: String, - registry: HashMap>, -} - -impl Objective for CustomService {} -impl From for Service { - fn from(value: CustomService) -> Self { - Service::Custom(value) - } -} -impl From<&str> for CustomService { - fn from(value: &str) -> Self { - let name = value.to_owned(); - let registry = HashMap::new(); - CustomService { name, registry } - } -} - -#[derive(Serialize, Deserialize,Default)] -pub struct Complex { - data: X, - stack: Vec, - registry: HashMap>, -} - -impl Objective for Complex {} - -pub fn generate_complex() -> Result> { - Ok(Complex::::from_json_str("hi")?) -} diff -r 1059e7b52e47 -r ba323d8c0f93 package.lisp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/package.lisp Sat Jun 03 22:48:46 2023 -0400 @@ -0,0 +1,75 @@ +;; demo package.lisp +(defpackage :demo + (:use #:cl #:cffi) + (:local-nicknames + ;; (#:rdb #:cl-rocksdb) + (#:v #:org.shirakumo.verbose) + (#:bt #:bordeaux-threads) + (#:cli #:clingon)) + ;; db.lisp + ;; (:export + ;; #:create-options + ;; #:destroy-options + ;; #:increase-parallelism + ;; #:optimize-level-style-compaction + ;; #:set-create-if-missing + ;; #:create-writeoptions + ;; #:destroy-writeoptions + ;; #:create-readoptions + ;; #:destroy-readoptions + ;; #:open-db + ;; #:close-db + ;; #:cancel-all-background-work + ;; #:put-kv + ;; #:put-kv-str + ;; #:get-kv + ;; #:get-kv-str + ;; #:create-iter + ;; #:destroy-iter + ;; #:move-iter-to-first + ;; #:move-iter-forward + ;; #:move-iter-backword + ;; #:valid-iter-p + ;; #:iter-key + ;; #:iter-key-str + ;; #:iter-value + ;; #:iter-value-str + ;; #:with-open-db + ;; #:with-iter) + ;; demo.lisp + (:export + #:main + #:demo-path + #:db-path + #:cli-opts + #:cli-handler + #:cli-cmd) + ;; ui.lisp + (:export + #:on-new-window + #:start-ui) + ;; tk.lisp + (:export + #:source-dir + #:random-id + #:scan-dir + #:mkstr + #:symb + #:sbq-reader) + ;; rs.lisp + (:export + #:*cargo-target* + #:*rs-macros* + #:rs-defmacro + #:rs-macroexpand-1 + #:rs-macroexpand) + ;; ffi.lisp + ;; (:export + ;; #:quiche-lib-path + ;; #:rocksdb-lib-path + ;; #:demo-lib-path + ;; #:find-rs-cdylib + ;; #:install-demo-lib + ;; #:install-quiche-lib + ;; #:install-rocksdb-lib) +) diff -r 1059e7b52e47 -r ba323d8c0f93 pkg.lisp --- a/pkg.lisp Sat Jun 03 19:57:46 2023 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,78 +0,0 @@ -#| -demo -> (demo:main) -|# -(defpackage :demo - (:use #:cl #:cffi) - (:local-nicknames - ;; (#:rdb #:cl-rocksdb) - (#:v #:org.shirakumo.verbose) - (#:bt #:bordeaux-threads) - (#:cli #:clingon)) - ;; db.lisp - ;; (:export - ;; #:create-options - ;; #:destroy-options - ;; #:increase-parallelism - ;; #:optimize-level-style-compaction - ;; #:set-create-if-missing - ;; #:create-writeoptions - ;; #:destroy-writeoptions - ;; #:create-readoptions - ;; #:destroy-readoptions - ;; #:open-db - ;; #:close-db - ;; #:cancel-all-background-work - ;; #:put-kv - ;; #:put-kv-str - ;; #:get-kv - ;; #:get-kv-str - ;; #:create-iter - ;; #:destroy-iter - ;; #:move-iter-to-first - ;; #:move-iter-forward - ;; #:move-iter-backword - ;; #:valid-iter-p - ;; #:iter-key - ;; #:iter-key-str - ;; #:iter-value - ;; #:iter-value-str - ;; #:with-open-db - ;; #:with-iter) - ;; demo.lisp - (:export - #:main - #:demo-path - #:db-path - #:cli-opts - #:cli-handler - #:cli-cmd) - ;; ui.lisp - (:export - #:on-new-window - #:start-ui) - ;; tk.lisp - (:export - #:source-dir - #:random-id - #:scan-dir - #:mkstr - #:symb - #:sbq-reader) - ;; rs.lisp - (:export - #:*cargo-target* - #:*rs-macros* - #:rs-defmacro - #:rs-macroexpand-1 - #:rs-macroexpand) - ;; ffi.lisp - ;; (:export - ;; #:quiche-lib-path - ;; #:rocksdb-lib-path - ;; #:demo-lib-path - ;; #:find-rs-cdylib - ;; #:install-demo-lib - ;; #:install-quiche-lib - ;; #:install-rocksdb-lib) - ) diff -r 1059e7b52e47 -r ba323d8c0f93 readme.org --- a/readme.org Sat Jun 03 19:57:46 2023 -0400 +++ b/readme.org Sat Jun 03 22:48:46 2023 -0400 @@ -1,5 +1,22 @@ -#+TITTLE: cl-demo -This is a demo software suite which showcases the power of Common Lisp and Rust. +#+TITTLE: Demo +Welcome to our first demo system. What you will find here is a modular +client-server software stack which can be extended and customized by +the user at runtime. + +* How it works +The backend services are written in Rust and controlled by a simple +messaging protocol. Services provide common runtime capabilities known +as the /core protocol/ but are specialized on a unique /service type/ +which may in turn register their own /custom protocols/ (via core). + +Services are capable of dispatching data directly to clients, or +storing data in the /database/ (TBD). + +The frontend clients are pre-dominantly written in Common Lisp and +come in many shapes and sizes. There is a cli-client, web-client +(CLOG), docker-client (archlinux, stumpwm, McCLIM), and native-client +which also compiles to WASM (slint-rs). + * Guide ** Build - *install dependencies* @@ -11,21 +28,19 @@ - on MacOS :: =brew install sbcl= - on Windows :: download from and figure it out. - - Quiche - - RocksDB - *make executables* \\ Simply run =make build=. Read the ~makefile~ and change the options as needed. - M :: Mode (debug, release) - L :: Lisp (sbcl, cmucl, ccl) -- P :: Python (python3, python2) +- C :: Config (default.cfg) ** Run #+begin_src shell - make build - ./out/demo + ./demo -i #+end_src ** Config -Configs can be specified in JSON, TOML, RON, or of course SEXP. +Configs can be specified in JSON, TOML, RON, or of course SEXP. See +=default.cfg= for an example. ** Play The high-level user interface is presented as a multi-modal GUI application which adapts to the specific application /instances/ diff -r 1059e7b52e47 -r ba323d8c0f93 rs.lisp --- a/rs.lisp Sat Jun 03 19:57:46 2023 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,78 +0,0 @@ -;;; RUST DSL - -;; So basically, this was born out of personal frustration with how -;; cbindgen and Rust macros work (they don't). Rust macros in general -;; are something of a pain in my opinion, so I thought why not just -;; generate Rust code from Lisp instead? - -(in-package :demo) - -(defvar *cargo-target* #p"/Users/ellis/dev/otom8/demo/target/") -(defvar *rs-macros* nil) - -;; TODO gensyms -(defmacro rs-defmacro (name args &body body) - "Define a macro which can be used within the body of a 'with-rs' form." - `(prog1 - (defmacro ,name ,@(mapcar #`(,a1) args) ,@body) - (push ',name *rs-macros*))) - -(defun rs-mod-form (crate &optional mods pub) - "Generate a basic mod form (CRATE . [MODS] [PUB])" - `(,crate ,mods ,pub)) - -(defmacro with-rs-env (imports &body body) - "Generate an environment for use within a Rust generator macro." - `(let ((imports ,(mapcar #'rs-mod-form imports))) - (format nil "~A~&~A" imports ',body))) - -(defun rs-use (crate &optional mods pub) - "Generate a single Rust use statement." - (concatenate - 'string - (if pub "pub " "") - "use " crate "::{" - (cond - ((consp mods) - (reduce - (lambda (x y) (format nil "~A,~A" x y)) - mods)) - (t mods)) - "};")) - -(defun rs-mod (mod &optional pub) - "Generate a single Rust mod statement." - (concatenate - 'string - (if pub "pub " "") - "mod " mod ";")) - -(defun rs-imports (&rest imports) - "Generate a string of Rust 'use' statements." - (cond - ((consp imports) - (mapcar (lambda (x) (apply #'rs-use (apply #'rs-mod-form x))) imports)) - (t imports))) - -(defmacro rs-extern-c-fn (name args &optional pub unsafe no-mangle &body body) - "Generate a Rust extern 'C' fn." - `(concatenate - 'string - ,(when no-mangle (format nil "#[no_mangle]~&")) - ,(when pub "pub ") - ,(when unsafe "unsafe ") - "extern \"C\" fn " ,name "(" - ,(cond - ((consp args) (reduce (lambda (x y) (format nil "~A,~A" x y)) args)) - (t args)) - ")" "{" ,@body "}")) - -(defun rs-obj-impl (obj) - "Implement Objective for give OBJ." - (format nil "impl Objective for ~A {};" obj)) - -;; (defun rs-macroexpand-1 (form &optional env)) - -;; (defun rs-macroexpand (env &rest body) - -;;; diff -r 1059e7b52e47 -r ba323d8c0f93 src/build.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/build.rs Sat Jun 03 22:48:46 2023 -0400 @@ -0,0 +1,17 @@ +use std::env; +use std::fs::create_dir; +use std::path::PathBuf; +fn main() { + let crate_dir: PathBuf = env::var("CARGO_MANIFEST_DIR") + .expect("CARGO_MANIFEST_DIR env var is not defined") + .into(); + // let mpk_py = "build.py"; + let build_dir = crate_dir.join("ffi/"); + if !build_dir.exists() { + create_dir(&build_dir).unwrap(); + } + cbindgen::generate(crate_dir) + .expect("Unable to find cbindgen.toml configuration file") + .write_to_file(build_dir.join("demo.h")); + +} diff -r 1059e7b52e47 -r ba323d8c0f93 src/cbindgen.toml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cbindgen.toml Sat Jun 03 22:48:46 2023 -0400 @@ -0,0 +1,14 @@ +include_guard = "demo_h" +autogen_warning = "/* DO NOT TOUCH */" +include_version = true +language = "C" +cpp_compat = true +line_length = 88 +documentation = true +[parse] +parse_deps = true +include = ["obj","fig","libc"] +extra_bindings = ["obj","fig","libc"] +#expand = ["demo","obj"] +#[parse.expand] +#crates = ["demo"] \ No newline at end of file diff -r 1059e7b52e47 -r ba323d8c0f93 src/cfg.lisp diff -r 1059e7b52e47 -r ba323d8c0f93 src/crates/obj/Cargo.toml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/crates/obj/Cargo.toml Sat Jun 03 22:48:46 2023 -0400 @@ -0,0 +1,27 @@ +[package] +name = "obj" +version = "0.1.0" +edition = "2021" +[features] +oauth = ["yup-oauth2"] + +[dependencies] +ron = "0.7.0" +bincode = "1.3.3" +serde_json = "1.0.68" +serde = { version = "1.0.130", features = ["derive"] } +chrono = { version = "0.4.19", features = ["serde"] } +mime = "0.3.16" +regex = "1.5.4" +rusty_ulid = "0.11.0" +uuid = { version = "0.8", features = ["serde"] } +yup-oauth2 = { version = "5.1.0", optional = true } +blake3 = "1.0.0" +hashbrown = "0.11.2" +rand = "0.8.0" +sha2 = "0.9.5" +hex = "0.4.3" +ulid = "1.0.0" + +[target.'cfg(target_arch = "wasm32")'.dependencies] +uuid = { version = "0.8", features = ["wasm-bindgen"] } diff -r 1059e7b52e47 -r ba323d8c0f93 src/crates/obj/proc_macros/Cargo.toml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/crates/obj/proc_macros/Cargo.toml Sat Jun 03 22:48:46 2023 -0400 @@ -0,0 +1,10 @@ +[package] +name = "proc_macros" +version = "0.1.0" +edition = "2021" +[lib] +proc-macro = true +[dependencies] +quote = "1.0" +proc-macro2 = "1.0" +syn = "1.0" \ No newline at end of file diff -r 1059e7b52e47 -r ba323d8c0f93 src/crates/obj/proc_macros/src/derive.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/crates/obj/proc_macros/src/derive.rs Sat Jun 03 22:48:46 2023 -0400 @@ -0,0 +1,5 @@ +mod derive; +use proc_macro::TokenStream; +pub fn derive_static_type(input: TokenStream) -> TokenStream { + derive::derive_static_type(input) +} diff -r 1059e7b52e47 -r ba323d8c0f93 src/crates/obj/proc_macros/src/lib.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/crates/obj/proc_macros/src/lib.rs Sat Jun 03 22:48:46 2023 -0400 @@ -0,0 +1,1 @@ + diff -r 1059e7b52e47 -r ba323d8c0f93 src/crates/obj/src/auth.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/crates/obj/src/auth.rs Sat Jun 03 22:48:46 2023 -0400 @@ -0,0 +1,72 @@ +//! Auth Configs +use serde::{Deserialize, Serialize}; + +#[cfg(feature = "oauth")] +use yup_oauth2::ApplicationSecret; + +#[derive(Serialize, Deserialize, Debug, Default, Hash)] +pub struct AuthConfig { + pub provider: String, + #[cfg(feature = "oauth")] + pub oauth: Option, + pub ssh: Option, + pub pw: Option, +} + +#[derive(Serialize, Deserialize, Default, Debug, Hash)] +pub struct PasswordConfig(String, String); + +#[cfg(feature = "oauth")] +#[derive(Serialize, Deserialize, Hash, Debug, PartialEq, Clone, Default)] +pub struct Oauth2Config { + pub client_id: String, + pub client_secret: String, + pub redirect_uris: Vec, + pub auth_uri: String, + pub token_uri: String, + pub project_id: Option, //for apptoken + pub client_email: Option, + /// The URL of the public x509 certificate, used to verify the signature on + /// JWTs, such as ID tokens, signed by the authentication provider. + pub auth_provider_x509_cert_url: Option, + /// The URL of the public x509 certificate, used to verify JWTs signed by the + /// client. + pub client_x509_cert_url: Option, +} + +#[cfg(feature = "oauth")] +impl From for Oauth2Config { + fn from(shh: ApplicationSecret) -> Self { + Oauth2Config { + client_id: shh.client_id, + client_secret: shh.client_secret, + redirect_uris: shh.redirect_uris, + auth_uri: shh.auth_uri, + token_uri: shh.token_uri, + project_id: shh.project_id, + client_email: shh.client_email, + auth_provider_x509_cert_url: shh.auth_provider_x509_cert_url, + client_x509_cert_url: shh.client_x509_cert_url, + } + } +} + +#[cfg(feature = "oauth")] +impl From for ApplicationSecret { + fn from(cfg: Oauth2Config) -> Self { + ApplicationSecret { + client_id: cfg.client_id, + client_secret: cfg.client_secret, + redirect_uris: cfg.redirect_uris, + auth_uri: cfg.auth_uri, + token_uri: cfg.token_uri, + project_id: cfg.project_id, + client_email: cfg.client_email, + auth_provider_x509_cert_url: cfg.auth_provider_x509_cert_url, + client_x509_cert_url: cfg.client_x509_cert_url, + } + } +} + +#[derive(Serialize, Deserialize, Hash, Debug, PartialEq, Clone, Default)] +pub struct SshConfig {} diff -r 1059e7b52e47 -r ba323d8c0f93 src/crates/obj/src/cfg.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/crates/obj/src/cfg.rs Sat Jun 03 22:48:46 2023 -0400 @@ -0,0 +1,51 @@ +/// common trait for all config modules. This trait provides functions +/// for de/serializing to/from RON, updating fields, and formatting. +use serde::{Serialize, Deserialize}; +use crate::Objective; +use std::collections::HashMap as M; +use std::path::PathBuf; +use std::string::String as S; +use std::error::Error as E; +use std::boxed::Box as B; +type R = std::result::Result>; + +pub trait Configure: Objective { + fn update(&self) -> R<()> { + Ok(()) + } +} + +#[derive(Serialize, Deserialize, Debug, Default)] +pub struct ShellConfig { + pub env: M, + pub cmds: M, + pub shell: ShellType, +} + +impl Objective for ShellConfig {} + +#[derive(Serialize, Deserialize, Debug, Hash, Default)] +pub enum ShellType { + #[default] + Bash, + Zsh, + Sh, +} + +#[derive(Serialize, Deserialize, Debug, Default)] +pub enum EditorType { + #[default] + Emacs, + Vi, + Nano, +} + +#[derive(Serialize, Deserialize, Debug, Default)] +pub struct EditorConfig { + pub editor: EditorType, + pub cmds: M, + pub init_file: PathBuf, +} + +#[cfg(test)] +mod tests; diff -r 1059e7b52e47 -r ba323d8c0f93 src/crates/obj/src/database.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/crates/obj/src/database.rs Sat Jun 03 22:48:46 2023 -0400 @@ -0,0 +1,18 @@ +//! cfg::config::database +//! +//! Database configuration primitives +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize, Debug, Hash, PartialEq, Eq)] +pub struct DatabaseConfig { + engine: DatabaseType, + path: String, + cfs: Vec, +} + +#[derive(Serialize, Deserialize, Debug, Hash, PartialEq, Eq)] +pub enum DatabaseType { + RocksDB, + Postgres, + Alch, +} diff -r 1059e7b52e47 -r ba323d8c0f93 src/crates/obj/src/err.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/crates/obj/src/err.rs Sat Jun 03 22:48:46 2023 -0400 @@ -0,0 +1,81 @@ +//! obj errors +use std::{fmt, io}; + +/// obj Result wrapper +pub type Result = std::result::Result; + +/// obj Error type +#[derive(Debug)] +pub enum Error { + Message(String), + Ron(ron::error::Error), + Json(serde_json::error::Error), + Io(io::Error), + Bincode(bincode::Error), + Utf8(std::string::FromUtf8Error), + Parse(std::string::ParseError), +} + +impl serde::ser::Error for Error { + fn custom(msg: T) -> Self { + Error::Message(msg.to_string()) + } +} + +impl serde::de::Error for Error { + fn custom(msg: T) -> Self { + Error::Message(msg.to_string()) + } +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Error::Message(msg) => f.write_str(msg), + Error::Io(ref err) => write!(f, "obj IO error: {}", err), + Error::Ron(ref err) => write!(f, "obj Ron error: {}", err), + Error::Json(ref err) => write!(f, "obj Json error: {}", err), + Error::Bincode(ref err) => write!(f, "obj Bincode error: {}", err), + Error::Utf8(ref err) => write!(f, "obj Utf8 error: {}", err), + Error::Parse(ref err) => write!(f, "obj Parse error: {}", err), + } + } +} + +impl From for Error { + fn from(e: io::Error) -> Self { + Error::Io(e) + } +} + +impl From for Error { + fn from(e: std::string::ParseError) -> Self { + Error::Parse(e) + } +} + +impl From for Error { + fn from(err: std::string::FromUtf8Error) -> Self { + Error::Utf8(err) + } +} + +impl From for Error { + fn from(e: ron::Error) -> Self { + Error::Ron(e) + } +} + +impl From for Error { + fn from(e: serde_json::Error) -> Self { + Error::Json(e) + } +} + +impl From for Error { + fn from(e: bincode::Error) -> Self { + Error::Bincode(e) + } +} + +impl std::error::Error for Error {} diff -r 1059e7b52e47 -r ba323d8c0f93 src/crates/obj/src/hash.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/crates/obj/src/hash.rs Sat Jun 03 22:48:46 2023 -0400 @@ -0,0 +1,51 @@ +//! hash - wrapper for hash algorithms and types + +pub use blake3::{derive_key, hash, keyed_hash, Hash as B3Hash, Hasher as B3Hasher, OutputReader}; +pub use hex; +pub use sha2::Sha512; + +pub use std::hash::{Hash, Hasher}; + +pub const KEY_LEN: usize = 32; +pub const OUT_LEN: usize = 32; +pub const OUT_LEN_HEX: usize = OUT_LEN * 2; + +#[cfg(test)] +mod tests { + use crate::*; + use super::*; + #[test] + fn id_state_hash() { + let id = id::Id(vec![0; KEY_LEN]); + let hash = id.state_hash(&mut B3Hasher::new()); + assert_eq!(hash, id.state_hash(&mut B3Hasher::new())); + } + + #[test] + fn id_hex() { + let id = id::Id(vec![255; KEY_LEN]); + + assert_eq!( + hex::decode("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap(), + id.0 + ); + } + + #[test] + fn rand_id() { + let id = id::Id::rand(); + let hash = id.state_hash(&mut B3Hasher::new()); + assert_eq!(hash, id.state_hash(&mut B3Hasher::new())); + } + + #[test] + fn random_demon_id_is_valid() { + use id::PeerId; + for _ in 0..5000 { + let did = PeerId::rand(); + let did2 = PeerId::rand(); + assert_eq!(did, did); + assert_ne!(did, did2); + } + } +} diff -r 1059e7b52e47 -r ba323d8c0f93 src/crates/obj/src/id.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/crates/obj/src/id.rs Sat Jun 03 22:48:46 2023 -0400 @@ -0,0 +1,120 @@ +use std::{fmt, str::FromStr}; +use serde::{Serialize, Deserialize}; +pub use uuid::Uuid; +pub use ulid::Ulid; +use rand::Rng; +use crate::hash::{KEY_LEN,OUT_LEN,B3Hasher}; +/// a simple Id abstraction +#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Serialize, Deserialize, Hash)] +pub struct Id(pub Vec); + +impl Id { + pub fn rand() -> Self { + let mut rng = rand::thread_rng(); + let vals: Vec = (0..KEY_LEN).map(|_| rng.gen_range(0..u8::MAX)).collect(); + Id(vals) + } + + pub fn state_hash(&self, state: &mut B3Hasher) -> Self { + let mut output = vec![0; OUT_LEN]; + state.update(&self.0); + let mut res = state.finalize_xof(); + res.fill(&mut output); + Id(output) + } + + pub fn to_hex(&self) -> String { + hex::encode(&self.0) + } +} + +/// PeerId +/// +/// identifies a unique Peer +#[derive(Clone, Eq, Hash, Ord, PartialEq, PartialOrd, Debug)] +pub struct PeerId { + id: [u8; 32], +} + +impl PeerId { + pub fn new() -> Self { + Self::default() + } + + pub fn rand() -> Self { + let pd = rand::thread_rng().gen::<[u8; 32]>(); + Self { id: pd } + } + + pub fn from_bytes(data: &[u8]) -> Self { + let pd = blake3::hash(data); + let hash = pd.as_bytes(); + Self { id: *hash } + } +} + +impl Default for PeerId { + fn default() -> Self { + PeerId { id: [0; 32] } + } +} + +/// Identity trait +/// +/// Defines Identity-related behaviors +pub trait Identity: Sized { + /// return the hashed bytes of an ObjectId + fn id(&self) -> Id; +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub struct ObjectId(u128); + +pub struct NameSpace { + pub prefix: Option, + pub capacity: u64, + pub route: Vec, + pub key: Option, +} + +pub struct Domain { + pub ns: NameSpace, + pub id: Id, +} + +impl From for ObjectId { + fn from(uuid: Uuid) -> Self { + ObjectId(uuid.as_u128()) + } +} + +impl From for ObjectId { + fn from(ulid: Ulid) -> Self { + ObjectId(u128::from(ulid)) + } +} + +impl From for ObjectId { + fn from(src: u128) -> Self { + ObjectId(src) + } +} + +impl FromStr for ObjectId { + type Err = (); + fn from_str(input: &str) -> std::result::Result { + match input { + i => Ok(ObjectId(u128::from(Ulid::from_str(i).unwrap()))), + } + } +} + +impl fmt::Display for ObjectId { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + ObjectId(i) => { + write!(f, "{}", Ulid::from(i)) + } + } + } +} diff -r 1059e7b52e47 -r ba323d8c0f93 src/crates/obj/src/lib.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/crates/obj/src/lib.rs Sat Jun 03 22:48:46 2023 -0400 @@ -0,0 +1,149 @@ +//! obj/src/lib.rs --- Objective type library +#![feature(associated_type_bounds)] +mod err; +pub use err::{Error, Result}; +mod types; +pub use types::*; +pub mod id; +pub mod auth; +pub mod hash; +pub mod network; +pub mod database; +pub use bincode; +pub use ron; +use ron::extensions::Extensions; +use serde::{de::DeserializeOwned, Deserialize, Serialize}; +pub use serde_json; +use std::collections::{BTreeMap, HashMap}; +use std::io; + +/// Objective trait +/// Define Object behaviors, implemented by Objects +pub trait Objective { + fn encode(&self) -> Result> + where + Self: Serialize, + { + Ok(bincode::serialize(self)?) + } + + fn encode_into(&self, writer: W) -> Result<()> + where + W: io::Write, + Self: Serialize, + { + Ok(bincode::serialize_into(writer, self)?) + } + + fn decode<'a>(bytes: &'a [u8]) -> Result + where + Self: Deserialize<'a>, + { + Ok(bincode::deserialize(bytes)?) + } + + fn decode_from(&self, rdr: R) -> Result + where + R: io::Read, + Self: DeserializeOwned, + { + Ok(bincode::deserialize_from(rdr)?) + } + + fn to_ron_writer(&self, writer: W) -> Result<()> + where + W: io::Write, + Self: Serialize, + { + Ok(ron::ser::to_writer_pretty( + writer, + &self, + ron::ser::PrettyConfig::new() + .indentor(" ".to_owned()) + .extensions(Extensions::all()), + )?) + } + + fn to_ron_string(&self) -> Result + where + Self: Serialize, + { + Ok(ron::ser::to_string_pretty( + &self, + ron::ser::PrettyConfig::new().indentor(" ".to_owned()), + )?) + } + + fn from_ron_reader(&self, mut rdr: R) -> Result + where + R: io::Read, + Self: DeserializeOwned, + { + let mut bytes = Vec::new(); + rdr.read_to_end(&mut bytes)?; + Ok(ron::de::from_bytes(&bytes)?) + } + + fn from_ron_str<'a>(s: &'a str) -> Result + where + Self: Deserialize<'a>, + { + Ok(ron::de::from_bytes(s.as_bytes())?) + } + + fn to_json_writer(&self, writer: W) -> Result<()> + where + W: io::Write, + Self: Serialize, + { + // let formatter = serde_json::ser::PrettyFormatter::with_indent(b" "); + Ok(serde_json::ser::to_writer_pretty(writer, &self)?) + } + + fn to_json_string(&self) -> Result + where + Self: Serialize, + { + Ok(serde_json::ser::to_string_pretty(&self)?) + } + + fn from_json_reader(&self, mut rdr: R) -> Result + where + R: io::Read, + Self: DeserializeOwned, + { + let mut bytes = Vec::new(); + rdr.read_to_end(&mut bytes)?; + Ok(serde_json::de::from_slice(&bytes)?) + } + + fn from_json_str<'a>(s: &'a str) -> Result + where + Self: Deserialize<'a>, + { + Ok(serde_json::de::from_slice(s.as_bytes())?) + } +} + +impl Objective for Vec {} +impl Objective for HashMap {} +impl Objective for BTreeMap {} +impl Objective for std::path::PathBuf {} +impl Objective for std::path::Path {} +impl Objective for std::string::String {} +impl Objective for std::any::TypeId {} +impl Objective for u8 {} +impl Objective for u16 {} +impl Objective for u32 {} +impl Objective for u64 {} +impl Objective for u128 {} +impl Objective for i8 {} +impl Objective for i16 {} +impl Objective for i32 {} +impl Objective for i64 {} +impl Objective for i128 {} +impl Objective for isize {} +impl Objective for usize {} +impl Objective for f32 {} +impl Objective for f64 {} + diff -r 1059e7b52e47 -r ba323d8c0f93 src/crates/obj/src/network.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/crates/obj/src/network.rs Sat Jun 03 22:48:46 2023 -0400 @@ -0,0 +1,59 @@ +//! cfg::config::network +//! +//! Network configuration primitives +use serde::{Deserialize, Serialize}; +use std::{fmt, net::SocketAddr}; + +/// Network configuration +#[derive(Serialize, Deserialize, Hash, Debug, PartialEq, Clone)] +pub struct NetworkConfig { + /// a socket to bind + pub socket: SocketAddr, + /// a proxy to forward packets from + pub proxy: Option, + /// tunnel to use + pub tunnel: Option, + /// network engine to attach + pub engine: EngineType, + /// peers to register AOT + pub peers: Option>, +} + +impl Default for NetworkConfig { + fn default() -> Self { + NetworkConfig { + socket: "127.0.0.1:0".parse().unwrap(), + proxy: None, + tunnel: None, + engine: EngineType::default(), + peers: None, + } + } +} + +#[derive(Serialize, Deserialize, Hash, Debug, PartialEq, Clone)] +pub enum EngineType { + Quic, + Http, + Dns, + Ssh, + Uds, +} + +impl Default for EngineType { + fn default() -> Self { + Self::Http + } +} + +impl std::fmt::Display for EngineType { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + EngineType::Quic => write!(f, "quic"), + EngineType::Http => write!(f, "http"), + EngineType::Dns => write!(f, "dns"), + EngineType::Ssh => write!(f, "ssh"), + EngineType::Uds => write!(f, "uds"), + } + } +} diff -r 1059e7b52e47 -r ba323d8c0f93 src/crates/obj/src/types.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/crates/obj/src/types.rs Sat Jun 03 22:48:46 2023 -0400 @@ -0,0 +1,70 @@ +//! obj/src/types.rs --- OBJ type descriptions used by our demo +use crate::{Deserialize, Objective, Result, Serialize}; +use std::collections::HashMap; + +/// APPLICATION TYPES +#[derive(Serialize,Deserialize,Default)] +pub enum Service { + Weather, + Stocks, + Dynamic(Vec), + Custom(CustomService), + #[default] + Bench, +} + +impl Objective for Service {} + +impl From<&str> for Service { + fn from(value: &str) -> Self { + match value { + "weather" => Service::Weather, + "stocks" => Service::Stocks, + "bench" => Service::Bench, + s => { + if s.contains(",") { + let x = s.split(","); + Service::Dynamic( + x.map(|y| Service::Custom(y.into())) + .collect::>(), + ) + } else { + Service::Custom(s.into()) + } + } + } + } +} + +#[derive(Serialize,Deserialize,Default)] +pub struct CustomService { + name: String, + registry: HashMap>, +} + +impl Objective for CustomService {} +impl From for Service { + fn from(value: CustomService) -> Self { + Service::Custom(value) + } +} +impl From<&str> for CustomService { + fn from(value: &str) -> Self { + let name = value.to_owned(); + let registry = HashMap::new(); + CustomService { name, registry } + } +} + +#[derive(Serialize, Deserialize,Default)] +pub struct Complex { + data: X, + stack: Vec, + registry: HashMap>, +} + +impl Objective for Complex {} + +pub fn generate_complex() -> Result> { + Ok(Complex::::from_json_str("hi")?) +} diff -r 1059e7b52e47 -r ba323d8c0f93 src/crates/ui/Cargo.toml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/crates/ui/Cargo.toml Sat Jun 03 22:48:46 2023 -0400 @@ -0,0 +1,22 @@ +[package] +name = "ui" +version = "0.1.0" +edition = "2021" +build = "build.rs" +[lib] +path = "lib.rs" +crate-type = ["rlib","cdylib"] +[[bin]] +name = "demo-ui" +path = "main.rs" +[build-dependencies] +slint-build = "1.0.2" +[dependencies] +obj = {version = "0.1.0",path = "../obj"} +env_logger = "0.10.0" +log = "0.4.17" +slint = "1.0.2" +[target.'cfg(target_arch = "wasm32")'.dependencies] +wasm-bindgen = { version = "0.2" } +web-sys = { version = "0.3", features=["console"] } +console_error_panic_hook = "0.1.5" diff -r 1059e7b52e47 -r ba323d8c0f93 src/crates/ui/build.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/crates/ui/build.rs Sat Jun 03 22:48:46 2023 -0400 @@ -0,0 +1,3 @@ +fn main() { + slint_build::compile("ui.slint").unwrap(); +} diff -r 1059e7b52e47 -r ba323d8c0f93 src/crates/ui/config.slint --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/crates/ui/config.slint Sat Jun 03 22:48:46 2023 -0400 @@ -0,0 +1,3 @@ +export global UiConfig { + in property widgets-disabled: false; +} \ No newline at end of file diff -r 1059e7b52e47 -r ba323d8c0f93 src/crates/ui/img/ayo.jpeg Binary file src/crates/ui/img/ayo.jpeg has changed diff -r 1059e7b52e47 -r ba323d8c0f93 src/crates/ui/img/treez.png Binary file src/crates/ui/img/treez.png has changed diff -r 1059e7b52e47 -r ba323d8c0f93 src/crates/ui/index.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/crates/ui/index.html Sat Jun 03 22:48:46 2023 -0400 @@ -0,0 +1,9 @@ + + + >/canvas> + + + diff -r 1059e7b52e47 -r ba323d8c0f93 src/crates/ui/lib.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/crates/ui/lib.rs Sat Jun 03 22:48:46 2023 -0400 @@ -0,0 +1,69 @@ +#![deny(unsafe_code)] + +#[cfg(target_arch = "wasm32")] +use wasm_bindgen::prelude::*; + +slint::include_modules!(); + +use std::rc::Rc; + +use slint::{Model, StandardListViewItem, VecModel}; + +#[cfg_attr(target_arch = "wasm32", wasm_bindgen(start))] +pub fn run() { + // This provides better error messages in debug mode. + // It's disabled in release mode so it doesn't bloat up the file size. + #[cfg(all(debug_assertions, target_arch = "wasm32"))] + console_error_panic_hook::set_once(); + + let app = App::new().unwrap(); + + let row_data: Rc>> = Rc::new(VecModel::default()); + + for r in 1..101 { + let items = Rc::new(VecModel::default()); + + for c in 1..5 { + items.push(slint::format!("Item {r}.{c}").into()); + } + + row_data.push(items.into()); + } + + app.global::().set_row_data(row_data.clone().into()); + + app.global::().on_sort_ascending({ + let app_weak = app.as_weak(); + let row_data = row_data.clone(); + move |index| { + let row_data = row_data.clone(); + + let sort_model = Rc::new(row_data.sort_by(move |r_a, r_b| { + let c_a = r_a.row_data(index as usize).unwrap(); + let c_b = r_b.row_data(index as usize).unwrap(); + + c_a.text.cmp(&c_b.text) + })); + + app_weak.unwrap().global::().set_row_data(sort_model.into()); + } + }); + + app.global::().on_sort_descending({ + let app_weak = app.as_weak(); + move |index| { + let row_data = row_data.clone(); + + let sort_model = Rc::new(row_data.sort_by(move |r_a, r_b| { + let c_a = r_a.row_data(index as usize).unwrap(); + let c_b = r_b.row_data(index as usize).unwrap(); + + c_b.text.cmp(&c_a.text) + })); + + app_weak.unwrap().global::().set_row_data(sort_model.into()); + } + }); + + app.run().unwrap(); +} diff -r 1059e7b52e47 -r ba323d8c0f93 src/crates/ui/main.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/crates/ui/main.rs Sat Jun 03 22:48:46 2023 -0400 @@ -0,0 +1,4 @@ +use ui::run; +fn main() { + run(); +} diff -r 1059e7b52e47 -r ba323d8c0f93 src/crates/ui/pages.slint --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/crates/ui/pages.slint Sat Jun 03 22:48:46 2023 -0400 @@ -0,0 +1,7 @@ +import { AboutPage } from "pages/about.slint"; +import { ControlsPage } from "pages/controls.slint"; +import { ListViewPage } from "pages/list_view.slint"; +import { TableViewPage, TableViewPageAdapter } from "pages/table_view.slint"; +import { TextEditPage } from "pages/text_edit.slint"; + +export { AboutPage, ControlsPage, ListViewPage, TextEditPage, TableViewPage, TableViewPageAdapter } \ No newline at end of file diff -r 1059e7b52e47 -r ba323d8c0f93 src/crates/ui/pages/about.slint --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/crates/ui/pages/about.slint Sat Jun 03 22:48:46 2023 -0400 @@ -0,0 +1,10 @@ +import { AboutSlint } from "std-widgets.slint"; +import { UiConfig } from "../config.slint"; +import { Page } from "page.slint"; + +export component AboutPage inherits Page { + title: "About"; + description: "Are you curious now? Check out the docs and gettings start from the Github repository and the website https://slint-ui.com and try it yourself."; + + AboutSlint {} +} \ No newline at end of file diff -r 1059e7b52e47 -r ba323d8c0f93 src/crates/ui/pages/controls.slint --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/crates/ui/pages/controls.slint Sat Jun 03 22:48:46 2023 -0400 @@ -0,0 +1,145 @@ +import { Button, GroupBox, SpinBox, ComboBox, CheckBox, LineEdit, TabWidget, VerticalBox, HorizontalBox, + Slider, SpinBox } from "std-widgets.slint"; +import { UiConfig } from "../config.slint"; +import { Page } from "page.slint"; + +export component ControlsPage inherits Page { + title: "Controls"; + description: "This page gives an overview of the default widget set provided by Slint. The widgets are available in different styles native, fluent-(dark/light) and material-(dark/light). The widgets can be imported from \"std-widgets.slint\"."; + + GroupBox { + vertical-stretch: 0; + title: "Buttons"; + + HorizontalLayout { + spacing: 8px; + alignment: start; + + Button { + text: "Regular Button"; + enabled: !UiConfig.widgets-disabled; + } + + Button { + text: "Button with Icon"; + icon: @image-url("../img/treez.png"); + enabled: !UiConfig.widgets-disabled; + } + + Button { + checkable: true; + text: self.checked ? "ON" : "OFF"; + enabled: !UiConfig.widgets-disabled; + } + } + } + + GroupBox { + title: "CheckBox - SpinBox - ComboBox"; + vertical-stretch: 0; + + HorizontalBox { + alignment: start; + checkbox := CheckBox { + text: checkbox.checked ? "(checked)" : "(unchecked)"; + checked: true; + enabled: !UiConfig.widgets-disabled; + } + + + SpinBox { + vertical-stretch: 0; + value: 42; + enabled: !UiConfig.widgets-disabled; + } + + ComboBox { + model: ["Select Something", "From this", "Combobox"]; + enabled: !UiConfig.widgets-disabled; + } + } + + + } + + GroupBox { + title: "LineEdit"; + vertical-stretch: 0; + + LineEdit { + placeholder-text: "Enter some text"; + enabled: !UiConfig.widgets-disabled; + } + } + + GroupBox { + title: "Slider"; + vertical-stretch: 0; + + Slider { + min-width: 160px; + minimum: -100; + maximum: 100; + value: 42; + enabled: !UiConfig.widgets-disabled; + } + } + + GroupBox { + title: "TabWidget"; + + TabWidget { + Tab { + title: "Tab 1"; + + VerticalBox { + alignment: start; + + GroupBox { + title: "Content of tab 1"; + + HorizontalBox { + alignment: start; + + Button { + text: "Click me"; + enabled: !UiConfig.widgets-disabled; + } + } + } + } + } + + Tab { + title: "Tab 2"; + + VerticalBox { + alignment: start; + + GroupBox { + title: "Content of tab 2"; + + VerticalBox { + alignment: start; + + CheckBox { + text: "Check me"; + enabled: !UiConfig.widgets-disabled; + } + } + } + } + } + + Tab { + title: "Tab 3"; + + VerticalBox { + Text { + text: "Content of tab 3"; + } + } + } + } + } +} \ No newline at end of file diff -r 1059e7b52e47 -r ba323d8c0f93 src/crates/ui/pages/list_view.slint --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/crates/ui/pages/list_view.slint Sat Jun 03 22:48:46 2023 -0400 @@ -0,0 +1,45 @@ +import { HorizontalBox, VerticalBox, ListView, StandardListView, GroupBox } from "std-widgets.slint"; +import { UiConfig } from "../config.slint"; +import { Page } from "page.slint"; + +export component ListViewPage inherits Page { + title: "ListView"; + description: "ListViews can be used to display a list of elements. The StandardListBox is like the default ListView just with a default text based definition of the visual items. Both can be imported from \"std-widgets.slint\""; + + HorizontalBox { + vertical-stretch: 1; + GroupBox { + title: "ListView"; + + ListView { + vertical-stretch: 0; + for i in [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] : HorizontalBox { + Image { + width: 24px; + source: @image-url("../img/ayo.jpeg"); + } + Text { + text: "Item " + i; + } + } + } + } + + GroupBox { + title: "StandardListView"; + vertical-stretch: 0; + + StandardListView { + model: [ + {text: "Lorem"}, {text: "ipsum"},{text: "dolor"},{text: "sit"},{text: "amet"},{text: "consetetur"}, + {text: "Lorem"}, {text: "ipsum"},{text: "dolor"},{text: "sit"},{text: "amet"},{text: "consetetur"}, + {text: "Lorem"}, {text: "ipsum"},{text: "dolor"},{text: "sit"},{text: "amet"},{text: "consetetur"}, + {text: "Lorem"}, {text: "ipsum"},{text: "dolor"},{text: "sit"},{text: "amet"},{text: "consetetur"}, + {text: "Lorem"}, {text: "ipsum"},{text: "dolor"},{text: "sit"},{text: "amet"},{text: "consetetur"}, + {text: "Lorem"}, {text: "ipsum"},{text: "dolor"},{text: "sit"},{text: "amet"},{text: "consetetur"}, + {text: "Lorem"}, {text: "ipsum"},{text: "dolor"},{text: "sit"},{text: "amet"},{text: "consetetur"}, + ]; + } + } + } +} \ No newline at end of file diff -r 1059e7b52e47 -r ba323d8c0f93 src/crates/ui/pages/page.slint --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/crates/ui/pages/page.slint Sat Jun 03 22:48:46 2023 -0400 @@ -0,0 +1,27 @@ +import { CheckBox, GridBox, ListView, ScrollView, VerticalBox } from "std-widgets.slint"; + +import { UiConfig } from "../config.slint"; + +export component Page inherits VerticalBox { + in property title: "title"; + in property description: "description"; + + HorizontalLayout { + height: 24px; + Text { + font-size: 20px; + text <=> root.title; + } + + // Spacer + Rectangle {} + + CheckBox { + horizontal-stretch: 0; + text: "Disable widgets"; + checked <=> UiConfig.widgets-disabled; + } + } + + @children +} \ No newline at end of file diff -r 1059e7b52e47 -r ba323d8c0f93 src/crates/ui/pages/table_view.slint --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/crates/ui/pages/table_view.slint Sat Jun 03 22:48:46 2023 -0400 @@ -0,0 +1,48 @@ +import { HorizontalBox, VerticalBox, StandardTableView, GroupBox} from "std-widgets.slint"; +import { UiConfig } from "../config.slint"; +import { Page } from "page.slint"; + +export global TableViewPageAdapter { + callback sort_ascending(int); + callback sort_descending(int); + in property <[[StandardListViewItem]]> row_data: [ + [ { text: "Item 1.1" }, { text: "Item 1.2" }, { text: "Item 1.3" }, { text: "Item 1.4" }, ], + [ { text: "Item 2.1" }, { text: "Item 2.2" }, { text: "Item 2.3" }, { text: "Item 2.4" }, ], + [ { text: "Item 3.1" }, { text: "Item 3.2" }, { text: "Item 3.3" }, { text: "Item 3.4" }, ], + [ { text: "Item 4.1" }, { text: "Item 4.2" }, { text: "Item 4.3" }, { text: "Item 4.4" }, ], + [ { text: "Item 5.1" }, { text: "Item 5.2" }, { text: "Item 5.3" }, { text: "Item 5.4" }, ], + [ { text: "Item 6.1" }, { text: "Item 6.2" }, { text: "Item 6.3" }, { text: "Item 6.4" }, ], + ]; +} + +export component TableViewPage inherits Page { + title: "TableView"; + description: "StandardTableView can be used to display a list of text elements in columns and rows. It can be imported from \"std-widgets.slint\""; + + HorizontalBox { + vertical-stretch: 1; + + GroupBox { + title: "StandardTableView"; + vertical-stretch: 0; + + StandardTableView { + sort-ascending(index) => { + TableViewPageAdapter.sort_ascending(index); + } + + sort-descending(index) => { + TableViewPageAdapter.sort-descending(index); + } + + columns: [ + { title: "Header 1" }, + { title: "Header 2" }, + { title: "Header 3" }, + { title: "Header 4" }, + ]; + rows: TableViewPageAdapter.row_data; + } + } + } +} \ No newline at end of file diff -r 1059e7b52e47 -r ba323d8c0f93 src/crates/ui/pages/text_edit.slint --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/crates/ui/pages/text_edit.slint Sat Jun 03 22:48:46 2023 -0400 @@ -0,0 +1,32 @@ +import { HorizontalBox, GroupBox, TextEdit } from "std-widgets.slint"; +import { UiConfig } from "../config.slint"; +import { Page } from "page.slint"; + +export component TextEditPage inherits Page { + title: "TextEdit"; + description: "Similar to LineEdit, but can be used to enter several lines of text. The widget can be imported from \"std-widgets.slint\"."; + + HorizontalBox { + GroupBox { + vertical-stretch: 0; + title: "Word-Wrap"; + te1 := TextEdit { + min-width: 200px; + text: "This is our TextEdit widget, which allows for editing text that spans over multiple paragraphs.\nFor example this line starts in a new paragraph.\n\nWhen the amount of lines - due to wrapping and number of paragraphs - exceeds the available vertical height, a vertical scrollbar is shown that allows scrolling.\nYou may want to enter a bit of text here then in order to make them visible."; + wrap: word-wrap; + enabled: !UiConfig.widgets-disabled; + } + } + + GroupBox { + title: "No-Wrap"; + vertical-stretch: 0; + te2 := TextEdit { + min-width: 200px; + text <=> te1.text; + wrap: no-wrap; + enabled: !UiConfig.widgets-disabled; + } + } + } +} \ No newline at end of file diff -r 1059e7b52e47 -r ba323d8c0f93 src/crates/ui/sidebar.slint --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/crates/ui/sidebar.slint Sat Jun 03 22:48:46 2023 -0400 @@ -0,0 +1,127 @@ +import { StyleMetrics } from "std-widgets.slint"; + +component SideBarItem inherits Rectangle { + callback clicked <=> touch.clicked; + in-out property text <=> label.text; + in property selected; + in property has-focus; + + min-height: l.preferred-height; + + state := Rectangle { + opacity: 0; + background: StyleMetrics.window-background; + + animate opacity { duration: 150ms; } + } + + l := HorizontalLayout { + y: (parent.height - self.height) / 2; + padding: StyleMetrics.layout-padding; + spacing: 0px; + + label := Text { + color: StyleMetrics.default-text-color; + vertical-alignment: center; + } + } + + touch := TouchArea { + width: 100%; + height: 100%; + } + + states [ + pressed when touch.pressed : { + state.opacity: 0.8; + } + hover when touch.has-hover : { + state.opacity: 0.6; + } + selected when root.selected : { + state.opacity: 1; + } + focused when root.has-focus : { + state.opacity: 0.8; + } + ] +} + +export component SideBar inherits Rectangle { + in property<[string]> model: []; + out property current-item: 0; + in property title <=> label.text; + out property current-focused: fs.has-focus ? fs.focused-tab : -1; // The currently focused tab + width: 180px; + + forward-focus: fs; + + accessible-role: tab; + accessible-delegate-focus: root.current-focused >= 0 ? root.current-focused : root.current-item; + + Rectangle { + background: StyleMetrics.window-background.darker(0.2); + + fs := FocusScope { + x:0; + width: 0px; // Do not react on clicks + property focused-tab: 0; + + key-pressed(event) => { + if (event.text == "\n") { + root.current-item = root.current-focused; + return accept; + } + if (event.text == Key.UpArrow) { + self.focused-tab = Math.max(self.focused-tab - 1, 0); + return accept; + } + if (event.text == Key.DownArrow) { + self.focused-tab = Math.min(self.focused-tab + 1, root.model.length - 1); + return accept; + } + return reject; + } + + key-released(event) => { + if (event.text == " ") { + root.current-item = root.current-focused; + return accept; + } + return reject; + } + } + } + + VerticalLayout { + padding-top: StyleMetrics.layout-padding; + padding-bottom: StyleMetrics.layout-padding; + spacing: StyleMetrics.layout-spacing; + alignment: start; + + label := Text { + font-size: 16px; + horizontal-alignment: center; + } + + navigation := VerticalLayout { + alignment: start; + vertical-stretch: 0; + for item[index] in root.model : SideBarItem { + has-focus: index == root.current-focused; + text: item; + selected: index == root.current-item; + clicked => { root.current-item = index; } + } + } + + VerticalLayout { + bottom := VerticalLayout { + padding-left: StyleMetrics.layout-padding; + padding-right: StyleMetrics.layout-padding; + + @children + } + } + } +} \ No newline at end of file diff -r 1059e7b52e47 -r ba323d8c0f93 src/crates/ui/ui.slint --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/crates/ui/ui.slint Sat Jun 03 22:48:46 2023 -0400 @@ -0,0 +1,20 @@ +import {CheckBox, StandardListView, StyleMetrics} from "std-widgets.slint"; +import {AboutPage, ControlsPage, ListViewPage, TableViewPage, TableViewPageAdapter, TextEditPage} from "pages.slint"; +import {UiConfig} from "config.slint"; +import {SideBar} from "sidebar.slint"; +export {TableViewPageAdapter} +export component App inherits Window { + title: "Demo"; + icon: @image-url("img/treez.png"); + HorizontalLayout { + side-bar := SideBar { + title: "Demo"; + model: ["Controls", "ListView", "TableView", "TextEdit", "About"]; + } + if(side-bar.current-item == 0) : ControlsPage {} + if(side-bar.current-item == 1) : ListViewPage {} + if(side-bar.current-item == 2) : TableViewPage {} + if(side-bar.current-item == 3) : TextEditPage {} + if(side-bar.current-item == 4) : AboutPage {} + } +} \ No newline at end of file diff -r 1059e7b52e47 -r ba323d8c0f93 src/db.lisp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/db.lisp Sat Jun 03 22:48:46 2023 -0400 @@ -0,0 +1,197 @@ +(in-package :demo) + +(define-foreign-library rocksdb + (:win32 "rocksdb") + (t (:default "librocksdb"))) + +(use-foreign-library rocksdb) + +(defcfun ("rocksdb_options_create" create-options) :pointer) +(defcfun ("rocksdb_options_destroy" destroy-options) :void (options :pointer)) +(defcfun ("rocksdb_options_increase_parallelism" increase-parallelism) :void (opt :pointer) (total-threads :int)) +(defcfun ("rocksdb_options_optimize_level_style_compaction" optimize-level-style-compaction) :void (opt :pointer) (memtable_memory_budget :uint64)) +(defcfun ("rocksdb_options_set_create_if_missing" set-create-if-missing) :void (opt :pointer) (val :boolean)) + +(defcfun ("rocksdb_writeoptions_create" create-writeoptions) :pointer) +(defcfun ("rocksdb_writeoptions_destroy" destroy-writeoptions) :void (opt :pointer)) +(defcfun ("rocksdb_readoptions_create" create-readoptions) :pointer) +(defcfun ("rocksdb_readoptions_destroy" destroy-readoptions) :void (opt :pointer)) + +(defcfun ("rocksdb_open" open-db*) :pointer (opt :pointer) (name :string) (errptr :pointer)) +(defcfun ("rocksdb_close" close-db) :void (opt :pointer)) +(defcfun ("rocksdb_cancel_all_background_work" cancel-all-background-work) :void (db :pointer) (wait :boolean)) + +(defcfun ("rocksdb_put" put*) :void (db :pointer) (options :pointer) (key :pointer) (keylen :unsigned-int) (val :pointer) (vallen :unsigned-int) (errptr :pointer)) +(defcfun ("rocksdb_get" get*) :pointer (db :pointer) (options :pointer) (key :pointer) (keylen :unsigned-int) (vallen :pointer) (errptr :pointer)) + +(defcfun ("rocksdb_create_iterator" create-iter*) :pointer (db :pointer) (opt :pointer)) +(defcfun ("rocksdb_iter_destroy" destroy-iter) :void (iter :pointer)) +(defcfun ("rocksdb_iter_seek_to_first" move-iter-to-first) :void (iter :pointer)) +(defcfun ("rocksdb_iter_valid" valid-iter-p) :boolean (iter :pointer)) +(defcfun ("rocksdb_iter_next" move-iter-forward) :void (iter :pointer)) +(defcfun ("rocksdb_iter_prev" move-iter-backward) :void (iter :pointer)) +(defcfun ("rocksdb_iter_key" iter-key*) :pointer (iter :pointer) (klen-ptr :pointer)) +(defcfun ("rocksdb_iter_value" iter-value*) :pointer (iter :pointer) (vlen-ptr :pointer)) + +(define-condition unable-to-open-db (error) + ((db-path :initarg :db-path + :reader db-path) + (error-message :initarg :error-message + :reader error-message))) + +(defmethod print-object ((obj unable-to-open-db) stream) + (print-unreadable-object (obj stream :type t :identity t) + (format stream "error-message=~A" (error-message obj)))) + +(define-condition unable-to-put-key-value-to-db (error) + ((db :initarg :db + :reader db) + (key :initarg :key + :reader key) + (val :initarg :val + :reader val) + (error-message :initarg :error-message + :reader error-message))) + +(define-condition unable-to-get-value-to-db (error) + ((db :initarg :db + :reader db) + (key :initarg :key + :reader key) + (error-message :initarg :error-message + :reader error-message))) + +(defun open-db (db-path &optional opt) + (unless opt + (setq opt (create-options))) + (let ((errptr (foreign-alloc :pointer))) + (setf (mem-ref errptr :pointer) (null-pointer)) + (let* ((db-path (if (pathnamep db-path) + (namestring db-path) + db-path)) + (db (open-db* opt db-path errptr)) + (err (mem-ref errptr :pointer))) + (unless (null-pointer-p err) + (error 'unable-to-open-db + :db-path db-path + :error-message (foreign-string-to-lisp err))) + db))) + +(defmacro clone-octets-to-foreign (lisp-array foreign-array) + (let ((i (gensym))) + `(loop for ,i from 0 below (length ,lisp-array) + do (setf (mem-aref ,foreign-array :unsigned-char ,i) + (aref ,lisp-array ,i))))) + +(defmacro clone-octets-from-foreign (foreign-array lisp-array len) + (let ((i (gensym))) + `(loop for ,i from 0 below ,len + do (setf (aref ,lisp-array ,i) + (mem-aref ,foreign-array :unsigned-char ,i))))) + +(defun put-kv (db key val &optional opt) + (unless opt + (setq opt (create-writeoptions))) + (with-foreign-objects ((errptr :pointer) + (key* :unsigned-char (length key)) + (val* :unsigned-char (length val))) + (clone-octets-to-foreign key key*) + (clone-octets-to-foreign val val*) + (setf (mem-ref errptr :pointer) (null-pointer)) + (put* db + opt + key* + (length key) + val* + (length val) + errptr) + (let ((err (mem-ref errptr :pointer))) + (unless (null-pointer-p err) + (error 'unable-to-put-key-value-to-db + :db db + :key key + :val val + :error-message (foreign-string-to-lisp err)))))) + +(defun put-kv-str (db key val &optional opt) + (let ((key-octets (babel:string-to-octets key)) + (val-octets (babel:string-to-octets val))) + (put-kv db key-octets val-octets opt))) + +(defun get-kv (db key &optional opt) + (unless opt + (setq opt (create-readoptions))) + + (with-foreign-objects ((val-len-ptr :unsigned-int) + (errptr :pointer) + (key* :unsigned-char (length key))) + (clone-octets-to-foreign key key*) + (setf (mem-ref errptr :pointer) (null-pointer)) + (let ((val (get* db + opt + key* + (length key) + val-len-ptr + errptr))) + (let ((err (mem-ref errptr :pointer))) + (unless (null-pointer-p err) + (error 'unable-to-get-value-to-db + :db db + :key key + :error-message (foreign-string-to-lisp err))) + + (unless (null-pointer-p val) + (let* ((val-len (mem-ref val-len-ptr :unsigned-int)) + (val* (make-array val-len + :element-type '(unsigned-byte 8)))) + (clone-octets-from-foreign val val* val-len) + val*)))))) + +(defun get-kv-str (db key &optional opt) + (let ((key-octets (babel:string-to-octets key))) + (let ((#1=val-octets (get-kv db key-octets opt))) + (when #1# + (babel:octets-to-string #1#))))) + +(defun create-iter (db &optional opt) + (unless opt + (setq opt (create-readoptions))) + (create-iter* db opt)) + +(defun iter-key (iter) + (with-foreign-objects ((klen-ptr :unsigned-int)) + (setf (mem-ref klen-ptr :unsigned-int) 0) + (let* ((key-ptr (iter-key* iter klen-ptr)) + (klen (mem-ref klen-ptr :unsigned-int)) + (key (make-array klen :element-type '(unsigned-byte 8)))) + (clone-octets-from-foreign key-ptr key klen) + key))) + +(defun iter-key-str (iter) + (let ((#1=key-octets (iter-key iter))) + (when #1# + (babel:octets-to-string #1#)))) + +(defun iter-value (iter) + (with-foreign-objects ((len-ptr :unsigned-int)) + (setf (mem-ref len-ptr :unsigned-int) 0) + (let* ((value-ptr (iter-value* iter len-ptr)) + (vlen (mem-ref len-ptr :unsigned-int)) + (value* (make-array vlen :element-type '(unsigned-byte 8)))) + (clone-octets-from-foreign value-ptr value* vlen) + value*))) + +(defun iter-value-str (iter) + (let ((#1=val-octets (iter-value iter))) + (when #1# + (babel:octets-to-string #1#)))) + +(defmacro with-open-db ((db-var db-path &optional opt) &body body) + `(let ((,db-var (open-db ,db-path ,opt))) + (unwind-protect (progn ,@body) + (close-db ,db-var)))) + +(defmacro with-iter ((iter-var db &optional opt) &body body) + `(let ((,iter-var (create-iter ,db ,opt))) + (unwind-protect (progn ,@body) + (destroy-iter ,iter-var)))) diff -r 1059e7b52e47 -r ba323d8c0f93 src/demo.lisp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/demo.lisp Sat Jun 03 22:48:46 2023 -0400 @@ -0,0 +1,45 @@ +;; demo.lisp +(in-package :demo) + +(defparameter demo-path (merge-pathnames "cl-demo" (uiop:temporary-directory))) + +(defvar db-path (merge-pathnames "db" demo-path)) + +(defun cli-opts () + "Returns the top-level CLI options." + (list + (cli:make-option + :string + :description "demo app to run" + :short-name #\x + :long-name "app" + :initial-value "client" + :env-vars '("DEMO_APP") + :key :app) + (cli:make-option + :string + :description "path to config" + :short-name #\c + :long-name "config" + :initial-value "$DEMO_PATH/.fig" + :env-vars '("DEMO_CONFIG")))) + +(defun cli-handler (cmd) + "Handler for the `demo' command." + (let ((app (cli:getopt cmd :app))) + (format t "running: ~A!~%" app))) + +(defun cli-cmd () + "Our demo command." + (cli:make-command + :name "demo" + :description "A collection of demos" + :version "1.0.0" + :authors '("ellis ") + :license "WTFPL" + :options (cli-opts) + :handler #'cli-handler)) + +(defun main () + "A demo of some common-lisp functionality." + (cli:run (cli-cmd))) diff -r 1059e7b52e47 -r ba323d8c0f93 src/ffi.lisp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/ffi.lisp Sat Jun 03 22:48:46 2023 -0400 @@ -0,0 +1,28 @@ +(in-package :demo) +(defparameter quiche-lib-path #p"./ffi/libquiche.dylib") +;;(defparameter rocksdb-lib-path #p"./ffi/librocksdb.dylib") +(defparameter demo-lib-path (find-rs-cdylib "libdemo.dylib")) +(defmacro find-rs-cdylib (name &optional debug) + "Find the rust dll specified by NAME." + (cond + ((uiop:directory-exists-p (merge-pathnames *cargo-target* "release")) + `,(mkstr "./target/release/" name)) + ((uiop:directory-exists-p (merge-pathnames *cargo-target* "debug")) + `,(mkstr "./target/debug/" name)) + (t `(progn + ,(uiop:run-program '("cargo" "build" (unless debug "--release")) :output t) + (find-rs-cdylib ,name ,debug))))) + +(define-foreign-library demo + (:win32 (:default "demo")) + (t (:default "libdemo"))) +(define-foreign-library quiche + (:win32 (:default "quiche")) + (t (:default "libquiche"))) +;; (define-foreign-library rocksdb +;; (:win32 (:default "rocksdb")) +;; (t (:default "librocksdb"))) + +(defun load-libdemo () (load-foreign-library (find-rs-cdylib "libdemo.dylib"))) +(defun install-quiche-lib (&optional path) (load-foreign-library (or path quiche-lib-path))) +;; (defun install-rocksdb-lib (&optional path) (load-foreign-library (or path rocksdb-lib-path))) diff -r 1059e7b52e47 -r ba323d8c0f93 src/gen.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/gen.rs Sat Jun 03 22:48:46 2023 -0400 @@ -0,0 +1,84 @@ +//! demo +pub use fig::*; +pub use obj::*; +use std::ffi::{CStr, CString}; //OsStr,Path + //use std::os::unix::ffi::OsStrExt; +use std::slice; +use libc::{c_char,size_t}; + +#[macro_export] +macro_rules! cdefn { + (free $t:tt $n:tt) => { + #[no_mangle] + pub unsafe extern "C" fn $n(ptr: *mut $t) { + if ptr.is_null() { + return; + } + let _ = Box::from_raw(ptr); + } + }; + (from_string $t:tt $n:tt) => { + #[no_mangle] + pub unsafe extern "C" fn $n(ptr: *const c_char) -> *mut $t { + assert!(!ptr.is_null()); + let p = CStr::from_ptr(ptr).to_str().unwrap(); + Box::into_raw(Box::new(p.into())) + } + }; + (json_string $t:tt $r:tt $w:tt) => { + #[no_mangle] + pub unsafe extern "C" fn $r(ptr: *const c_char) -> *mut $t { + assert!(!ptr.is_null()); + let s = CStr::from_ptr(ptr); + Box::into_raw(Box::new($t::from_json_str(&s.to_str().unwrap()).unwrap())) + } + + #[no_mangle] + pub unsafe extern "C" fn $w(ptr: *const $t) -> *mut c_char { + let p = &*ptr; + let x = p.to_json_string().unwrap(); + CString::new(x.as_str().as_bytes()).unwrap().into_raw() + } + }; + (ron_string $t:tt $r:tt $w:tt) => { + #[no_mangle] + pub unsafe extern "C" fn $r(ptr: *const c_char) -> *mut $t { + assert!(!ptr.is_null()); + let s = CStr::from_ptr(ptr); + Box::into_raw(Box::new($t::from_ron_str(&s.to_str().unwrap()).unwrap())) + } + + #[no_mangle] + pub unsafe extern "C" fn $w(ptr: *const $t) -> *mut c_char { + let p = &*ptr; + let x = p.to_ron_string().unwrap(); + CString::new(x.as_str().as_bytes()).unwrap().into_raw() + } + }; + (bytes $t:tt $r:tt $w:tt) => { + #[no_mangle] + pub unsafe extern "C" fn $r(ptr: *const u8, len: size_t) -> *mut $t { + Box::into_raw(Box::new($t::decode(slice::from_raw_parts(ptr,len)).unwrap())) + } + + #[no_mangle] + pub unsafe extern "C" fn $w(ptr: *const $t) -> *mut u8 { + let p = &*ptr; + let mut x = p.encode().unwrap(); + let r = x.as_mut_ptr(); + std::mem::forget(x); + r + } + } +} + +cdefn!(free Service free_service); +cdefn!(from_string Service service_from_string); +cdefn!(json_string Service service_from_json_string service_to_json_string); +cdefn!(ron_string Service service_from_ron_string service_to_ron_string); +cdefn!(bytes Service service_decode service_encode); +cdefn!(free CustomService free_custom_service); +cdefn!(from_string CustomService custom_service_from_string); +cdefn!(json_string CustomService custom_service_from_json_string custom_service_to_json_string); +cdefn!(ron_string CustomService custom_service_from_ron_string custom_service_to_ron_string); +cdefn!(bytes CustomService custom_service_decode custom_service_encode); diff -r 1059e7b52e47 -r ba323d8c0f93 src/lib.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib.rs Sat Jun 03 22:48:46 2023 -0400 @@ -0,0 +1,117 @@ +//! demo/lib.rs --- generated by DEMO:RS-MACROEXPAND +extern crate libc; +extern crate obj; +use libc::{c_char, size_t}; +use obj::{CustomService, Objective, Service}; +use std::ffi::{CStr, CString}; +use std::slice; +#[no_mangle] +pub unsafe extern "C" fn free_service(ptr: *mut Service) { + if ptr.is_null() { + return; + } + let _ = Box::from_raw(ptr); +} +#[no_mangle] +pub unsafe extern "C" fn service_from_string(ptr: *const c_char) -> *mut Service { + assert!(!ptr.is_null()); + let p = CStr::from_ptr(ptr).to_str().unwrap(); + Box::into_raw(Box::new(p.into())) +} +#[no_mangle] +pub unsafe extern "C" fn service_from_json_string(ptr: *const c_char) -> *mut Service { + assert!(!ptr.is_null()); + let s = CStr::from_ptr(ptr); + Box::into_raw(Box::new( + Service::from_json_str(&s.to_str().unwrap()).unwrap(), + )) +} +#[no_mangle] +pub unsafe extern "C" fn service_to_json_string(ptr: *const Service) -> *mut c_char { + let p = &*ptr; + let x = p.to_json_string().unwrap(); + CString::new(x.as_str().as_bytes()).unwrap().into_raw() +} +#[no_mangle] +pub unsafe extern "C" fn service_from_ron_string(ptr: *const c_char) -> *mut Service { + assert!(!ptr.is_null()); + let s = CStr::from_ptr(ptr); + Box::into_raw(Box::new( + Service::from_ron_str(&s.to_str().unwrap()).unwrap(), + )) +} +#[no_mangle] +pub unsafe extern "C" fn service_to_ron_string(ptr: *const Service) -> *mut c_char { + let p = &*ptr; + let x = p.to_ron_string().unwrap(); + CString::new(x.as_str().as_bytes()).unwrap().into_raw() +} +#[no_mangle] +pub unsafe extern "C" fn service_decode(ptr: *const u8, len: size_t) -> *mut Service { + Box::into_raw(Box::new( + Service::decode(slice::from_raw_parts(ptr, len)).unwrap(), + )) +} +#[no_mangle] +pub unsafe extern "C" fn service_encode(ptr: *const Service) -> *mut u8 { + let p = &*ptr; + let mut x = p.encode().unwrap(); + let r = x.as_mut_ptr(); + std::mem::forget(x); + r +} +#[no_mangle] +pub unsafe extern "C" fn free_custom_service(ptr: *mut CustomService) { + if ptr.is_null() { + return; + } + let _ = Box::from_raw(ptr); +} +#[no_mangle] +pub unsafe extern "C" fn custom_service_from_string(ptr: *const c_char) -> *mut CustomService { + assert!(!ptr.is_null()); + let p = CStr::from_ptr(ptr).to_str().unwrap(); + Box::into_raw(Box::new(p.into())) +} +#[no_mangle] +pub unsafe extern "C" fn custom_service_from_json_string(ptr: *const c_char) -> *mut CustomService { + assert!(!ptr.is_null()); + let s = CStr::from_ptr(ptr); + Box::into_raw(Box::new( + CustomService::from_json_str(&s.to_str().unwrap()).unwrap(), + )) +} +#[no_mangle] +pub unsafe extern "C" fn custom_service_to_json_string(ptr: *const CustomService) -> *mut c_char { + let p = &*ptr; + let x = p.to_json_string().unwrap(); + CString::new(x.as_str().as_bytes()).unwrap().into_raw() +} +#[no_mangle] +pub unsafe extern "C" fn custom_service_from_ron_string(ptr: *const c_char) -> *mut CustomService { + assert!(!ptr.is_null()); + let s = CStr::from_ptr(ptr); + Box::into_raw(Box::new( + CustomService::from_ron_str(&s.to_str().unwrap()).unwrap(), + )) +} +#[no_mangle] +pub unsafe extern "C" fn custom_service_to_ron_string(ptr: *const CustomService) -> *mut c_char { + let p = &*ptr; + let x = p.to_ron_string().unwrap(); + CString::new(x.as_str().as_bytes()).unwrap().into_raw() +} +#[no_mangle] +pub unsafe extern "C" fn custom_service_decode(ptr: *const u8, len: size_t) -> *mut CustomService { + Box::into_raw(Box::new( + CustomService::decode(slice::from_raw_parts(ptr, len)).unwrap(), + )) +} +#[no_mangle] +pub unsafe extern "C" fn custom_service_encode(ptr: *const CustomService) -> *mut u8 { + let p = &*ptr; + let mut x = p.encode().unwrap(); + let r = x.as_mut_ptr(); + std::mem::forget(x); + r +} diff -r 1059e7b52e47 -r ba323d8c0f93 src/rs.lisp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/rs.lisp Sat Jun 03 22:48:46 2023 -0400 @@ -0,0 +1,78 @@ +;;; RUST DSL + +;; So basically, this was born out of personal frustration with how +;; cbindgen and Rust macros work (they don't). Rust macros in general +;; are something of a pain in my opinion, so I thought why not just +;; generate Rust code from Lisp instead? + +(in-package :demo) + +(defvar *cargo-target* #p"/Users/ellis/dev/otom8/demo/target/") +(defvar *rs-macros* nil) + +;; TODO gensyms +(defmacro rs-defmacro (name args &body body) + "Define a macro which can be used within the body of a 'with-rs' form." + `(prog1 + (defmacro ,name ,@(mapcar #`(,a1) args) ,@body) + (push ',name *rs-macros*))) + +(defun rs-mod-form (crate &optional mods pub) + "Generate a basic mod form (CRATE . [MODS] [PUB])" + `(,crate ,mods ,pub)) + +(defmacro with-rs-env (imports &body body) + "Generate an environment for use within a Rust generator macro." + `(let ((imports ,(mapcar #'rs-mod-form imports))) + (format nil "~A~&~A" imports ',body))) + +(defun rs-use (crate &optional mods pub) + "Generate a single Rust use statement." + (concatenate + 'string + (if pub "pub " "") + "use " crate "::{" + (cond + ((consp mods) + (reduce + (lambda (x y) (format nil "~A,~A" x y)) + mods)) + (t mods)) + "};")) + +(defun rs-mod (mod &optional pub) + "Generate a single Rust mod statement." + (concatenate + 'string + (if pub "pub " "") + "mod " mod ";")) + +(defun rs-imports (&rest imports) + "Generate a string of Rust 'use' statements." + (cond + ((consp imports) + (mapcar (lambda (x) (apply #'rs-use (apply #'rs-mod-form x))) imports)) + (t imports))) + +(defmacro rs-extern-c-fn (name args &optional pub unsafe no-mangle &body body) + "Generate a Rust extern 'C' fn." + `(concatenate + 'string + ,(when no-mangle (format nil "#[no_mangle]~&")) + ,(when pub "pub ") + ,(when unsafe "unsafe ") + "extern \"C\" fn " ,name "(" + ,(cond + ((consp args) (reduce (lambda (x y) (format nil "~A,~A" x y)) args)) + (t args)) + ")" "{" ,@body "}")) + +(defun rs-obj-impl (obj) + "Implement Objective for give OBJ." + (format nil "impl Objective for ~A {};" obj)) + +;; (defun rs-macroexpand-1 (form &optional env)) + +;; (defun rs-macroexpand (env &rest body) + +;;; diff -r 1059e7b52e47 -r ba323d8c0f93 src/tests.lisp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/tests.lisp Sat Jun 03 22:48:46 2023 -0400 @@ -0,0 +1,1 @@ +(in-package :demo) diff -r 1059e7b52e47 -r ba323d8c0f93 src/tests/demo_test.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/tests/demo_test.c Sat Jun 03 22:48:46 2023 -0400 @@ -0,0 +1,8 @@ +#include +#include "demo.h" + +int main() { + Service *srv = service_from_string("weather"); + printf!("%s\n",service_to_json_str(srv)); + free_service(srv); +} diff -r 1059e7b52e47 -r ba323d8c0f93 src/tests/demo_test.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/tests/demo_test.py Sat Jun 03 22:48:46 2023 -0400 @@ -0,0 +1,1 @@ +from _demo import lib diff -r 1059e7b52e47 -r ba323d8c0f93 src/tests/prime-test.lisp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/tests/prime-test.lisp Sat Jun 03 22:48:46 2023 -0400 @@ -0,0 +1,67 @@ +;; (defun is-prime(n) +;; (do ((num 2 (+ num 1))) +;; ((> num (/ n 2)) t) +;; (if (= 0 (mod n num)) +;; (return-from is-prime nil)))) + +;; (defun kth-prime(k) +;; (do ((candidate 2 (+ candidate 1))) +;; ((< k 1) (- candidate 1)) +;; (when (is-prime candidate) +;; (decf k)))) + +;; (time (kth-prime 10000)) + +;; (declaim (inline is-prime)) +;; (defun is-prime (n) +;; (loop for num of-type fixnum from 3 to (isqrt n) by 2 +;; when (zerop (mod n num)) +;; return nil +;; finally (return t))) + +;; (defun kth-prime (k) +;; (declare (optimize (speed 3) (safety 0)) +;; (fixnum k)) +;; (if (zerop k) +;; 2 +;; (loop for candidate of-type fixnum from 3 by 2 +;; when (<= k 0) return (- candidate 2) +;; when (is-prime candidate) do (decf k)))) + +;; (declaim +;; (optimize (speed 3) (safety 0)) +;; (inline is-prime)) + +;; (defun is-prime(n) +;; (declare (fixnum n)) +;; (do ((num 2 (+ num 1))) +;; ((> num (floor n 2)) t) +;; (declare (fixnum num)) +;; (if (= 0 (mod n num)) +;; (return-from is-prime nil)))) + +;; (defun kth-prime(k) +;; (declare (fixnum k)) +;; (do ((candidate 2 (+ candidate 1))) +;; ((< k 1) (- candidate 1)) +;; (declare (fixnum candidate)) +;; (when (is-prime candidate) +;; (decf k)))) + +;; (time (kth-prime 10000)) + +(declaim (inline is-prime)) +(defun is-prime (n) + (loop for num of-type fixnum from 2 to (ash n -1) + when (zerop (mod n num)) + return nil + finally (return t))) + +(defun kth-prime (k) + (declare (optimize (speed 3) (safety 0)) + (fixnum k)) + (loop for candidate of-type fixnum from 2 + when (<= k 0) return (1- candidate) + when (is-prime candidate) do (decf k))) + +(time (kth-prime 10000)) diff -r 1059e7b52e47 -r ba323d8c0f93 src/tk.lisp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/tk.lisp Sat Jun 03 22:48:46 2023 -0400 @@ -0,0 +1,28 @@ +(in-package :demo) + +(defun mkstr (&rest args) + (with-output-to-string (s) + (dolist (a args) (princ a s)))) + +(defun symb (&rest args) + (values (intern (apply #'mkstr args)))) + +(defun random-id () + (format NIL "~8,'0x-~8,'0x" (random #xFFFFFFFF) (get-universal-time))) + +(defun scan-dir (dir filename callback) + (dolist (path (directory (merge-pathnames (merge-pathnames filename "**/") dir))) + (funcall callback path))) + +(defun sbq-reader (stream sub-char numarg) + "The anaphoric sharp-backquote reader: #`((,a1))" + (declare (ignore sub-char)) + (unless numarg (setq numarg 1)) + `(lambda ,(loop for i from 1 to numarg + collect (symb 'a i)) + ,(funcall + (get-macro-character #\`) stream nil))) + +(eval-when (:load-toplevel) + (set-dispatch-macro-character + #\# #\` #'demo:sbq-reader)) diff -r 1059e7b52e47 -r ba323d8c0f93 src/ui.lisp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/ui.lisp Sat Jun 03 22:48:46 2023 -0400 @@ -0,0 +1,43 @@ +(in-package :demo) + +(defparameter ui-server-port 8080) +(defparameter ui-server-host "0.0.0.0") + +(defclass ui-element (clog-element) () + (:documentation "UI Element Object.")) + +(defgeneric create-ui-element (obj &key hidden class id mode) + (:documentation "Create a new ui-element as a child of OBJ.")) +(defmethod create-ui-element ((obj clog:clog-obj) + &key (class nil) + (hidden nil) + (id nil) + (mode 'auto)) + (let ((new (clog:create-div obj + :class class + :hidden hidden + :id id + :mode mode))) + (clog:set-geometry new :width 200 :height 100) + (change-class new 'ui-element))) + +(defun on-new-window (body) + "Handle new window event." + (clog:debug-mode body) + (let ((elt (clog:create-child body "

foobar

"))) + (clog:set-on-click + elt + (lambda (o) + (setf (clog:color elt) "green"))))) + +(defun start-ui () + "Start the UI." + (clog:initialize #'on-new-window + :extended-routing t + :host ui-server-host + :port ui-server-port) + (clog:open-browser)) + +(defun stop-ui () + "Stop the UI." + (clog:shutdown)) diff -r 1059e7b52e47 -r ba323d8c0f93 tests.lisp --- a/tests.lisp Sat Jun 03 19:57:46 2023 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -(in-package :demo) diff -r 1059e7b52e47 -r ba323d8c0f93 tests/demo_test.c --- a/tests/demo_test.c Sat Jun 03 19:57:46 2023 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,8 +0,0 @@ -#include -#include "demo.h" - -int main() { - Service *srv = service_from_string("weather"); - printf!("%s\n",service_to_json_str(srv)); - free_service(srv); -} diff -r 1059e7b52e47 -r ba323d8c0f93 tests/demo_test.py --- a/tests/demo_test.py Sat Jun 03 19:57:46 2023 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -from _demo import lib diff -r 1059e7b52e47 -r ba323d8c0f93 tests/prime-test.lisp --- a/tests/prime-test.lisp Sat Jun 03 19:57:46 2023 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,67 +0,0 @@ -;; (defun is-prime(n) -;; (do ((num 2 (+ num 1))) -;; ((> num (/ n 2)) t) -;; (if (= 0 (mod n num)) -;; (return-from is-prime nil)))) - -;; (defun kth-prime(k) -;; (do ((candidate 2 (+ candidate 1))) -;; ((< k 1) (- candidate 1)) -;; (when (is-prime candidate) -;; (decf k)))) - -;; (time (kth-prime 10000)) - -;; (declaim (inline is-prime)) -;; (defun is-prime (n) -;; (loop for num of-type fixnum from 3 to (isqrt n) by 2 -;; when (zerop (mod n num)) -;; return nil -;; finally (return t))) - -;; (defun kth-prime (k) -;; (declare (optimize (speed 3) (safety 0)) -;; (fixnum k)) -;; (if (zerop k) -;; 2 -;; (loop for candidate of-type fixnum from 3 by 2 -;; when (<= k 0) return (- candidate 2) -;; when (is-prime candidate) do (decf k)))) - -;; (declaim -;; (optimize (speed 3) (safety 0)) -;; (inline is-prime)) - -;; (defun is-prime(n) -;; (declare (fixnum n)) -;; (do ((num 2 (+ num 1))) -;; ((> num (floor n 2)) t) -;; (declare (fixnum num)) -;; (if (= 0 (mod n num)) -;; (return-from is-prime nil)))) - -;; (defun kth-prime(k) -;; (declare (fixnum k)) -;; (do ((candidate 2 (+ candidate 1))) -;; ((< k 1) (- candidate 1)) -;; (declare (fixnum candidate)) -;; (when (is-prime candidate) -;; (decf k)))) - -;; (time (kth-prime 10000)) - -(declaim (inline is-prime)) -(defun is-prime (n) - (loop for num of-type fixnum from 2 to (ash n -1) - when (zerop (mod n num)) - return nil - finally (return t))) - -(defun kth-prime (k) - (declare (optimize (speed 3) (safety 0)) - (fixnum k)) - (loop for candidate of-type fixnum from 2 - when (<= k 0) return (1- candidate) - when (is-prime candidate) do (decf k))) - -(time (kth-prime 10000)) diff -r 1059e7b52e47 -r ba323d8c0f93 tk.lisp --- a/tk.lisp Sat Jun 03 19:57:46 2023 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,28 +0,0 @@ -(in-package :demo) - -(defun mkstr (&rest args) - (with-output-to-string (s) - (dolist (a args) (princ a s)))) - -(defun symb (&rest args) - (values (intern (apply #'mkstr args)))) - -(defun random-id () - (format NIL "~8,'0x-~8,'0x" (random #xFFFFFFFF) (get-universal-time))) - -(defun scan-dir (dir filename callback) - (dolist (path (directory (merge-pathnames (merge-pathnames filename "**/") dir))) - (funcall callback path))) - -(defun sbq-reader (stream sub-char numarg) - "The anaphoric sharp-backquote reader: #`((,a1))" - (declare (ignore sub-char)) - (unless numarg (setq numarg 1)) - `(lambda ,(loop for i from 1 to numarg - collect (symb 'a i)) - ,(funcall - (get-macro-character #\`) stream nil))) - -(eval-when (:load-toplevel) - (set-dispatch-macro-character - #\# #\` #'demo:sbq-reader)) diff -r 1059e7b52e47 -r ba323d8c0f93 tools/ci.nu diff -r 1059e7b52e47 -r ba323d8c0f93 tools/deps.nu --- a/tools/deps.nu Sat Jun 03 19:57:46 2023 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,13 +0,0 @@ - -pacman -Sy sbcl -curl -o /tmp/ql.lisp http://beta.quicklisp.org/quicklisp.lisp -sbcl --no-sysinit --no-userinit --load /tmp/ql.lisp \ - --eval '(quicklisp-quickstart:install :path "~/.quicklisp")' \ - --eval '(ql:add-to-init-file)' \ - --quit -sbcl --eval '(ql:quickload :quicklisp-slime-helper)' --quit -# (load (expand-file-name "~/.quicklisp/slime-helper.el")) -# (setq inferior-lisp-program "sbcl") -curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -cargo install cbindgen --force -cargo install slint-lsp --git https://github.com/slint-ui/slint --force \ No newline at end of file diff -r 1059e7b52e47 -r ba323d8c0f93 tools/scripts/check.ros --- a/tools/scripts/check.ros Sat Jun 03 19:57:46 2023 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,17 +0,0 @@ -#!/bin/sh -#|-*- mode:lisp -*-|# -#| -exec ros -Q -- $0 "$@" -|# -(progn ;;init forms - (ros:ensure-asdf) - #+quicklisp(ql:quickload '() :silent t) - ) - -(defpackage :ros.script.scriptscheck.3892329806 - (:use :cl)) -(in-package :ros.script.scriptscheck.3892329806) - -(defun main (&rest argv) - (declare (ignorable argv))) -;;; vim: set ft=lisp lisp: diff -r 1059e7b52e47 -r ba323d8c0f93 tools/scripts/db.ros --- a/tools/scripts/db.ros Sat Jun 03 19:57:46 2023 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,12 +0,0 @@ -#!/bin/sh -#|-*- mode:lisp -*-|# -#| -exec ros -Q -- $0 "$@" -|# -(progn ;;init forms - (ros:ensure-asdf) - #+quicklisp(ql:quickload '() :silent t)) - -(defun main (&rest argv) - (declare (ignorable argv)) - (format t "hello world")) diff -r 1059e7b52e47 -r ba323d8c0f93 tools/scripts/demo.ros --- a/tools/scripts/demo.ros Sat Jun 03 19:57:46 2023 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,12 +0,0 @@ -#!/bin/sh -#|-*- mode:lisp -*-|# -#| -exec ros -Q -- $0 "$@" -|# -(progn ;;init forms - (ros:ensure-asdf) - #+quicklisp(ql:quickload '(demo) :silent t)) - -(defun main (&rest argv) - (declare (ignorable argv)) - (demo:main)) diff -r 1059e7b52e47 -r ba323d8c0f93 tools/scripts/pack.ros --- a/tools/scripts/pack.ros Sat Jun 03 19:57:46 2023 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,17 +0,0 @@ -#!/bin/sh -#|-*- mode:lisp -*-|# -#| -exec ros -Q -- $0 "$@" -|# -(progn ;;init forms - (ros:ensure-asdf) - #+quicklisp(ql:quickload '() :silent t) - ) - -(defpackage :ros.script.pack.3891893796 - (:use :cl)) -(in-package :ros.script.pack.3891893796) - -(defun main (&rest argv) - (declare (ignorable argv))) -;;; vim: set ft=lisp lisp: diff -r 1059e7b52e47 -r ba323d8c0f93 tools/scripts/test.ros --- a/tools/scripts/test.ros Sat Jun 03 19:57:46 2023 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,14 +0,0 @@ -#!/bin/sh -#|-*- mode:lisp -*-|# -#| -exec ros -Q -- $0 "$@" -|# -(progn ;;init forms - (ros:ensure-asdf) - #+quicklisp(ql:quickload '() :silent t)) - -(defun main (&rest argv) - (declare (ignorable argv)) - (write-line "> cargo test") - (uiop:run-program "cargo test") - (write-line " tested rust crates")) diff -r 1059e7b52e47 -r ba323d8c0f93 tools/vc.nu diff -r 1059e7b52e47 -r ba323d8c0f93 ui.lisp --- a/ui.lisp Sat Jun 03 19:57:46 2023 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,43 +0,0 @@ -(in-package :demo) - -(defparameter ui-server-port 8080) -(defparameter ui-server-host "0.0.0.0") - -(defclass ui-element (clog-element) () - (:documentation "UI Element Object.")) - -(defgeneric create-ui-element (obj &key hidden class id mode) - (:documentation "Create a new ui-element as a child of OBJ.")) -(defmethod create-ui-element ((obj clog:clog-obj) - &key (class nil) - (hidden nil) - (id nil) - (mode 'auto)) - (let ((new (clog:create-div obj - :class class - :hidden hidden - :id id - :mode mode))) - (clog:set-geometry new :width 200 :height 100) - (change-class new 'ui-element))) - -(defun on-new-window (body) - "Handle new window event." - (clog:debug-mode body) - (let ((elt (clog:create-child body "

foobar

"))) - (clog:set-on-click - elt - (lambda (o) - (setf (clog:color elt) "green"))))) - -(defun start-ui () - "Start the UI." - (clog:initialize #'on-new-window - :extended-routing t - :host ui-server-host - :port ui-server-port) - (clog:open-browser)) - -(defun stop-ui () - "Stop the UI." - (clog:shutdown)) diff -r 1059e7b52e47 -r ba323d8c0f93 ui/Cargo.toml --- a/ui/Cargo.toml Sat Jun 03 19:57:46 2023 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,22 +0,0 @@ -[package] -name = "ui" -version = "0.1.0" -edition = "2021" -build = "build.rs" -[lib] -path = "lib.rs" -crate-type = ["rlib","cdylib"] -[[bin]] -name = "demo-ui" -path = "main.rs" -[build-dependencies] -slint-build = "1.0.2" -[dependencies] -obj = {version = "0.1.0",path = "../obj"} -env_logger = "0.10.0" -log = "0.4.17" -slint = "1.0.2" -[target.'cfg(target_arch = "wasm32")'.dependencies] -wasm-bindgen = { version = "0.2" } -web-sys = { version = "0.3", features=["console"] } -console_error_panic_hook = "0.1.5" diff -r 1059e7b52e47 -r ba323d8c0f93 ui/build.rs --- a/ui/build.rs Sat Jun 03 19:57:46 2023 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,3 +0,0 @@ -fn main() { - slint_build::compile("ui.slint").unwrap(); -} diff -r 1059e7b52e47 -r ba323d8c0f93 ui/config.slint --- a/ui/config.slint Sat Jun 03 19:57:46 2023 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,3 +0,0 @@ -export global UiConfig { - in property widgets-disabled: false; -} \ No newline at end of file diff -r 1059e7b52e47 -r ba323d8c0f93 ui/img/ayo.jpeg Binary file ui/img/ayo.jpeg has changed diff -r 1059e7b52e47 -r ba323d8c0f93 ui/img/treez.png Binary file ui/img/treez.png has changed diff -r 1059e7b52e47 -r ba323d8c0f93 ui/index.html --- a/ui/index.html Sat Jun 03 19:57:46 2023 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,9 +0,0 @@ - - - >/canvas> - - - diff -r 1059e7b52e47 -r ba323d8c0f93 ui/lib.rs --- a/ui/lib.rs Sat Jun 03 19:57:46 2023 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,69 +0,0 @@ -#![deny(unsafe_code)] - -#[cfg(target_arch = "wasm32")] -use wasm_bindgen::prelude::*; - -slint::include_modules!(); - -use std::rc::Rc; - -use slint::{Model, StandardListViewItem, VecModel}; - -#[cfg_attr(target_arch = "wasm32", wasm_bindgen(start))] -pub fn run() { - // This provides better error messages in debug mode. - // It's disabled in release mode so it doesn't bloat up the file size. - #[cfg(all(debug_assertions, target_arch = "wasm32"))] - console_error_panic_hook::set_once(); - - let app = App::new().unwrap(); - - let row_data: Rc>> = Rc::new(VecModel::default()); - - for r in 1..101 { - let items = Rc::new(VecModel::default()); - - for c in 1..5 { - items.push(slint::format!("Item {r}.{c}").into()); - } - - row_data.push(items.into()); - } - - app.global::().set_row_data(row_data.clone().into()); - - app.global::().on_sort_ascending({ - let app_weak = app.as_weak(); - let row_data = row_data.clone(); - move |index| { - let row_data = row_data.clone(); - - let sort_model = Rc::new(row_data.sort_by(move |r_a, r_b| { - let c_a = r_a.row_data(index as usize).unwrap(); - let c_b = r_b.row_data(index as usize).unwrap(); - - c_a.text.cmp(&c_b.text) - })); - - app_weak.unwrap().global::().set_row_data(sort_model.into()); - } - }); - - app.global::().on_sort_descending({ - let app_weak = app.as_weak(); - move |index| { - let row_data = row_data.clone(); - - let sort_model = Rc::new(row_data.sort_by(move |r_a, r_b| { - let c_a = r_a.row_data(index as usize).unwrap(); - let c_b = r_b.row_data(index as usize).unwrap(); - - c_b.text.cmp(&c_a.text) - })); - - app_weak.unwrap().global::().set_row_data(sort_model.into()); - } - }); - - app.run().unwrap(); -} diff -r 1059e7b52e47 -r ba323d8c0f93 ui/main.rs --- a/ui/main.rs Sat Jun 03 19:57:46 2023 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,4 +0,0 @@ -use ui::run; -fn main() { - run(); -} diff -r 1059e7b52e47 -r ba323d8c0f93 ui/pages.slint --- a/ui/pages.slint Sat Jun 03 19:57:46 2023 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,7 +0,0 @@ -import { AboutPage } from "pages/about.slint"; -import { ControlsPage } from "pages/controls.slint"; -import { ListViewPage } from "pages/list_view.slint"; -import { TableViewPage, TableViewPageAdapter } from "pages/table_view.slint"; -import { TextEditPage } from "pages/text_edit.slint"; - -export { AboutPage, ControlsPage, ListViewPage, TextEditPage, TableViewPage, TableViewPageAdapter } \ No newline at end of file diff -r 1059e7b52e47 -r ba323d8c0f93 ui/pages/about.slint --- a/ui/pages/about.slint Sat Jun 03 19:57:46 2023 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,10 +0,0 @@ -import { AboutSlint } from "std-widgets.slint"; -import { UiConfig } from "../config.slint"; -import { Page } from "page.slint"; - -export component AboutPage inherits Page { - title: "About"; - description: "Are you curious now? Check out the docs and gettings start from the Github repository and the website https://slint-ui.com and try it yourself."; - - AboutSlint {} -} \ No newline at end of file diff -r 1059e7b52e47 -r ba323d8c0f93 ui/pages/controls.slint --- a/ui/pages/controls.slint Sat Jun 03 19:57:46 2023 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,145 +0,0 @@ -import { Button, GroupBox, SpinBox, ComboBox, CheckBox, LineEdit, TabWidget, VerticalBox, HorizontalBox, - Slider, SpinBox } from "std-widgets.slint"; -import { UiConfig } from "../config.slint"; -import { Page } from "page.slint"; - -export component ControlsPage inherits Page { - title: "Controls"; - description: "This page gives an overview of the default widget set provided by Slint. The widgets are available in different styles native, fluent-(dark/light) and material-(dark/light). The widgets can be imported from \"std-widgets.slint\"."; - - GroupBox { - vertical-stretch: 0; - title: "Buttons"; - - HorizontalLayout { - spacing: 8px; - alignment: start; - - Button { - text: "Regular Button"; - enabled: !UiConfig.widgets-disabled; - } - - Button { - text: "Button with Icon"; - icon: @image-url("../img/treez.png"); - enabled: !UiConfig.widgets-disabled; - } - - Button { - checkable: true; - text: self.checked ? "ON" : "OFF"; - enabled: !UiConfig.widgets-disabled; - } - } - } - - GroupBox { - title: "CheckBox - SpinBox - ComboBox"; - vertical-stretch: 0; - - HorizontalBox { - alignment: start; - checkbox := CheckBox { - text: checkbox.checked ? "(checked)" : "(unchecked)"; - checked: true; - enabled: !UiConfig.widgets-disabled; - } - - - SpinBox { - vertical-stretch: 0; - value: 42; - enabled: !UiConfig.widgets-disabled; - } - - ComboBox { - model: ["Select Something", "From this", "Combobox"]; - enabled: !UiConfig.widgets-disabled; - } - } - - - } - - GroupBox { - title: "LineEdit"; - vertical-stretch: 0; - - LineEdit { - placeholder-text: "Enter some text"; - enabled: !UiConfig.widgets-disabled; - } - } - - GroupBox { - title: "Slider"; - vertical-stretch: 0; - - Slider { - min-width: 160px; - minimum: -100; - maximum: 100; - value: 42; - enabled: !UiConfig.widgets-disabled; - } - } - - GroupBox { - title: "TabWidget"; - - TabWidget { - Tab { - title: "Tab 1"; - - VerticalBox { - alignment: start; - - GroupBox { - title: "Content of tab 1"; - - HorizontalBox { - alignment: start; - - Button { - text: "Click me"; - enabled: !UiConfig.widgets-disabled; - } - } - } - } - } - - Tab { - title: "Tab 2"; - - VerticalBox { - alignment: start; - - GroupBox { - title: "Content of tab 2"; - - VerticalBox { - alignment: start; - - CheckBox { - text: "Check me"; - enabled: !UiConfig.widgets-disabled; - } - } - } - } - } - - Tab { - title: "Tab 3"; - - VerticalBox { - Text { - text: "Content of tab 3"; - } - } - } - } - } -} \ No newline at end of file diff -r 1059e7b52e47 -r ba323d8c0f93 ui/pages/list_view.slint --- a/ui/pages/list_view.slint Sat Jun 03 19:57:46 2023 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,45 +0,0 @@ -import { HorizontalBox, VerticalBox, ListView, StandardListView, GroupBox } from "std-widgets.slint"; -import { UiConfig } from "../config.slint"; -import { Page } from "page.slint"; - -export component ListViewPage inherits Page { - title: "ListView"; - description: "ListViews can be used to display a list of elements. The StandardListBox is like the default ListView just with a default text based definition of the visual items. Both can be imported from \"std-widgets.slint\""; - - HorizontalBox { - vertical-stretch: 1; - GroupBox { - title: "ListView"; - - ListView { - vertical-stretch: 0; - for i in [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] : HorizontalBox { - Image { - width: 24px; - source: @image-url("../img/ayo.jpeg"); - } - Text { - text: "Item " + i; - } - } - } - } - - GroupBox { - title: "StandardListView"; - vertical-stretch: 0; - - StandardListView { - model: [ - {text: "Lorem"}, {text: "ipsum"},{text: "dolor"},{text: "sit"},{text: "amet"},{text: "consetetur"}, - {text: "Lorem"}, {text: "ipsum"},{text: "dolor"},{text: "sit"},{text: "amet"},{text: "consetetur"}, - {text: "Lorem"}, {text: "ipsum"},{text: "dolor"},{text: "sit"},{text: "amet"},{text: "consetetur"}, - {text: "Lorem"}, {text: "ipsum"},{text: "dolor"},{text: "sit"},{text: "amet"},{text: "consetetur"}, - {text: "Lorem"}, {text: "ipsum"},{text: "dolor"},{text: "sit"},{text: "amet"},{text: "consetetur"}, - {text: "Lorem"}, {text: "ipsum"},{text: "dolor"},{text: "sit"},{text: "amet"},{text: "consetetur"}, - {text: "Lorem"}, {text: "ipsum"},{text: "dolor"},{text: "sit"},{text: "amet"},{text: "consetetur"}, - ]; - } - } - } -} \ No newline at end of file diff -r 1059e7b52e47 -r ba323d8c0f93 ui/pages/page.slint --- a/ui/pages/page.slint Sat Jun 03 19:57:46 2023 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,27 +0,0 @@ -import { CheckBox, GridBox, ListView, ScrollView, VerticalBox } from "std-widgets.slint"; - -import { UiConfig } from "../config.slint"; - -export component Page inherits VerticalBox { - in property title: "title"; - in property description: "description"; - - HorizontalLayout { - height: 24px; - Text { - font-size: 20px; - text <=> root.title; - } - - // Spacer - Rectangle {} - - CheckBox { - horizontal-stretch: 0; - text: "Disable widgets"; - checked <=> UiConfig.widgets-disabled; - } - } - - @children -} \ No newline at end of file diff -r 1059e7b52e47 -r ba323d8c0f93 ui/pages/table_view.slint --- a/ui/pages/table_view.slint Sat Jun 03 19:57:46 2023 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,48 +0,0 @@ -import { HorizontalBox, VerticalBox, StandardTableView, GroupBox} from "std-widgets.slint"; -import { UiConfig } from "../config.slint"; -import { Page } from "page.slint"; - -export global TableViewPageAdapter { - callback sort_ascending(int); - callback sort_descending(int); - in property <[[StandardListViewItem]]> row_data: [ - [ { text: "Item 1.1" }, { text: "Item 1.2" }, { text: "Item 1.3" }, { text: "Item 1.4" }, ], - [ { text: "Item 2.1" }, { text: "Item 2.2" }, { text: "Item 2.3" }, { text: "Item 2.4" }, ], - [ { text: "Item 3.1" }, { text: "Item 3.2" }, { text: "Item 3.3" }, { text: "Item 3.4" }, ], - [ { text: "Item 4.1" }, { text: "Item 4.2" }, { text: "Item 4.3" }, { text: "Item 4.4" }, ], - [ { text: "Item 5.1" }, { text: "Item 5.2" }, { text: "Item 5.3" }, { text: "Item 5.4" }, ], - [ { text: "Item 6.1" }, { text: "Item 6.2" }, { text: "Item 6.3" }, { text: "Item 6.4" }, ], - ]; -} - -export component TableViewPage inherits Page { - title: "TableView"; - description: "StandardTableView can be used to display a list of text elements in columns and rows. It can be imported from \"std-widgets.slint\""; - - HorizontalBox { - vertical-stretch: 1; - - GroupBox { - title: "StandardTableView"; - vertical-stretch: 0; - - StandardTableView { - sort-ascending(index) => { - TableViewPageAdapter.sort_ascending(index); - } - - sort-descending(index) => { - TableViewPageAdapter.sort-descending(index); - } - - columns: [ - { title: "Header 1" }, - { title: "Header 2" }, - { title: "Header 3" }, - { title: "Header 4" }, - ]; - rows: TableViewPageAdapter.row_data; - } - } - } -} \ No newline at end of file diff -r 1059e7b52e47 -r ba323d8c0f93 ui/pages/text_edit.slint --- a/ui/pages/text_edit.slint Sat Jun 03 19:57:46 2023 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,32 +0,0 @@ -import { HorizontalBox, GroupBox, TextEdit } from "std-widgets.slint"; -import { UiConfig } from "../config.slint"; -import { Page } from "page.slint"; - -export component TextEditPage inherits Page { - title: "TextEdit"; - description: "Similar to LineEdit, but can be used to enter several lines of text. The widget can be imported from \"std-widgets.slint\"."; - - HorizontalBox { - GroupBox { - vertical-stretch: 0; - title: "Word-Wrap"; - te1 := TextEdit { - min-width: 200px; - text: "This is our TextEdit widget, which allows for editing text that spans over multiple paragraphs.\nFor example this line starts in a new paragraph.\n\nWhen the amount of lines - due to wrapping and number of paragraphs - exceeds the available vertical height, a vertical scrollbar is shown that allows scrolling.\nYou may want to enter a bit of text here then in order to make them visible."; - wrap: word-wrap; - enabled: !UiConfig.widgets-disabled; - } - } - - GroupBox { - title: "No-Wrap"; - vertical-stretch: 0; - te2 := TextEdit { - min-width: 200px; - text <=> te1.text; - wrap: no-wrap; - enabled: !UiConfig.widgets-disabled; - } - } - } -} \ No newline at end of file diff -r 1059e7b52e47 -r ba323d8c0f93 ui/sidebar.slint --- a/ui/sidebar.slint Sat Jun 03 19:57:46 2023 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,127 +0,0 @@ -import { StyleMetrics } from "std-widgets.slint"; - -component SideBarItem inherits Rectangle { - callback clicked <=> touch.clicked; - in-out property text <=> label.text; - in property selected; - in property has-focus; - - min-height: l.preferred-height; - - state := Rectangle { - opacity: 0; - background: StyleMetrics.window-background; - - animate opacity { duration: 150ms; } - } - - l := HorizontalLayout { - y: (parent.height - self.height) / 2; - padding: StyleMetrics.layout-padding; - spacing: 0px; - - label := Text { - color: StyleMetrics.default-text-color; - vertical-alignment: center; - } - } - - touch := TouchArea { - width: 100%; - height: 100%; - } - - states [ - pressed when touch.pressed : { - state.opacity: 0.8; - } - hover when touch.has-hover : { - state.opacity: 0.6; - } - selected when root.selected : { - state.opacity: 1; - } - focused when root.has-focus : { - state.opacity: 0.8; - } - ] -} - -export component SideBar inherits Rectangle { - in property<[string]> model: []; - out property current-item: 0; - in property title <=> label.text; - out property current-focused: fs.has-focus ? fs.focused-tab : -1; // The currently focused tab - width: 180px; - - forward-focus: fs; - - accessible-role: tab; - accessible-delegate-focus: root.current-focused >= 0 ? root.current-focused : root.current-item; - - Rectangle { - background: StyleMetrics.window-background.darker(0.2); - - fs := FocusScope { - x:0; - width: 0px; // Do not react on clicks - property focused-tab: 0; - - key-pressed(event) => { - if (event.text == "\n") { - root.current-item = root.current-focused; - return accept; - } - if (event.text == Key.UpArrow) { - self.focused-tab = Math.max(self.focused-tab - 1, 0); - return accept; - } - if (event.text == Key.DownArrow) { - self.focused-tab = Math.min(self.focused-tab + 1, root.model.length - 1); - return accept; - } - return reject; - } - - key-released(event) => { - if (event.text == " ") { - root.current-item = root.current-focused; - return accept; - } - return reject; - } - } - } - - VerticalLayout { - padding-top: StyleMetrics.layout-padding; - padding-bottom: StyleMetrics.layout-padding; - spacing: StyleMetrics.layout-spacing; - alignment: start; - - label := Text { - font-size: 16px; - horizontal-alignment: center; - } - - navigation := VerticalLayout { - alignment: start; - vertical-stretch: 0; - for item[index] in root.model : SideBarItem { - has-focus: index == root.current-focused; - text: item; - selected: index == root.current-item; - clicked => { root.current-item = index; } - } - } - - VerticalLayout { - bottom := VerticalLayout { - padding-left: StyleMetrics.layout-padding; - padding-right: StyleMetrics.layout-padding; - - @children - } - } - } -} \ No newline at end of file diff -r 1059e7b52e47 -r ba323d8c0f93 ui/ui.slint --- a/ui/ui.slint Sat Jun 03 19:57:46 2023 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,20 +0,0 @@ -import {CheckBox, StandardListView, StyleMetrics} from "std-widgets.slint"; -import {AboutPage, ControlsPage, ListViewPage, TableViewPage, TableViewPageAdapter, TextEditPage} from "pages.slint"; -import {UiConfig} from "config.slint"; -import {SideBar} from "sidebar.slint"; -export {TableViewPageAdapter} -export component App inherits Window { - title: "Demo"; - icon: @image-url("img/treez.png"); - HorizontalLayout { - side-bar := SideBar { - title: "Demo"; - model: ["Controls", "ListView", "TableView", "TextEdit", "About"]; - } - if(side-bar.current-item == 0) : ControlsPage {} - if(side-bar.current-item == 1) : ListViewPage {} - if(side-bar.current-item == 2) : TableViewPage {} - if(side-bar.current-item == 3) : TextEditPage {} - if(side-bar.current-item == 4) : AboutPage {} - } -} \ No newline at end of file