Compare commits
8 Commits
Author | SHA1 | Date |
---|---|---|
Emery Hemingway | 3a0bd1cd02 | |
Emery Hemingway | cda940cf75 | |
Emery Hemingway | bf0b5d6b86 | |
Emery Hemingway | b3a417a072 | |
Emery Hemingway | 48408d2763 | |
Emery Hemingway | 494418540a | |
Emery Hemingway | e954fdefec | |
Emery Hemingway | de7683b467 |
106
README.md
106
README.md
|
@ -47,6 +47,46 @@ Query the size of a directory in bytes by observing `<file-system-usage "/SOME/P
|
|||
]
|
||||
```
|
||||
|
||||
### HTTP driver
|
||||
|
||||
Experimental HTTP server that services requests using [some version](https://git.syndicate-lang.org/syndicate-lang/syndicate-protocols/src/commit/9864ce0ec86fb2f916c2aab318a1e6994ab8834c/schemas/http.prs) of the http Syndicate protocol schema.
|
||||
|
||||
```
|
||||
# Configuration example
|
||||
|
||||
let ?not-found = dataspace
|
||||
$not-found ? <request _ ?res> [
|
||||
$res ! <status 503 "Service unavailable">
|
||||
$res ! <done "No binding here.">
|
||||
]
|
||||
|
||||
let ?greeting = dataspace
|
||||
$greeting ? <request _ ?res> [
|
||||
$res ! <status 200 "ok">
|
||||
$res ! <chunk "Hello world">
|
||||
$res ! <done "!">
|
||||
]
|
||||
|
||||
let ?http = dataspace
|
||||
$http [
|
||||
<http-bind #f 80 get [ ] $not-found>
|
||||
<http-bind #f 80 get [|...|] $not-found>
|
||||
<http-bind #f 80 get ["hello"] $greeting>
|
||||
]
|
||||
|
||||
? <service-object <daemon http-driver> ?cap> [
|
||||
$cap <http-driver { dataspace: $http }>
|
||||
]
|
||||
|
||||
<daemon http-driver {
|
||||
argv: [ "/bin/syndesizer" ]
|
||||
clearEnv: #t
|
||||
protocol: application/syndicate
|
||||
}>
|
||||
|
||||
<require-service <daemon http-driver>>
|
||||
```
|
||||
|
||||
### JSON Socket Translator
|
||||
|
||||
Communicate with sockets that send and receive lines of JSON using `<send …>` and `<recv …>` messages.
|
||||
|
@ -168,8 +208,13 @@ let ?tuplespace = dataspace
|
|||
|
||||
$sqlspace <query "SELECT id, name FROM stuff" $tuplespace>
|
||||
|
||||
$tuplespace ? [?id ?name] [
|
||||
$log ! <log "-" { row: <example-row $id $name> }>
|
||||
$tuplespace [
|
||||
? [?id ?name] [
|
||||
$log ! <log "-" { row: <example-row $id $name> }>
|
||||
]
|
||||
? <sqlite-error ?msg ?ctx> [
|
||||
$log ! <log "-" { msg: $msg ctx: $ctx }>
|
||||
]
|
||||
]
|
||||
```
|
||||
|
||||
|
@ -196,6 +241,63 @@ Examples:
|
|||
|
||||
---
|
||||
|
||||
## http_client
|
||||
|
||||
The inverse of `http-driver`.
|
||||
|
||||
### Caveats
|
||||
- HTTPS is assumed unless the request is to port 80.
|
||||
- If the request or response sets `Content-Type` to `application/json` or `…/preserves`
|
||||
the body will be a parsed Preserves value.
|
||||
- No cache support.
|
||||
- Internal errors propagate using a `400 Internal client error` response.
|
||||
|
||||
Sample Syndicate server script:
|
||||
```
|
||||
# A top-level dataspace
|
||||
let ?ds = dataspace
|
||||
|
||||
# A dataspace for handling the HTTP response.
|
||||
let ?response = dataspace
|
||||
$response [
|
||||
?? <done { "code": "EUR" "exchange_middle": ?middle } > [
|
||||
$ds <exchange EUR RSD $middle>
|
||||
]
|
||||
]
|
||||
|
||||
$ds [
|
||||
<request
|
||||
# Request Euro to Dinar exchange rate.
|
||||
<http-request 0 "kurs.resenje.org" 443
|
||||
get ["api" "v1" "currencies" "eur" "rates" "today"]
|
||||
{Content-Type: "application/json"} {} #f
|
||||
>
|
||||
$response
|
||||
>
|
||||
|
||||
# Log all assertions.
|
||||
? ?any [
|
||||
$log ! <log "-" { assertion: $any }>
|
||||
]
|
||||
]
|
||||
|
||||
? <service-object <daemon http-client> ?cap> [
|
||||
$cap <http-client {
|
||||
dataspace: $ds
|
||||
}>
|
||||
]
|
||||
|
||||
<require-service <daemon http-client>>
|
||||
|
||||
? <built http-client ?path ?sum> [
|
||||
<daemon http-client {
|
||||
argv: [ "/bin/http_client" ]
|
||||
clearEnv: #t
|
||||
protocol: application/syndicate
|
||||
}>
|
||||
]
|
||||
```
|
||||
|
||||
## mintsturdyref
|
||||
|
||||
A utility for minting [Sturdyrefs](https://synit.org/book/operation/builtin/gatekeeper.html#sturdyrefs).
|
||||
|
|
|
@ -26,6 +26,10 @@ UnixAddress = <unix @path string>.
|
|||
|
||||
SocketAddress = TcpAddress / UnixAddress .
|
||||
|
||||
HttpClientArguments = <http-client {
|
||||
dataspace: #:any
|
||||
}>.
|
||||
|
||||
HttpDriverArguments = <http-driver {
|
||||
dataspace: #:any
|
||||
}>.
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
{
|
||||
pkgs ? import <nixpkgs> { },
|
||||
}:
|
||||
|
||||
let
|
||||
inherit (pkgs)
|
||||
lib
|
||||
buildNimPackage
|
||||
fetchFromGitea
|
||||
libxml2
|
||||
libxslt
|
||||
openssl
|
||||
pkg-config
|
||||
postgresql
|
||||
sqlite
|
||||
;
|
||||
in
|
||||
|
||||
buildNimPackage {
|
||||
pname = "syndicate_utils";
|
||||
version = "unstable";
|
||||
|
||||
src = if lib.inNixShell then null else lib.cleanSource ./.;
|
||||
|
||||
buildInputs = [
|
||||
postgresql.out
|
||||
sqlite
|
||||
libxml2
|
||||
libxslt
|
||||
openssl
|
||||
];
|
||||
|
||||
lockFile = ./lock.json;
|
||||
}
|
194
lock.json
194
lock.json
|
@ -1,16 +1,5 @@
|
|||
{
|
||||
"depends": [
|
||||
{
|
||||
"method": "fetchzip",
|
||||
"packages": [
|
||||
"bigints"
|
||||
],
|
||||
"path": "/nix/store/jvrm392g8adfsgf36prgwkbyd7vh5jsw-source",
|
||||
"rev": "86ea14d31eea9275e1408ca34e6bfe9c99989a96",
|
||||
"sha256": "15pcpmnk1bnw3k8769rjzcpg00nahyrypwbxs88jnwr4aczp99j4",
|
||||
"srcDir": "src",
|
||||
"url": "https://github.com/ehmry/nim-bigints/archive/86ea14d31eea9275e1408ca34e6bfe9c99989a96.tar.gz"
|
||||
},
|
||||
{
|
||||
"method": "fetchzip",
|
||||
"packages": [
|
||||
|
@ -22,28 +11,6 @@
|
|||
"srcDir": "",
|
||||
"url": "https://github.com/ehmry/cps/archive/c90530ac57f98a842b7be969115c6ef08bdcc564.tar.gz"
|
||||
},
|
||||
{
|
||||
"method": "fetchzip",
|
||||
"packages": [
|
||||
"cps"
|
||||
],
|
||||
"path": "/nix/store/8gbhwni0akqskdb3qhn5nfgv6gkdz0vz-source",
|
||||
"rev": "c90530ac57f98a842b7be969115c6ef08bdcc564",
|
||||
"sha256": "0h8ghs2fqg68j3jdcg7grnxssmllmgg99kym2w0a3vlwca1zvr62",
|
||||
"srcDir": "",
|
||||
"url": "https://github.com/ehmry/cps/archive/c90530ac57f98a842b7be969115c6ef08bdcc564.tar.gz"
|
||||
},
|
||||
{
|
||||
"method": "fetchzip",
|
||||
"packages": [
|
||||
"getdns"
|
||||
],
|
||||
"path": "/nix/store/x9xmn7w4k6jg8nv5bnx148ibhnsfh362-source",
|
||||
"rev": "c73cbe288d9f9480586b8fa87f6d794ffb6a6ce6",
|
||||
"sha256": "1sbgx2x51szr22i72n7c8jglnfmr8m7y7ga0v85d58fwadiv7g6b",
|
||||
"srcDir": "src",
|
||||
"url": "https://git.sr.ht/~ehmry/getdns-nim/archive/c73cbe288d9f9480586b8fa87f6d794ffb6a6ce6.tar.gz"
|
||||
},
|
||||
{
|
||||
"method": "fetchzip",
|
||||
"packages": [
|
||||
|
@ -71,11 +38,11 @@
|
|||
"packages": [
|
||||
"preserves"
|
||||
],
|
||||
"path": "/nix/store/2hy124xgabz134dxj3wji7mp47fdwy3w-source",
|
||||
"rev": "9ae435a83c6d5028405538af5d24a023af625b6e",
|
||||
"sha256": "1k7ywcp1a53x2fpc6wc2b0qzb264dkifash0s1wcp66rw3lx15k2",
|
||||
"path": "/nix/store/hzb7af7lbd4kgd5y4hbgxv1lswig36yj-source",
|
||||
"rev": "fd498c6457cb9ad2f3179daa40da69eec00326dd",
|
||||
"sha256": "182xvw04vjw83mlcrkwkip29b44h0v8dapg2014k9011h90mdsj4",
|
||||
"srcDir": "src",
|
||||
"url": "https://git.syndicate-lang.org/ehmry/preserves-nim/archive/9ae435a83c6d5028405538af5d24a023af625b6e.tar.gz"
|
||||
"url": "https://git.syndicate-lang.org/ehmry/preserves-nim/archive/fd498c6457cb9ad2f3179daa40da69eec00326dd.tar.gz"
|
||||
},
|
||||
{
|
||||
"method": "fetchzip",
|
||||
|
@ -93,11 +60,11 @@
|
|||
"packages": [
|
||||
"syndicate"
|
||||
],
|
||||
"path": "/nix/store/kl628g7vg2ww8wilf8h2ag7qqnvvwdzb-source",
|
||||
"rev": "c2e1e2e0fa403529750196ce3ccb5a99a4d6c006",
|
||||
"sha256": "1r8ab79pgrrnzmp49h8rp50c9x8zd0p7bsvzxaphc221nvyfx09j",
|
||||
"path": "/nix/store/dw30cq9gxz3353zgaq4a36ajq6chvbwc-source",
|
||||
"rev": "3a4dc1f13392830b587138199643d30fdbec8541",
|
||||
"sha256": "1mbd17rjm1fsx7d0ckzyjih2nzdjqs52ck9wscqcg9nvf3ib5mvh",
|
||||
"srcDir": "src",
|
||||
"url": "https://git.syndicate-lang.org/ehmry/syndicate-nim/archive/c2e1e2e0fa403529750196ce3ccb5a99a4d6c006.tar.gz"
|
||||
"url": "https://git.syndicate-lang.org/ehmry/syndicate-nim/archive/3a4dc1f13392830b587138199643d30fdbec8541.tar.gz"
|
||||
},
|
||||
{
|
||||
"method": "fetchzip",
|
||||
|
@ -110,39 +77,6 @@
|
|||
"srcDir": "src",
|
||||
"url": "https://github.com/ehmry/nim-sys/archive/4ef3b624db86e331ba334e705c1aa235d55b05e1.tar.gz"
|
||||
},
|
||||
{
|
||||
"method": "fetchzip",
|
||||
"packages": [
|
||||
"sys"
|
||||
],
|
||||
"path": "/nix/store/syhxsjlsdqfap0hk4qp3s6kayk8cqknd-source",
|
||||
"rev": "4ef3b624db86e331ba334e705c1aa235d55b05e1",
|
||||
"sha256": "1q4qgw4an4mmmcbx48l6xk1jig1vc8p9cq9dbx39kpnb0890j32q",
|
||||
"srcDir": "src",
|
||||
"url": "https://github.com/ehmry/nim-sys/archive/4ef3b624db86e331ba334e705c1aa235d55b05e1.tar.gz"
|
||||
},
|
||||
{
|
||||
"method": "fetchzip",
|
||||
"packages": [
|
||||
"sys"
|
||||
],
|
||||
"path": "/nix/store/vf9ls2wip6d8xhsi3rjh0dqsqg597i6b-source",
|
||||
"rev": "c117ee60542f084525f254e6ade590675a6a2ed6",
|
||||
"sha256": "12qzx2lnh84xqfgypy0pka8nflq0y8n1izfwx8mb4zya5nzawmyf",
|
||||
"srcDir": "src",
|
||||
"url": "https://github.com/alaviss/nim-sys/archive/c117ee60542f084525f254e6ade590675a6a2ed6.tar.gz"
|
||||
},
|
||||
{
|
||||
"method": "fetchzip",
|
||||
"packages": [
|
||||
"taps"
|
||||
],
|
||||
"path": "/nix/store/6y14ia52kr7jyaa0izx37mlablmq9s65-source",
|
||||
"rev": "8c8572cd971d1283e6621006b310993c632da247",
|
||||
"sha256": "1dp166bv9x773jmfqppg5i3v3rilgff013vb11yzwcid9l7s3iy8",
|
||||
"srcDir": "src",
|
||||
"url": "https://git.sr.ht/~ehmry/nim_taps/archive/8c8572cd971d1283e6621006b310993c632da247.tar.gz"
|
||||
},
|
||||
{
|
||||
"method": "fetchzip",
|
||||
"packages": [
|
||||
|
@ -174,35 +108,24 @@
|
|||
{
|
||||
"method": "fetchzip",
|
||||
"packages": [
|
||||
"getdns"
|
||||
"bigints"
|
||||
],
|
||||
"path": "/nix/store/x9xmn7w4k6jg8nv5bnx148ibhnsfh362-source",
|
||||
"rev": "c73cbe288d9f9480586b8fa87f6d794ffb6a6ce6",
|
||||
"sha256": "1sbgx2x51szr22i72n7c8jglnfmr8m7y7ga0v85d58fwadiv7g6b",
|
||||
"path": "/nix/store/jvrm392g8adfsgf36prgwkbyd7vh5jsw-source",
|
||||
"rev": "86ea14d31eea9275e1408ca34e6bfe9c99989a96",
|
||||
"sha256": "15pcpmnk1bnw3k8769rjzcpg00nahyrypwbxs88jnwr4aczp99j4",
|
||||
"srcDir": "src",
|
||||
"url": "https://git.sr.ht/~ehmry/getdns-nim/archive/c73cbe288d9f9480586b8fa87f6d794ffb6a6ce6.tar.gz"
|
||||
},
|
||||
{
|
||||
"method": "fetchzip",
|
||||
"packages": [
|
||||
"getdns"
|
||||
],
|
||||
"path": "/nix/store/x9xmn7w4k6jg8nv5bnx148ibhnsfh362-source",
|
||||
"rev": "c73cbe288d9f9480586b8fa87f6d794ffb6a6ce6",
|
||||
"sha256": "1sbgx2x51szr22i72n7c8jglnfmr8m7y7ga0v85d58fwadiv7g6b",
|
||||
"srcDir": "src",
|
||||
"url": "https://git.sr.ht/~ehmry/getdns-nim/archive/c73cbe288d9f9480586b8fa87f6d794ffb6a6ce6.tar.gz"
|
||||
"url": "https://github.com/ehmry/nim-bigints/archive/86ea14d31eea9275e1408ca34e6bfe9c99989a96.tar.gz"
|
||||
},
|
||||
{
|
||||
"method": "fetchzip",
|
||||
"packages": [
|
||||
"nimcrypto"
|
||||
],
|
||||
"path": "/nix/store/fkrcpp8lzj2yi21na79xm63xk0ggnqsp-source",
|
||||
"rev": "f147d30c69bc1c9bcf0e37f7699bcf0fbaab97b5",
|
||||
"sha256": "1h3dzdbc9kacwpi10mj73yjglvn7kbizj1x8qc9099ax091cj5xn",
|
||||
"path": "/nix/store/h7lgq3by9mx8in03vzh0y964lnnlkalp-source",
|
||||
"rev": "ff6afc6a753bd645cad4568472c7733d1715e31e",
|
||||
"sha256": "0h9vpayp66pg66114bl0nsvlv1nzp7f0x5b35gbsbd7svzlcz5zj",
|
||||
"srcDir": "",
|
||||
"url": "https://github.com/cheatfate/nimcrypto/archive/f147d30c69bc1c9bcf0e37f7699bcf0fbaab97b5.tar.gz"
|
||||
"url": "https://github.com/cheatfate/nimcrypto/archive/ff6afc6a753bd645cad4568472c7733d1715e31e.tar.gz"
|
||||
},
|
||||
{
|
||||
"method": "fetchzip",
|
||||
|
@ -214,89 +137,6 @@
|
|||
"sha256": "1fi9ls3xl20bmv1ikillxywl96i9al6zmmxrbffx448gbrxs86kg",
|
||||
"srcDir": "src",
|
||||
"url": "https://github.com/zevv/npeg/archive/ec0cc6e64ea4c62d2aa382b176a4838474238f8d.tar.gz"
|
||||
},
|
||||
{
|
||||
"method": "fetchzip",
|
||||
"packages": [
|
||||
"stew"
|
||||
],
|
||||
"path": "/nix/store/mqg8qzsbcc8xqabq2yzvlhvcyqypk72c-source",
|
||||
"rev": "3c91b8694e15137a81ec7db37c6c58194ec94a6a",
|
||||
"sha256": "17lfhfxp5nxvld78xa83p258y80ks5jb4n53152cdr57xk86y07w",
|
||||
"srcDir": "",
|
||||
"url": "https://github.com/status-im/nim-stew/archive/3c91b8694e15137a81ec7db37c6c58194ec94a6a.tar.gz"
|
||||
},
|
||||
{
|
||||
"method": "fetchzip",
|
||||
"packages": [
|
||||
"sys"
|
||||
],
|
||||
"path": "/nix/store/vf9ls2wip6d8xhsi3rjh0dqsqg597i6b-source",
|
||||
"rev": "c117ee60542f084525f254e6ade590675a6a2ed6",
|
||||
"sha256": "12qzx2lnh84xqfgypy0pka8nflq0y8n1izfwx8mb4zya5nzawmyf",
|
||||
"srcDir": "src",
|
||||
"url": "https://github.com/alaviss/nim-sys/archive/c117ee60542f084525f254e6ade590675a6a2ed6.tar.gz"
|
||||
},
|
||||
{
|
||||
"date": "2024-04-02T15:38:57+01:00",
|
||||
"deepClone": false,
|
||||
"fetchLFS": false,
|
||||
"fetchSubmodules": true,
|
||||
"hash": "sha256-iZb9aAgYr4FGkqfIg49QWiCqeizIi047kFhugHiP8o0=",
|
||||
"leaveDotGit": false,
|
||||
"method": "git",
|
||||
"packages": [
|
||||
"solo5_dispatcher"
|
||||
],
|
||||
"path": "/nix/store/sf5dgj2ljvahcm6my7d61ibda51vnrii-solo5_dispatcher",
|
||||
"rev": "a7a894a96a2221284012800e6fd32923d83d20bd",
|
||||
"sha256": "13gjixw80vjqj0xlx2y85ixal82sa27q7j57j9383bqq11lgv5l9",
|
||||
"srcDir": "pkg",
|
||||
"url": "https://git.sr.ht/~ehmry/solo5_dispatcher"
|
||||
},
|
||||
{
|
||||
"method": "fetchzip",
|
||||
"packages": [
|
||||
"cps"
|
||||
],
|
||||
"path": "/nix/store/phdf6siqbhj7vx4qq507lzla81si60iz-source",
|
||||
"rev": "58772ff9ddb38a4b2ec52da142d8532ba2fe7039",
|
||||
"sha256": "1lph7v27nqwgm3a0ssi8q348gjrkjwgqc50agw38j7xif6wj80cw",
|
||||
"srcDir": "",
|
||||
"url": "https://github.com/ehmry/cps/archive/58772ff9ddb38a4b2ec52da142d8532ba2fe7039.tar.gz"
|
||||
},
|
||||
{
|
||||
"method": "fetchzip",
|
||||
"packages": [
|
||||
"cps"
|
||||
],
|
||||
"path": "/nix/store/phdf6siqbhj7vx4qq507lzla81si60iz-source",
|
||||
"rev": "58772ff9ddb38a4b2ec52da142d8532ba2fe7039",
|
||||
"sha256": "1lph7v27nqwgm3a0ssi8q348gjrkjwgqc50agw38j7xif6wj80cw",
|
||||
"srcDir": "",
|
||||
"url": "https://github.com/ehmry/cps/archive/58772ff9ddb38a4b2ec52da142d8532ba2fe7039.tar.gz"
|
||||
},
|
||||
{
|
||||
"method": "fetchzip",
|
||||
"packages": [
|
||||
"stew"
|
||||
],
|
||||
"path": "/nix/store/mqg8qzsbcc8xqabq2yzvlhvcyqypk72c-source",
|
||||
"rev": "3c91b8694e15137a81ec7db37c6c58194ec94a6a",
|
||||
"sha256": "17lfhfxp5nxvld78xa83p258y80ks5jb4n53152cdr57xk86y07w",
|
||||
"srcDir": "",
|
||||
"url": "https://github.com/status-im/nim-stew/archive/3c91b8694e15137a81ec7db37c6c58194ec94a6a.tar.gz"
|
||||
},
|
||||
{
|
||||
"method": "fetchzip",
|
||||
"packages": [
|
||||
"stew"
|
||||
],
|
||||
"path": "/nix/store/mqg8qzsbcc8xqabq2yzvlhvcyqypk72c-source",
|
||||
"rev": "3c91b8694e15137a81ec7db37c6c58194ec94a6a",
|
||||
"sha256": "17lfhfxp5nxvld78xa83p258y80ks5jb4n53152cdr57xk86y07w",
|
||||
"srcDir": "",
|
||||
"url": "https://github.com/status-im/nim-stew/archive/3c91b8694e15137a81ec7db37c6c58194ec94a6a.tar.gz"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
11
shell.nix
11
shell.nix
|
@ -1,11 +0,0 @@
|
|||
{ pkgs ? import <nixpkgs> { } }:
|
||||
|
||||
pkgs.buildNimPackage {
|
||||
name = "syndicate_utils";
|
||||
propagatedNativeBuildInputs =
|
||||
builtins.attrValues { inherit (pkgs) pkg-config solo5; };
|
||||
propagatedBuildInputs = builtins.attrValues {
|
||||
inherit (pkgs) getdns postgresql solo5 sqlite libxml2 libxslt;
|
||||
};
|
||||
lockFile = ./lock.json;
|
||||
}
|
5
sql.prs
5
sql.prs
|
@ -2,4 +2,7 @@ version 1 .
|
|||
|
||||
# When asserted the actor reponds to @target rows as records
|
||||
# of the given label and row columns as record fields.
|
||||
Query = <query @statement string @target #:any> .
|
||||
Query = <query @statement [any ...] @target #:any> .
|
||||
|
||||
# When a query fails this is asserted instead.
|
||||
SqlError = <sql-error @msg string @context string>.
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
||||
# SPDX-License-Identifier: Unlicense
|
||||
|
||||
# TODO: write a TAPS HTTP client. Figure out how to externalise TLS.
|
||||
|
||||
import
|
||||
std/[httpclient, options, streams, strutils, tables, uri],
|
||||
pkg/taps,
|
||||
pkg/preserves,
|
||||
pkg/syndicate, pkg/syndicate/protocols/http,
|
||||
./schema/config
|
||||
|
||||
proc url(req: HttpRequest): Uri =
|
||||
result.scheme = if req.port == 80: "http" else: "https"
|
||||
result.hostname = req.host.present
|
||||
result.port = $req.port
|
||||
for i, p in req.path:
|
||||
if 0 < i: result.path.add '/'
|
||||
result.path.add p.encodeUrl
|
||||
for key, vals in req.query:
|
||||
if result.query.len > 0:
|
||||
result.query.add '&'
|
||||
result.query.add key.string.encodeUrl
|
||||
for i, val in vals:
|
||||
if i == 0: result.query.add '='
|
||||
elif i < vals.high: result.query.add ','
|
||||
result.query.add val.string.encodeUrl
|
||||
|
||||
proc bodyString(req: HttpRequest): string =
|
||||
if req.body.orKind == RequestBodyKind.present:
|
||||
return cast[string](req.body.present)
|
||||
|
||||
proc spawnHttpClient*(turn: Turn; root: Cap): Actor {.discardable.} =
|
||||
|
||||
during(turn, root, ?:HttpClientArguments) do (ds: Cap):
|
||||
spawn("http-client", turn) do (turn: Turn):
|
||||
during(turn, ds, HttpContext.grabType) do (ctx: HttpContext):
|
||||
let peer = ctx.res.unembed(Cap).get
|
||||
var client = newHttpClient()
|
||||
try:
|
||||
var
|
||||
headers = newHttpHeaders()
|
||||
contentType = ""
|
||||
for key, val in ctx.req.headers:
|
||||
if key == Symbol"Content-Type":
|
||||
contentType = val
|
||||
client.headers[key.string] = val
|
||||
let stdRes = client.request(
|
||||
ctx.req.url,
|
||||
ctx.req.method.string.toUpper,
|
||||
ctx.req.bodyString, headers
|
||||
)
|
||||
var resp = HttpResponse(orKind: HttpResponseKind.status)
|
||||
resp.status.code = stdRes.status[0 .. 2].parseInt
|
||||
resp.status.message = stdRes.status[3 .. ^1]
|
||||
message(turn, peer, resp)
|
||||
resp = HttpResponse(orKind: HttpResponseKind.header)
|
||||
for key, val in stdRes.headers:
|
||||
if key == "Content-Type":
|
||||
contentType = val
|
||||
resp.header.name = key.Symbol
|
||||
resp.header.value = val
|
||||
message(turn, peer, resp)
|
||||
case contentType
|
||||
of "application/json", "text/preserves":
|
||||
message(turn, peer,
|
||||
initRecord("done", stdRes.bodyStream.readAll.parsePreserves))
|
||||
of "application/preserves":
|
||||
message(turn, peer,
|
||||
initRecord("done", stdRes.bodyStream.decodePreserves))
|
||||
else:
|
||||
resp = HttpResponse(orKind: HttpResponseKind.done)
|
||||
resp.done.chunk.string = stdRes.bodyStream.readAll()
|
||||
message(turn, peer, resp)
|
||||
except CatchableError as err:
|
||||
var resp = HttpResponse(orKind: HttpResponseKind.status)
|
||||
resp.status.code = 400
|
||||
resp.status.message = "Internal client error"
|
||||
message(turn, peer, resp)
|
||||
resp = HttpResponse(orKind: HttpResponseKind.done)
|
||||
resp.done.chunk.string = err.msg
|
||||
message(turn, peer, resp)
|
||||
client.close()
|
||||
do:
|
||||
client.close()
|
||||
|
||||
when isMainModule:
|
||||
import syndicate/relays
|
||||
runActor("main") do (turn: Turn):
|
||||
resolveEnvironment(turn) do (turn: Turn; ds: Cap):
|
||||
spawnHttpClient(turn, ds)
|
|
@ -0,0 +1 @@
|
|||
define:ssl
|
|
@ -7,7 +7,7 @@ when not defined(linux):
|
|||
{.error: "this component only tested for Linux".}
|
||||
|
||||
import std/oserrors
|
||||
import preserves
|
||||
import preserves, preserves/sugar
|
||||
import syndicate
|
||||
import ./schema/mountpoints
|
||||
|
||||
|
@ -20,11 +20,14 @@ proc mount(source, target, fsType: cstring; flags: culong; data: pointer): cint
|
|||
proc umount(target: cstring): cint {.importc, header: "<sys/mount.h>".}
|
||||
## `umount(2)`
|
||||
|
||||
proc spawnMountActor*(turn: var Turn; ds: Cap): Actor {.discardable.} =
|
||||
spawnActor(turn, "mount_actor") do (turn: var Turn):
|
||||
proc spawnMountActor*(turn: Turn; ds: Cap): Actor {.discardable.} =
|
||||
spawnActor(turn, "mount_actor") do (turn: Turn):
|
||||
let
|
||||
targetPat = ?Observe(pattern: !Mountpoint) ?? { 1: grabLit() }
|
||||
sourcePat = ?Observe(pattern: !Mountpoint) ?? { 0: grabLit(), 2: grabLit() }
|
||||
targetPat = observePattern(!Mountpoint, { @[%1]: grabLit() })
|
||||
sourcePat = observePattern(!Mountpoint, {
|
||||
@[%0]: grabLit(),
|
||||
@[%2]: grabLit(),
|
||||
})
|
||||
during(turn, ds, ?:BootArgs) do (ds: Cap):
|
||||
during(turn, ds, targetPat) do (target: string):
|
||||
during(turn, ds, sourcePat) do (source: string, fsType: string):
|
||||
|
@ -45,6 +48,6 @@ proc spawnMountActor*(turn: var Turn; ds: Cap): Actor {.discardable.} =
|
|||
|
||||
when isMainModule:
|
||||
import syndicate/relays
|
||||
runActor("main") do (turn: var Turn):
|
||||
resolveEnvironment(turn) do (turn: var Turn; ds: Cap):
|
||||
runActor("main") do (turn: Turn):
|
||||
resolveEnvironment(turn) do (turn: Turn; ds: Cap):
|
||||
discard spawnMountActor(turn, ds)
|
||||
|
|
|
@ -6,11 +6,11 @@ import preserves, syndicate, syndicate/relays
|
|||
|
||||
setControlCHook(proc () {.noconv.} = quit())
|
||||
|
||||
runActor("msg") do (turn: var Turn):
|
||||
runActor("msg") do (turn: Turn):
|
||||
let
|
||||
data = map(commandLineParams(), parsePreserves)
|
||||
cmd = paramStr(0).extractFilename.normalize
|
||||
resolveEnvironment(turn) do (turn: var Turn; ds: Cap):
|
||||
resolveEnvironment(turn) do (turn: Turn; ds: Cap):
|
||||
case cmd
|
||||
of "assert":
|
||||
for e in data:
|
||||
|
@ -18,5 +18,5 @@ runActor("msg") do (turn: var Turn):
|
|||
else: # "msg"
|
||||
for e in data:
|
||||
message(turn, ds, e)
|
||||
sync(turn, ds) do (turn: var Turn):
|
||||
sync(turn, ds) do (turn: Turn):
|
||||
stopActor(turn)
|
||||
|
|
|
@ -87,8 +87,26 @@ proc splitParams(params: StringPairs): (cstringArray, cstringArray) =
|
|||
for i, _ in params: strings[i] = params[i][1]
|
||||
result[1] = allocCStringArray(strings)
|
||||
|
||||
proc spawnPostgreActor*(turn: var Turn; root: Cap): Actor {.discardable.} =
|
||||
spawn("postgre", turn) do (turn: var Turn):
|
||||
proc renderSql(tokens: openarray[Value]): string =
|
||||
for token in tokens:
|
||||
if result.len > 0: result.add ' '
|
||||
case token.kind
|
||||
of pkSymbol:
|
||||
result.add token.symbol.string
|
||||
of pkString:
|
||||
result.add '\''
|
||||
result.add token.string
|
||||
result.add '\''
|
||||
of pkFloat, pkRegister, pkBigInt:
|
||||
result.add $token
|
||||
of pkBoolean:
|
||||
if token.bool: result.add '1'
|
||||
else: result.add '0'
|
||||
else:
|
||||
return ""
|
||||
|
||||
proc spawnPostgreActor*(turn: Turn; root: Cap): Actor {.discardable.} =
|
||||
spawn("postgre", turn) do (turn: Turn):
|
||||
during(turn, root, ?:PostgreArguments) do (params: StringPairs, ds: Cap):
|
||||
var
|
||||
conn: PGconn
|
||||
|
@ -102,21 +120,29 @@ proc spawnPostgreActor*(turn: var Turn; root: Cap): Actor {.discardable.} =
|
|||
statusHandle = publish(turn, ds,
|
||||
initRecord("status", toSymbol($status), msg.toPreserves))
|
||||
if status == CONNECTION_OK:
|
||||
during(turn, ds, ?:Query) do (statement: string, target: Cap):
|
||||
var res = PQexec(conn, statement)
|
||||
var st = PQresultStatus(res)
|
||||
discard publish(turn, ds, toRecord(
|
||||
"error", statement, toSymbol($PQresStatus(st)), $PQresultErrorMessage(res)))
|
||||
if st == PGRES_TUPLES_OK or st == PGRES_SINGLE_TUPLE:
|
||||
let tuples = PQntuples(res)
|
||||
let fields = PQnfields(res)
|
||||
if tuples > 0 and fields > 0:
|
||||
for r in 0..<tuples:
|
||||
var tupl = initSequence(fields)
|
||||
for f in 0..<fields:
|
||||
tupl[f] = toPreserves($PQgetvalue(res, r, f))
|
||||
discard publish(turn, target, tupl)
|
||||
PQclear(res)
|
||||
during(turn, ds, ?:Query) do (statement: seq[Value], target: Cap):
|
||||
var text = renderSql statement
|
||||
if text == "":
|
||||
discard publish(turn, ds, SqlError(msg: "invalid statement", context: $statement))
|
||||
else:
|
||||
var
|
||||
res = PQexec(conn, text)
|
||||
st = PQresultStatus(res)
|
||||
if st == PGRES_TUPLES_OK or st == PGRES_SINGLE_TUPLE:
|
||||
let tuples = PQntuples(res)
|
||||
let fields = PQnfields(res)
|
||||
if tuples > 0 and fields > 0:
|
||||
for r in 0..<tuples:
|
||||
var tupl = initSequence(fields)
|
||||
for f in 0..<fields:
|
||||
tupl[f] = toPreserves($PQgetvalue(res, r, f))
|
||||
discard publish(turn, target, tupl)
|
||||
else:
|
||||
discard publish(turn, ds, SqlError(
|
||||
msg: $PQresStatus(st),
|
||||
context: $PQresultErrorMessage(res),
|
||||
))
|
||||
PQclear(res)
|
||||
else:
|
||||
stderr.writeLine "refusing to do anything when status is ", status
|
||||
do:
|
||||
|
@ -127,6 +153,6 @@ proc spawnPostgreActor*(turn: var Turn; root: Cap): Actor {.discardable.} =
|
|||
when isMainModule:
|
||||
import syndicate/relays
|
||||
|
||||
runActor("main") do (turn: var Turn):
|
||||
resolveEnvironment(turn) do (turn: var Turn; ds: Cap):
|
||||
runActor("main") do (turn: Turn):
|
||||
resolveEnvironment(turn) do (turn: Turn; ds: Cap):
|
||||
spawnPostgreActor(turn, ds)
|
||||
|
|
|
@ -10,8 +10,8 @@ import ./schema/rofi
|
|||
if getEnv("ROFI_OUTSIDE") == "":
|
||||
quit("run this program in rofi")
|
||||
|
||||
runActor("rofi_script_actor") do (turn: var Turn):
|
||||
resolveEnvironment(turn) do (turn: var Turn; ds: Cap):
|
||||
runActor("rofi_script_actor") do (turn: Turn):
|
||||
resolveEnvironment(turn) do (turn: Turn; ds: Cap):
|
||||
case paramCount()
|
||||
of 0:
|
||||
let pat = ?:Options
|
||||
|
|
|
@ -10,6 +10,12 @@ type
|
|||
WebsocketArguments* {.preservesRecord: "websocket".} = object
|
||||
`field0`*: WebsocketArgumentsField0
|
||||
|
||||
HttpClientArgumentsField0* {.preservesDictionary.} = object
|
||||
`dataspace`* {.preservesEmbedded.}: EmbeddedRef
|
||||
|
||||
HttpClientArguments* {.preservesRecord: "http-client".} = object
|
||||
`field0`*: HttpClientArgumentsField0
|
||||
|
||||
JsonTranslatorArgumentsField0* {.preservesDictionary.} = object
|
||||
`argv`*: seq[string]
|
||||
`dataspace`* {.preservesEmbedded.}: EmbeddedRef
|
||||
|
@ -117,7 +123,8 @@ type
|
|||
`host`*: string
|
||||
`port`*: BiggestInt
|
||||
|
||||
proc `$`*(x: WebsocketArguments | JsonTranslatorArguments | SocketAddress |
|
||||
proc `$`*(x: WebsocketArguments | HttpClientArguments | JsonTranslatorArguments |
|
||||
SocketAddress |
|
||||
Base64DecoderArguments |
|
||||
JsonTranslatorConnected |
|
||||
JsonSocketTranslatorArguments |
|
||||
|
@ -136,7 +143,9 @@ proc `$`*(x: WebsocketArguments | JsonTranslatorArguments | SocketAddress |
|
|||
Tcp): string =
|
||||
`$`(toPreserves(x))
|
||||
|
||||
proc encode*(x: WebsocketArguments | JsonTranslatorArguments | SocketAddress |
|
||||
proc encode*(x: WebsocketArguments | HttpClientArguments |
|
||||
JsonTranslatorArguments |
|
||||
SocketAddress |
|
||||
Base64DecoderArguments |
|
||||
JsonTranslatorConnected |
|
||||
JsonSocketTranslatorArguments |
|
||||
|
|
|
@ -4,11 +4,15 @@ import
|
|||
|
||||
type
|
||||
Query* {.preservesRecord: "query".} = object
|
||||
`statement`*: string
|
||||
`statement`*: seq[Value]
|
||||
`target`* {.preservesEmbedded.}: Value
|
||||
|
||||
proc `$`*(x: Query): string =
|
||||
SqlError* {.preservesRecord: "sql-error".} = object
|
||||
`msg`*: string
|
||||
`context`*: string
|
||||
|
||||
proc `$`*(x: Query | SqlError): string =
|
||||
`$`(toPreserves(x))
|
||||
|
||||
proc encode*(x: Query): seq[byte] =
|
||||
proc encode*(x: Query | SqlError): seq[byte] =
|
||||
encode(toPreserves(x))
|
||||
|
|
|
@ -54,8 +54,19 @@ proc finalize(stmt: Stmt): cint {.importSqlite3.}
|
|||
|
||||
doAssert libversion_number() == SQLITE_VERSION_NUMBER
|
||||
|
||||
proc logError(db: Sqlite3; context: string) =
|
||||
writeLine(stderr, errmsg(db), ": ", context)
|
||||
proc assertError(facet: Facet; cap: Cap; db: Sqlite3; context: string) =
|
||||
run(facet) do (turn: Turn):
|
||||
publish(turn, cap, SqlError(
|
||||
msg: $errmsg(db),
|
||||
context: context,
|
||||
))
|
||||
|
||||
proc assertError(facet: Facet; cap: Cap; msg, context: string) =
|
||||
run(facet) do (turn: Turn):
|
||||
publish(turn, cap, SqlError(
|
||||
msg: msg,
|
||||
context: context,
|
||||
))
|
||||
|
||||
proc extractValue(stmt: Stmt; col: cint): Value =
|
||||
case column_type(stmt, col)
|
||||
|
@ -78,37 +89,61 @@ proc extractTuple(stmt: Stmt; arity: cint): Value =
|
|||
result = initSequence(arity)
|
||||
for col in 0..<arity: result[col] = extractValue(stmt, col)
|
||||
|
||||
proc spawnSqliteActor*(turn: var Turn; root: Cap): Actor {.discardable.} =
|
||||
spawn("sqlite-actor", turn) do (turn: var Turn):
|
||||
proc renderSql(tokens: openarray[Value]): string =
|
||||
for token in tokens:
|
||||
if result.len > 0: result.add ' '
|
||||
case token.kind
|
||||
of pkSymbol:
|
||||
result.add token.symbol.string
|
||||
of pkString:
|
||||
result.add '\''
|
||||
result.add token.string
|
||||
result.add '\''
|
||||
of pkFloat, pkRegister, pkBigInt:
|
||||
result.add $token
|
||||
of pkBoolean:
|
||||
if token.bool: result.add '1'
|
||||
else: result.add '0'
|
||||
else:
|
||||
return ""
|
||||
|
||||
proc spawnSqliteActor*(turn: Turn; root: Cap): Actor {.discardable.} =
|
||||
spawn("sqlite-actor", turn) do (turn: Turn):
|
||||
during(turn, root, ?:SqliteArguments) do (path: string, ds: Cap):
|
||||
stderr.writeLine("opening SQLite database ", path)
|
||||
var db: Sqlite3
|
||||
if open_v2(path, addr db, SQLITE_OPEN_READONLY, nil) != SQLITE_OK:
|
||||
logError(db, path)
|
||||
else:
|
||||
during(turn, ds, ?:Query) do (statement: string, target: Cap):
|
||||
var stmt: Stmt
|
||||
if prepare_v2(db, statement, statement.len.cint, addr stmt, nil) != SQLITE_OK:
|
||||
logError(db, statement)
|
||||
else:
|
||||
try:
|
||||
let arity = column_count(stmt)
|
||||
var res = step(stmt)
|
||||
while res == SQLITE_ROW:
|
||||
var rec = extractTuple(stmt, arity)
|
||||
discard publish(turn, target, rec)
|
||||
res = step(stmt)
|
||||
assert res != 100
|
||||
if res != SQLITE_DONE:
|
||||
logError(db, statement)
|
||||
finally:
|
||||
if finalize(stmt) != SQLITE_OK: logError(db, statement)
|
||||
do:
|
||||
close(db)
|
||||
stderr.writeLine("closed SQLite database ", path)
|
||||
linkActor(turn, path) do (turn: Turn):
|
||||
let facet = turn.facet
|
||||
stderr.writeLine("opening SQLite database ", path)
|
||||
var db: Sqlite3
|
||||
if open_v2(path, addr db, SQLITE_OPEN_READONLY, nil) != SQLITE_OK:
|
||||
assertError(facet, ds, db, path)
|
||||
else:
|
||||
turn.onStop do (turn: Turn):
|
||||
close(db)
|
||||
stderr.writeLine("closed SQLite database ", path)
|
||||
during(turn, ds, ?:Query) do (statement: seq[Value], target: Cap):
|
||||
var
|
||||
stmt: Stmt
|
||||
text = renderSql statement
|
||||
if text == "":
|
||||
assertError(facet, target, "invalid statement", $statement)
|
||||
elif prepare_v2(db, text, text.len.cint, addr stmt, nil) != SQLITE_OK:
|
||||
assertError(facet, target, db, text)
|
||||
else:
|
||||
try:
|
||||
let arity = column_count(stmt)
|
||||
var res = step(stmt)
|
||||
while res == SQLITE_ROW:
|
||||
var rec = extractTuple(stmt, arity)
|
||||
discard publish(turn, target, rec)
|
||||
res = step(stmt)
|
||||
assert res != 100
|
||||
if res != SQLITE_DONE:
|
||||
assertError(facet, target, db, text)
|
||||
finally:
|
||||
if finalize(stmt) != SQLITE_OK: assertError(facet, target, db, text)
|
||||
|
||||
when isMainModule:
|
||||
import syndicate/relays
|
||||
runActor("main") do (turn: var Turn):
|
||||
resolveEnvironment(turn) do (turn: var Turn; ds: Cap):
|
||||
runActor("main") do (turn: Turn):
|
||||
resolveEnvironment(turn) do (turn: Turn; ds: Cap):
|
||||
spawnSqliteActor(turn, ds)
|
||||
|
|
|
@ -15,8 +15,8 @@ import ./syndesizer/[
|
|||
pulses,
|
||||
xml_translator]
|
||||
|
||||
runActor("syndesizer") do (turn: var Turn):
|
||||
resolveEnvironment(turn) do (turn: var Turn; ds: Cap):
|
||||
runActor("syndesizer") do (turn: Turn):
|
||||
resolveEnvironment(turn) do (turn: Turn; ds: Cap):
|
||||
discard spawnTimerDriver(turn, ds)
|
||||
discard spawnBase64Decoder(turn, ds)
|
||||
discard spawnCacheActor(turn, ds)
|
||||
|
|
|
@ -3,33 +3,33 @@
|
|||
|
||||
import std/[base64, os]
|
||||
import pkg/hashlib/misc/blake2
|
||||
import preserves, syndicate
|
||||
import preserves, preserves/sugar, syndicate
|
||||
import ../schema/config
|
||||
import ../schema/base64 as schema
|
||||
|
||||
export Base64DecoderArguments
|
||||
export schema
|
||||
|
||||
proc spawnBase64Decoder*(turn: var Turn; root: Cap): Actor {.discardable.} =
|
||||
spawnActor(turn, "base64-decoder") do (turn: var Turn):
|
||||
proc spawnBase64Decoder*(turn: Turn; root: Cap): Actor {.discardable.} =
|
||||
spawnActor(turn, "base64-decoder") do (turn: Turn):
|
||||
let tmpDir = getTempDir()
|
||||
during(turn, root, ?:Base64DecoderArguments) do (ds: Cap):
|
||||
|
||||
let decTextPat = ?Observe(pattern: !Base64Text) ?? { 0: grabLit() }
|
||||
let decTextPat = observePattern(!Base64Text, { @[%0]: grabLit() })
|
||||
during(turn, ds, decTextPat) do (txt: string):
|
||||
discard publish(turn, ds, Base64Text(
|
||||
txt: txt,
|
||||
bin: cast[seq[byte]](decode(txt)),
|
||||
))
|
||||
|
||||
let encTextPat = ?Observe(pattern: !Base64Text) ?? { 1: grabLit() }
|
||||
let encTextPat = observePattern(!Base64Text, { @[%1]: grabLit() })
|
||||
during(turn, ds, encTextPat) do (bin: seq[byte]):
|
||||
discard publish(turn, ds, Base64Text(
|
||||
txt: encode(bin),
|
||||
bin: bin,
|
||||
))
|
||||
|
||||
let decFilePat = ?Observe(pattern: !Base64File) ?? { 0: grabLit() }
|
||||
let decFilePat = observePattern( !Base64File, { @[%0]: grabLit() })
|
||||
during(turn, ds, decFilePat) do (txt: string):
|
||||
var bin = decode(txt)
|
||||
var ctx = init[BLAKE2B_512]()
|
||||
|
@ -46,6 +46,6 @@ proc spawnBase64Decoder*(turn: var Turn; root: Cap): Actor {.discardable.} =
|
|||
|
||||
when isMainModule:
|
||||
import syndicate/relays
|
||||
runActor("main") do (turn: var Turn):
|
||||
resolveEnvironment(turn) do (turn: var Turn; ds: Cap):
|
||||
runActor("main") do (turn: Turn):
|
||||
resolveEnvironment(turn) do (turn: Turn; ds: Cap):
|
||||
spawnBase64Decoder(turn, ds)
|
||||
|
|
|
@ -18,9 +18,9 @@ type CacheEntity {.final.} = ref object of Entity
|
|||
pattern: Pattern
|
||||
lifetime: float64
|
||||
|
||||
method publish(cache: CacheEntity; turn: var Turn; ass: AssertionRef; h: Handle) =
|
||||
method publish(cache: CacheEntity; turn: Turn; ass: AssertionRef; h: Handle) =
|
||||
## Re-assert pattern captures in a sub-facet.
|
||||
discard inFacet(turn) do (turn: var Turn):
|
||||
discard inFacet(turn) do (turn: Turn):
|
||||
# TODO: a seperate facet for every assertion, too much?
|
||||
var ass = depattern(cache.pattern, ass.value.sequence)
|
||||
# Build an assertion with what he have of the pattern and capture.
|
||||
|
@ -30,12 +30,12 @@ method publish(cache: CacheEntity; turn: var Turn; ass: AssertionRef; h: Handle)
|
|||
stop(turn) # end this facet
|
||||
|
||||
proc isObserve(pat: Pattern): bool =
|
||||
pat.orKind == PatternKind.DCompound and
|
||||
pat.dcompound.orKind == DCompoundKind.rec and
|
||||
pat.dcompound.rec.label.isSymbol"Observe"
|
||||
pat.orKind == PatternKind.group and
|
||||
pat.group.type.orKind == GroupTypeKind.rec and
|
||||
pat.group.type.rec.label.isSymbol"Observe"
|
||||
|
||||
proc spawnCacheActor*(turn: var Turn; root: Cap): Actor =
|
||||
spawnActor(turn, "cache_actor") do (turn: var Turn):
|
||||
proc spawnCacheActor*(turn: Turn; root: Cap): Actor =
|
||||
spawnActor(turn, "cache_actor") do (turn: Turn):
|
||||
during(turn, root, ?:CacheArguments) do (ds: Cap, lifetime: float64):
|
||||
onPublish(turn, ds, ?:Observe) do (pat: Pattern, obs: Cap):
|
||||
var cache: CacheEntity
|
||||
|
@ -52,7 +52,7 @@ proc spawnCacheActor*(turn: var Turn; root: Cap): Actor =
|
|||
|
||||
when isMainModule:
|
||||
import syndicate/relays
|
||||
runActor("main") do (turn: var Turn):
|
||||
resolveEnvironment(turn) do (turn: var Turn; ds: Cap):
|
||||
runActor("main") do (turn: Turn):
|
||||
resolveEnvironment(turn) do (turn: Turn; ds: Cap):
|
||||
discard spawnTimerDriver(turn, ds)
|
||||
discard spawnCacheActor(turn, ds)
|
||||
|
|
|
@ -2,15 +2,15 @@
|
|||
# SPDX-License-Identifier: Unlicense
|
||||
|
||||
import std/[dirs, os, paths]
|
||||
import preserves
|
||||
import preserves, preserves/sugar
|
||||
import syndicate
|
||||
|
||||
import ../schema/[assertions, config]
|
||||
|
||||
proc spawnFileSystemUsageActor*(turn: var Turn; root: Cap): Actor {.discardable.} =
|
||||
spawn("file-system-usage", turn) do (turn: var Turn):
|
||||
proc spawnFileSystemUsageActor*(turn: Turn; root: Cap): Actor {.discardable.} =
|
||||
spawn("file-system-usage", turn) do (turn: Turn):
|
||||
during(turn, root, ?:FileSystemUsageArguments) do (ds: Cap):
|
||||
var pat = ?Observe(pattern: !FileSystemUsage) ?? { 0: grab() }
|
||||
let pat = observePattern(!FileSystemUsage, { @[%0]: grab() })
|
||||
during(turn, ds, pat) do (lit: Literal[string]):
|
||||
var ass = FileSystemUsage(path: lit.value)
|
||||
if fileExists(ass.path): ass.size = getFileSize(ass.path)
|
||||
|
@ -23,6 +23,6 @@ proc spawnFileSystemUsageActor*(turn: var Turn; root: Cap): Actor {.discardable.
|
|||
|
||||
when isMainModule:
|
||||
import syndicate/relays
|
||||
runActor("main") do (turn: var Turn):
|
||||
resolveEnvironment(turn) do (turn: var Turn; ds: Cap):
|
||||
runActor("main") do (turn: Turn):
|
||||
resolveEnvironment(turn) do (turn: Turn; ds: Cap):
|
||||
discard spawnFileSystemUsageActor(turn, ds)
|
||||
|
|
|
@ -8,7 +8,7 @@ import pkg/syndicate/drivers/http_driver
|
|||
import pkg/taps
|
||||
import ../schema/config
|
||||
|
||||
proc spawnHttpDriver*(turn: var Turn; ds: Cap): Actor {.discardable.}=
|
||||
proc spawnHttpDriver*(turn: Turn; ds: Cap): Actor {.discardable.}=
|
||||
http_driver.spawnHttpDriver(turn, ds)
|
||||
during(turn, ds, ?:HttpDriverArguments) do (ds: Cap):
|
||||
http_driver.spawnHttpDriver(turn, ds)
|
||||
|
@ -30,13 +30,13 @@ when isMainModule:
|
|||
return
|
||||
quit("failed to parse command line for route to Syndicate gatekeeper")
|
||||
|
||||
runActor("main") do (turn: var Turn):
|
||||
runActor("main") do (turn: Turn):
|
||||
let ds = newDataspace(turn)
|
||||
spawnRelays(turn, ds)
|
||||
resolve(turn, ds, envRoute(), spawnHttpDriver)
|
||||
|
||||
else:
|
||||
|
||||
runActor("main") do (turn: var Turn):
|
||||
resolveEnvironment(turn) do (turn: var Turn; ds: Cap):
|
||||
runActor("main") do (turn: Turn):
|
||||
resolveEnvironment(turn) do (turn: Turn; ds: Cap):
|
||||
spawnHttpDriver(turn, ds)
|
||||
|
|
|
@ -14,9 +14,9 @@ template translateSocketBody {.dirty.} =
|
|||
dec = newBufferedDecoder(0)
|
||||
buf = new string #TODO: get a pointer into the decoder
|
||||
alive = true
|
||||
proc kill(turn: var Turn) =
|
||||
proc kill(turn: Turn) =
|
||||
alive = false
|
||||
proc setup(turn: var Turn) =
|
||||
proc setup(turn: Turn) =
|
||||
# Closure, not CPS.
|
||||
onMessage(turn, ds, ?:SendJson) do (data: JsonNode):
|
||||
if alive:
|
||||
|
@ -38,7 +38,7 @@ template translateSocketBody {.dirty.} =
|
|||
dec.feed(buf[])
|
||||
var data = dec.parse()
|
||||
if data.isSome:
|
||||
proc send(turn: var Turn) =
|
||||
proc send(turn: Turn) =
|
||||
# Closure, not CPS.
|
||||
message(turn, ds, initRecord("recv", data.get))
|
||||
run(facet, send)
|
||||
|
@ -59,19 +59,19 @@ proc translateSocket(facet: Facet; ds: Cap; sa: UnixAddress) {.asyncio.} =
|
|||
socket[] = conn
|
||||
translateSocketBody()
|
||||
|
||||
proc spawnJsonSocketTranslator*(turn: var Turn; root: Cap): Actor {.discardable.} =
|
||||
spawnActor(turn, "json-socket-translator") do (turn: var Turn):
|
||||
proc spawnJsonSocketTranslator*(turn: Turn; root: Cap): Actor {.discardable.} =
|
||||
spawnActor(turn, "json-socket-translator") do (turn: Turn):
|
||||
during(turn, root, ?:JsonSocketTranslatorArguments) do (ds: Cap, sa: TcpAddress):
|
||||
linkActor(turn, "json-socket-translator") do (turn: var Turn):
|
||||
linkActor(turn, "json-socket-translator") do (turn: Turn):
|
||||
discard trampoline:
|
||||
whelp translateSocket(turn.facet, ds, sa)
|
||||
during(turn, root, ?:JsonSocketTranslatorArguments) do (ds: Cap, sa: UnixAddress):
|
||||
linkActor(turn, "json-socket-translator") do (turn: var Turn):
|
||||
linkActor(turn, "json-socket-translator") do (turn: Turn):
|
||||
discard trampoline:
|
||||
whelp translateSocket(turn.facet, ds, sa)
|
||||
|
||||
when isMainModule:
|
||||
import syndicate/relays
|
||||
runActor("main") do (turn: var Turn):
|
||||
resolveEnvironment(turn) do (turn: var Turn; ds: Cap):
|
||||
runActor("main") do (turn: Turn):
|
||||
resolveEnvironment(turn) do (turn: Turn; ds: Cap):
|
||||
spawnJsonSocketTranslator(turn, ds)
|
||||
|
|
|
@ -19,8 +19,8 @@ proc runChild(params: seq[string]): string =
|
|||
if result == "":
|
||||
stderr.writeLine "no ouput"
|
||||
|
||||
proc spawnJsonStdioTranslator*(turn: var Turn; root: Cap): Actor {.discardable.} =
|
||||
spawnActor(turn, "json-stdio-translator") do (turn: var Turn):
|
||||
proc spawnJsonStdioTranslator*(turn: Turn; root: Cap): Actor {.discardable.} =
|
||||
spawnActor(turn, "json-stdio-translator") do (turn: Turn):
|
||||
during(turn, root, ?:JsonTranslatorArguments) do (argv: seq[string], ds: Cap):
|
||||
var js = parseJson(runChild(argv))
|
||||
message(turn, ds, RecvJson(data: js))
|
||||
|
@ -28,6 +28,6 @@ proc spawnJsonStdioTranslator*(turn: var Turn; root: Cap): Actor {.discardable.}
|
|||
|
||||
when isMainModule:
|
||||
import syndicate/relays
|
||||
runActor("main") do (turn: var Turn):
|
||||
resolveEnvironment(turn) do (turn: var Turn; ds: Cap):
|
||||
runActor("main") do (turn: Turn):
|
||||
resolveEnvironment(turn) do (turn: Turn; ds: Cap):
|
||||
spawnJsonStdioTranslator(turn, ds)
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
# SPDX-License-Identifier: Unlicense
|
||||
|
||||
import std/[options, tables, times]
|
||||
import preserves, syndicate, syndicate/drivers/timers
|
||||
import preserves, preserves/sugar
|
||||
import syndicate, syndicate/drivers/timers
|
||||
|
||||
import ../schema/[assertions, config]
|
||||
|
||||
|
@ -16,7 +17,7 @@ type PulseEntity {.final.} = ref object of Entity
|
|||
observePattern: Pattern
|
||||
observing: bool
|
||||
|
||||
proc schedule(turn: var Turn; pulse: PulseEntity) =
|
||||
proc schedule(turn: Turn; pulse: PulseEntity) =
|
||||
## Schedule the next pulse.
|
||||
## The next pulse will be schedule using the current time as
|
||||
## reference point and not the moment of the previous pulse.
|
||||
|
@ -26,7 +27,7 @@ proc schedule(turn: var Turn; pulse: PulseEntity) =
|
|||
observer: pulse.self,
|
||||
))
|
||||
|
||||
method publish(pulse: PulseEntity; turn: var Turn; ass: AssertionRef; h: Handle) =
|
||||
method publish(pulse: PulseEntity; turn: Turn; ass: AssertionRef; h: Handle) =
|
||||
## Publish observers in reponse to <later-than …> assertions.
|
||||
pulse.timers.target.retract(turn, pulse.timerHandle)
|
||||
schedule(turn, pulse)
|
||||
|
@ -35,7 +36,7 @@ method publish(pulse: PulseEntity; turn: var Turn; ass: AssertionRef; h: Handle)
|
|||
pulse.target.publish(turn, a, h)
|
||||
pulse.target.sync(turn, pulse.self)
|
||||
|
||||
method message(pulse: PulseEntity; turn: var Turn; v: AssertionRef) =
|
||||
method message(pulse: PulseEntity; turn: Turn; v: AssertionRef) =
|
||||
## Retract observers in response to a sync message.
|
||||
pulse.observing = false
|
||||
for h in pulse.observers.keys:
|
||||
|
@ -45,7 +46,7 @@ type ProxyEntity {.final.} = ref object of Entity
|
|||
## A proxy `Entity` that diverts observers to a `PulseEntity`.
|
||||
pulse: PulseEntity
|
||||
|
||||
method publish(proxy: ProxyEntity; turn: var Turn; ass: AssertionRef; h: Handle) =
|
||||
method publish(proxy: ProxyEntity; turn: Turn; ass: AssertionRef; h: Handle) =
|
||||
## Proxy assertions that are not observations.
|
||||
if proxy.pulse.observePattern.matches ass.value:
|
||||
if proxy.pulse.observers.len == 0:
|
||||
|
@ -54,7 +55,7 @@ method publish(proxy: ProxyEntity; turn: var Turn; ass: AssertionRef; h: Handle)
|
|||
else:
|
||||
proxy.pulse.target.publish(turn, ass, h)
|
||||
|
||||
method retract(proxy: ProxyEntity; turn: var Turn; h: Handle) =
|
||||
method retract(proxy: ProxyEntity; turn: Turn; h: Handle) =
|
||||
## Retract proxied assertions.
|
||||
var obs: AssertionRef
|
||||
if proxy.pulse.observers.pop(h, obs):
|
||||
|
@ -65,15 +66,15 @@ method retract(proxy: ProxyEntity; turn: var Turn; h: Handle) =
|
|||
else:
|
||||
proxy.pulse.target.retract(turn, h)
|
||||
|
||||
method message(proxy: ProxyEntity; turn: var Turn; v: AssertionRef) =
|
||||
method message(proxy: ProxyEntity; turn: Turn; v: AssertionRef) =
|
||||
## Proxy mesages.
|
||||
proxy.pulse.target.message(turn, v)
|
||||
|
||||
method sync(proxy: ProxyEntity; turn: var Turn; peer: Cap) =
|
||||
method sync(proxy: ProxyEntity; turn: Turn; peer: Cap) =
|
||||
## Proxy sync.
|
||||
proxy.pulse.target.sync(turn, peer)
|
||||
|
||||
proc newProxyEntity(turn: var Turn; timers, ds: Cap; period: float): ProxyEntity =
|
||||
proc newProxyEntity(turn: Turn; timers, ds: Cap; period: float): ProxyEntity =
|
||||
new result
|
||||
result.pulse = PulseEntity(
|
||||
target: ds.target,
|
||||
|
@ -83,11 +84,11 @@ proc newProxyEntity(turn: var Turn; timers, ds: Cap; period: float): ProxyEntity
|
|||
)
|
||||
result.pulse.self = newCap(turn, result.pulse)
|
||||
|
||||
proc spawnPulseActor*(turn: var Turn; root: Cap): Actor =
|
||||
proc spawnPulseActor*(turn: Turn; root: Cap): Actor =
|
||||
## Spawn an actor that retracts and re-asserts observers on
|
||||
## a timed pulse. Requires a timer service on the `root` capability.
|
||||
spawnActor(turn, "pulse") do (turn: var Turn):
|
||||
let grabPeriod = ?Observe(pattern: !Pulse) ?? { 0: grab() }
|
||||
spawnActor(turn, "pulse") do (turn: Turn):
|
||||
let grabPeriod = observePattern(!Pulse, { @[%0]: grab() })
|
||||
during(turn, root, ?:PulseArguments) do (ds: Cap):
|
||||
during(turn, ds, grabPeriod) do (lit: Literal[float]):
|
||||
if lit.value < 0.000_1:
|
||||
|
@ -99,6 +100,6 @@ proc spawnPulseActor*(turn: var Turn; root: Cap): Actor =
|
|||
|
||||
when isMainModule:
|
||||
import syndicate/relays
|
||||
runActor("main") do (turn: var Turn):
|
||||
resolveEnvironment(turn) do (turn: var Turn; ds: Cap):
|
||||
runActor("main") do (turn: Turn):
|
||||
resolveEnvironment(turn) do (turn: Turn; ds: Cap):
|
||||
discard spawnPulseActor(turn, ds)
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
# SPDX-License-Identifier: Unlicense
|
||||
|
||||
import std/[options, parsexml, xmlparser, xmltree]
|
||||
import preserves, preserves/xmlhooks
|
||||
import preserves, preserves/sugar, preserves/xmlhooks
|
||||
import syndicate
|
||||
|
||||
import ../schema/[assertions, config]
|
||||
|
@ -17,17 +17,18 @@ proc translatePreserves(pr: Value): XmlTranslation {.gcsafe.} =
|
|||
var xn = result.pr.preservesTo(XmlNode)
|
||||
if xn.isSome: result.xml = $get(xn)
|
||||
|
||||
proc spawnXmlTranslator*(turn: var Turn; root: Cap): Actor {.discardable.} =
|
||||
spawnActor(turn, "xml-translator") do (turn: var Turn):
|
||||
proc spawnXmlTranslator*(turn: Turn; root: Cap): Actor {.discardable.} =
|
||||
spawnActor(turn, "xml-translator") do (turn: Turn):
|
||||
during(turn, root, ?:XmlTranslatorArguments) do (ds: Cap):
|
||||
let obsPat = ?Observe(pattern: !XmlTranslation)
|
||||
during(turn, ds, obsPat ?? {0: grab()}) do (xs: Literal[string]):
|
||||
let xmlPat = observePattern(!XmlTranslation, {@[%0]:grab()})
|
||||
during(turn, ds, xmlPat) do (xs: Literal[string]):
|
||||
publish(turn, ds, translateXml(xs.value))
|
||||
during(turn, ds, obsPat ?? {1: grab()}) do (pr: Literal[Value]):
|
||||
let prPat = observePattern(!XmlTranslation, {@[%1]:grab()})
|
||||
during(turn, ds, prPat) do (pr: Literal[Value]):
|
||||
publish(turn, ds, translatePreserves(pr.value))
|
||||
|
||||
when isMainModule:
|
||||
import syndicate/relays
|
||||
runActor("main") do (turn: var Turn):
|
||||
resolveEnvironment(turn) do (turn: var Turn; ds: Cap):
|
||||
runActor("main") do (turn: Turn):
|
||||
resolveEnvironment(turn) do (turn: Turn; ds: Cap):
|
||||
spawnXmlTranslator(turn, ds)
|
||||
|
|
|
@ -36,19 +36,19 @@ proc toLine(values: seq[Value]; prefix: char): string =
|
|||
add(result, $v)
|
||||
add(result, '\n')
|
||||
|
||||
method publish(dump: DumpEntity; turn: var Turn; ass: AssertionRef; h: Handle) =
|
||||
method publish(dump: DumpEntity; turn: Turn; ass: AssertionRef; h: Handle) =
|
||||
var values = ass.value.sequence
|
||||
stdout.write(values.toLine('+'))
|
||||
stdout.flushFile()
|
||||
dump.assertions[h] = values
|
||||
|
||||
method retract(dump: DumpEntity; turn: var Turn; h: Handle) =
|
||||
method retract(dump: DumpEntity; turn: Turn; h: Handle) =
|
||||
var values: seq[Value]
|
||||
if dump.assertions.pop(h, values):
|
||||
stdout.write(values.toLine('-'))
|
||||
stdout.flushFile()
|
||||
|
||||
method message*(dump: DumpEntity; turn: var Turn; ass: AssertionRef) =
|
||||
method message*(dump: DumpEntity; turn: Turn; ass: AssertionRef) =
|
||||
stdout.write(ass.value.sequence.toLine('!'))
|
||||
stdout.flushFile()
|
||||
|
||||
|
@ -60,8 +60,8 @@ proc main =
|
|||
let
|
||||
patterns = inputPatterns()
|
||||
entity = DumpEntity()
|
||||
runActor("syndex_card") do (turn: var Turn):
|
||||
resolveEnvironment(turn) do (turn: var Turn; ds: Cap):
|
||||
runActor("syndex_card") do (turn: Turn):
|
||||
resolveEnvironment(turn) do (turn: Turn; ds: Cap):
|
||||
for pat in patterns:
|
||||
discard observe(turn, ds, pat, entity)
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
# SPDX-License-Identifier: Unlicense
|
||||
|
||||
import std/[os, strutils]
|
||||
import preserves, syndicate
|
||||
import preserves, preserves/sugar, syndicate
|
||||
import ./schema/[assertions, config]
|
||||
|
||||
{.passC: staticExec("pkg-config --cflags libxslt").}
|
||||
|
@ -173,11 +173,11 @@ proc toPreservesHook*(xn: xmlNodePtr): Value =
|
|||
preserveSiblings(items, xn)
|
||||
items[0]
|
||||
|
||||
proc spawnXsltActor*(turn: var Turn; root: Cap): Actor {.discardable.} =
|
||||
spawnActor(turn, "xslt") do (turn: var Turn):
|
||||
proc spawnXsltActor*(turn: Turn; root: Cap): Actor {.discardable.} =
|
||||
spawnActor(turn, "xslt") do (turn: Turn):
|
||||
initLibXml()
|
||||
during(turn, root, ?:XsltArguments) do (ds: Cap):
|
||||
let sheetsPat = ?Observe(pattern: !XsltTransform) ?? {0: grab(), 1: grab()}
|
||||
let sheetsPat = observePattern(!XsltTransform, {@[%0]: grab(), @[%1]: grab()})
|
||||
during(turn, ds, sheetsPat) do (stylesheet: Literal[string], input: Literal[string]):
|
||||
let cur = loadStylesheet(stylesheet.value)
|
||||
if cur.isNil:
|
||||
|
@ -206,6 +206,6 @@ proc spawnXsltActor*(turn: var Turn; root: Cap): Actor {.discardable.} =
|
|||
|
||||
when isMainModule:
|
||||
import syndicate/relays
|
||||
runActor("main") do (turn: var Turn):
|
||||
resolveEnvironment(turn) do (turn: var Turn; ds: Cap):
|
||||
runActor("main") do (turn: Turn):
|
||||
resolveEnvironment(turn) do (turn: Turn; ds: Cap):
|
||||
spawnXsltActor(turn, ds)
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
# Package
|
||||
|
||||
version = "20240407"
|
||||
version = "20240509"
|
||||
author = "Emery Hemingway"
|
||||
description = "Utilites for Syndicated Actors and Synit"
|
||||
license = "unlicense"
|
||||
srcDir = "src"
|
||||
bin = @["mintsturdyref", "mount_actor", "msg", "postgre_actor", "preserve_process_environment", "rofi_script_actor", "sqlite_actor", "syndesizer", "syndump", "xslt_actor"]
|
||||
bin = @["http_client", "mintsturdyref", "mount_actor", "msg", "postgre_actor", "preserve_process_environment", "rofi_script_actor", "sqlite_actor", "syndesizer", "syndump", "xslt_actor"]
|
||||
|
||||
|
||||
# Dependencies
|
||||
|
||||
requires "http://git.syndicate-lang.org/ehmry/syndicate-nim.git >= 20240405", "https://github.com/ehmry/nim-sys.git#4ef3b624db86e331ba334e705c1aa235d55b05e1", "https://git.sr.ht/~ehmry/nim_taps >= 20240405"
|
||||
requires "https://git.syndicate-lang.org/ehmry/syndicate-nim.git >= 20240507", "https://github.com/ehmry/nim-sys.git#4ef3b624db86e331ba334e705c1aa235d55b05e1", "https://git.sr.ht/~ehmry/nim_taps >= 20240405"
|
||||
|
|
Loading…
Reference in New Issue