Compare commits
37 Commits
http_drive
...
trunk
Author | SHA1 | Date |
---|---|---|
Emery Hemingway | 50b88842f4 | |
Emery Hemingway | 9ada190061 | |
Emery Hemingway | c77fe46a48 | |
Emery Hemingway | 624cc7513a | |
Emery Hemingway | 6a4b34afcb | |
Emery Hemingway | 9b670d2c24 | |
Emery Hemingway | 6718a3b040 | |
Emery Hemingway | 8452d5811e | |
Emery Hemingway | d76f1df350 | |
Emery Hemingway | 0b4aa89311 | |
Emery Hemingway | a40cdbce2c | |
Emery Hemingway | 1fd920f1c8 | |
Emery Hemingway | 9e143c8b0a | |
Emery Hemingway | 59ca3d2443 | |
Emery Hemingway | 12ed9cd35b | |
Emery Hemingway | 92dd48b528 | |
Emery Hemingway | 32f8cd792c | |
Emery Hemingway | 1bf29bdf02 | |
Emery Hemingway | 6a7646ff54 | |
Emery Hemingway | dc134898b5 | |
Emery Hemingway | 3a0bd1cd02 | |
Emery Hemingway | cda940cf75 | |
Emery Hemingway | bf0b5d6b86 | |
Emery Hemingway | b3a417a072 | |
Emery Hemingway | 48408d2763 | |
Emery Hemingway | 494418540a | |
Emery Hemingway | e954fdefec | |
Emery Hemingway | de7683b467 | |
Emery Hemingway | 5e075f3a0c | |
Emery Hemingway | e2b96e39ef | |
Emery Hemingway | d40c29ecad | |
Emery Hemingway | 1f099d6bd2 | |
Emery Hemingway | 920cd28c89 | |
Emery Hemingway | fc9762eb87 | |
Emery Hemingway | 2b80be0fcf | |
Emery Hemingway | c9b38dd86e | |
Emery Hemingway | 242bda24e5 |
377
README.md
377
README.md
|
@ -4,8 +4,6 @@
|
||||||
|
|
||||||
A Syndicate multitool that includes a number of different actors that become active via configuration.
|
A Syndicate multitool that includes a number of different actors that become active via configuration.
|
||||||
|
|
||||||
Think of it as a Busybox for Syndicate, if Busybox was created before POSIX.
|
|
||||||
|
|
||||||
Whether you use a single instance for many protocols or many specialized instances is up to you.
|
Whether you use a single instance for many protocols or many specialized instances is up to you.
|
||||||
|
|
||||||
### Cache
|
### Cache
|
||||||
|
@ -28,6 +26,7 @@ Example configuration:
|
||||||
$cap <cache { dataspace: $nixspace lifetime: 3600.0 }> ]
|
$cap <cache { dataspace: $nixspace lifetime: 3600.0 }> ]
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
### File System Usage
|
### File System Usage
|
||||||
|
|
||||||
Summarize the size of file-system directory. Equivalent to `du -s -b`.
|
Summarize the size of file-system directory. Equivalent to `du -s -b`.
|
||||||
|
@ -46,11 +45,50 @@ 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
|
### JSON Socket Translator
|
||||||
|
|
||||||
Communicate with sockets that send and receive lines of JSON using `<send …>` and `<recv …>` messages.
|
Communicate with sockets that send and receive lines of JSON using `<send …>` and `<recv …>` messages.
|
||||||
|
Responds to the gatekeeper step `<json-socket-translator { socket: <unix "…"> / <tcp "…" … }> $resolver>`.
|
||||||
Do not send messages into the dataspace configure with `<json-socket-translator …>` until `<connected @socketPath string>` is asserted.
|
|
||||||
|
|
||||||
```
|
```
|
||||||
# MPV configuration example
|
# MPV configuration example
|
||||||
|
@ -68,25 +106,11 @@ Do not send messages into the dataspace configure with `<json-socket-translator
|
||||||
protocol: none
|
protocol: none
|
||||||
}>
|
}>
|
||||||
|
|
||||||
let ?mpvSpace = dataspace
|
let ?resolver = dataspace
|
||||||
|
$resolver ? <accepted ?mpvSpace> $mpvSpace [
|
||||||
? <service-state <daemon mpv-server> ready> [
|
|
||||||
<require-service <daemon syndesizer>>
|
|
||||||
? <service-object <daemon syndesizer> ?cap> [
|
|
||||||
$cap <json-socket-translator {
|
|
||||||
dataspace: $mpvSpace
|
|
||||||
socket: "/run/user/1000/mpv.sock"
|
|
||||||
}>
|
|
||||||
]
|
|
||||||
]
|
|
||||||
|
|
||||||
$mpvSpace [
|
|
||||||
|
|
||||||
# announce the dataspace when the translator is connected
|
# announce the dataspace when the translator is connected
|
||||||
? <connected $socketPath> [
|
|
||||||
$config <mpv $mpvSpace>
|
$config <mpv $mpvSpace>
|
||||||
$config <bind <ref { oid: "mpv" key: #x"" }> $mpvSpace #f>
|
$config <bind <ref { oid: "mpv" key: #x"" }> $mpvSpace #f>
|
||||||
]
|
|
||||||
|
|
||||||
# translate <play-file …> to an MPV command
|
# translate <play-file …> to an MPV command
|
||||||
?? <play-file ?file> [
|
?? <play-file ?file> [
|
||||||
|
@ -98,6 +122,15 @@ $mpvSpace [
|
||||||
! <send { "command": ["playlist-clear"] }>
|
! <send { "command": ["playlist-clear"] }>
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
|
||||||
|
? <service-state <daemon mpv-server> ready> [
|
||||||
|
<require-service <daemon syndesizer>>
|
||||||
|
? <service-object <daemon syndesizer> ?cap> [
|
||||||
|
$cap <resolve <json-socket-translator {
|
||||||
|
socket: <unix "/run/user/1000/mpv.sock">
|
||||||
|
}> $resolver>
|
||||||
|
]
|
||||||
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
### JSON Stdio Translator
|
### JSON Stdio Translator
|
||||||
|
@ -123,38 +156,6 @@ let ?ds = dataspace
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
### PostgreSQL
|
|
||||||
|
|
||||||
Readonly access to PostgreSQL databases. Asserts rows as records in response to SQL query assertions. Dynamic updates are not implemented.
|
|
||||||
|
|
||||||
Can be disabled by passing `--define:withPostgre=no` to the Nim compiler.
|
|
||||||
|
|
||||||
```
|
|
||||||
# Configuration example
|
|
||||||
<require-service <daemon syndesizer>>
|
|
||||||
|
|
||||||
let ?sqlspace = dataspace
|
|
||||||
|
|
||||||
? <service-object <daemon syndesizer> ?cap> [
|
|
||||||
$cap <postgre {
|
|
||||||
dataspace: $sqlspace
|
|
||||||
connection: [
|
|
||||||
["host" "example.com"]
|
|
||||||
["dbname" "foobar"]
|
|
||||||
["user" "hackme"]
|
|
||||||
]
|
|
||||||
}>
|
|
||||||
]
|
|
||||||
|
|
||||||
let ?tuplespace = dataspace
|
|
||||||
|
|
||||||
$sqlspace <query "SELECT id, name FROM stuff" $tuplespace>
|
|
||||||
|
|
||||||
$tuplespace ? [?id ?name] [
|
|
||||||
$log ! <log "-" { row: <example-row $id $name> }>
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
### Pulse proxy
|
### Pulse proxy
|
||||||
|
|
||||||
A proxy actor that passes assertions and messages to a configured capability but only asserts observations on a a periodic pulse.
|
A proxy actor that passes assertions and messages to a configured capability but only asserts observations on a a periodic pulse.
|
||||||
|
@ -199,56 +200,16 @@ let ?tuplespace = dataspace
|
||||||
|
|
||||||
$sqlspace <query "SELECT id, name FROM stuff" $tuplespace>
|
$sqlspace <query "SELECT id, name FROM stuff" $tuplespace>
|
||||||
|
|
||||||
$tuplespace ? [?id ?name] [
|
$tuplespace [
|
||||||
|
? [?id ?name] [
|
||||||
$log ! <log "-" { row: <example-row $id $name> }>
|
$log ! <log "-" { row: <example-row $id $name> }>
|
||||||
|
]
|
||||||
|
? <sqlite-error ?msg ?ctx> [
|
||||||
|
$log ! <log "-" { msg: $msg ctx: $ctx }>
|
||||||
|
]
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
### Webooks
|
|
||||||
|
|
||||||
Listens for webhook requests and sends request data to a dataspace as messages.
|
|
||||||
Request data is formated according to the http schema [defined in syndicate-protocols](https://git.syndicate-lang.org/syndicate-lang/syndicate-protocols/src/branch/main/schemas/http.prs), with the exception that messages bodies may be **bytes**, **string**, or **any** for the `content-type`s of `application/octet-stream`, `text/*`, and `application/json` respectively.
|
|
||||||
|
|
||||||
```
|
|
||||||
# Configuration example
|
|
||||||
<require-service <daemon syndesizer>>
|
|
||||||
? <service-object <daemon syndesizer> ?cap> [
|
|
||||||
$cap <webhooks {
|
|
||||||
listen: <tcp "0.0.0.0" 1048>
|
|
||||||
endpoints: {
|
|
||||||
|
|
||||||
# http://0.0.0.0:1048/my-endpoint
|
|
||||||
["my-endpoint"]: $target-dataspace
|
|
||||||
|
|
||||||
# http://0.0.0.0:1048/some/multi-element/path
|
|
||||||
["some", "multi-element", "path"]: $target-dataspace
|
|
||||||
|
|
||||||
}
|
|
||||||
}>
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
### Websockets
|
|
||||||
|
|
||||||
connects to a websocket endpoint. During the lifetime of the connection a `<connected $URL>` assertion is made. Messages received from the server are sent to the dataspace wrapped in `<recv …>` records and messages observed as `<send …>` are sent to the server.
|
|
||||||
|
|
||||||
```
|
|
||||||
# Configuration example
|
|
||||||
<require-service <daemon syndesizer>>
|
|
||||||
|
|
||||||
let ?websocketspace = dataspace
|
|
||||||
|
|
||||||
? <service-object <daemon syndesizer> ?cap> [
|
|
||||||
$cap <websocket {
|
|
||||||
dataspace: $websocketspace
|
|
||||||
url: "ws://127.0.0.1:5225/"
|
|
||||||
}>
|
|
||||||
]
|
|
||||||
|
|
||||||
$websocketspace ? <connected $websocketUrl> [
|
|
||||||
<bind <ref { oid: "websocket" key: #x"" }> $websocketspace #f>
|
|
||||||
]
|
|
||||||
```
|
|
||||||
### XML translator
|
### XML translator
|
||||||
|
|
||||||
Translates between Preserves and XML according to the [Conventions for Common Data Types](https://preserves.dev/conventions.html).
|
Translates between Preserves and XML according to the [Conventions for Common Data Types](https://preserves.dev/conventions.html).
|
||||||
|
@ -270,30 +231,95 @@ Examples:
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
### XSLT processor
|
|
||||||
|
|
||||||
Perform XML stylesheet transformations. For a given textual XSLT stylesheet and a textual XML document generate an abstract XML document in Preserves form. Inputs may be XML text or paths to XML files.
|
|
||||||
|
|
||||||
```
|
|
||||||
# Configuration example
|
|
||||||
let ?ds = dataspace
|
|
||||||
$ds [
|
|
||||||
? <xslt-transform "/stylesheet.xls" "/doc.xml" ?output> [
|
|
||||||
? <xml-translation ?text $output> [
|
|
||||||
$log ! <log "-" { xslt-output: $text }>
|
|
||||||
]
|
|
||||||
]
|
|
||||||
]
|
|
||||||
|
|
||||||
<require-service <daemon syndesizer>>
|
|
||||||
? <service-object <daemon syndesizer> ?cap> $cap [
|
|
||||||
<xml-translator { dataspace: $ds }>
|
|
||||||
<xslt { dataspace: $ds }>
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## esc-printer-driver
|
||||||
|
|
||||||
|
A basic [ESC/P](https://en.wikipedia.org/wiki/ESC/P) printer driver.
|
||||||
|
|
||||||
|
Takes a path to a printer device file as a command line argument.
|
||||||
|
The driver speaks the gatekeeper protocol and responds to the `<print {}>` step with a capability that prints strings it receives as messages.
|
||||||
|
While the `<bold>` or `<italic>` is asserted to this entity the printer will go into the corresponding font mode (if the printer supports it).
|
||||||
|
|
||||||
|
Sample Syndicate server script:
|
||||||
|
```
|
||||||
|
<require-service <daemon printer>>
|
||||||
|
|
||||||
|
let ?printer-resolver = dataspace
|
||||||
|
$printer-resolver ? <accepted ?printer> [
|
||||||
|
$printer <italic>
|
||||||
|
$printer ! "printer resolved\r\n"
|
||||||
|
]
|
||||||
|
|
||||||
|
? <service-object <daemon printer> ?cap> [
|
||||||
|
$cap <resolve <printer {}> $printer-resolver>
|
||||||
|
$log ! <log "-" { line: "printer started"}>
|
||||||
|
]
|
||||||
|
|
||||||
|
<daemon printer {
|
||||||
|
argv: [ "/bin/esc-printer-driver" "/dev/usb/lp0"]
|
||||||
|
protocol: application/syndicate
|
||||||
|
clearEnv: #t
|
||||||
|
}>
|
||||||
|
```
|
||||||
|
|
||||||
|
## 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
|
## mintsturdyref
|
||||||
|
|
||||||
A utility for minting [Sturdyrefs](https://synit.org/book/operation/builtin/gatekeeper.html#sturdyrefs).
|
A utility for minting [Sturdyrefs](https://synit.org/book/operation/builtin/gatekeeper.html#sturdyrefs).
|
||||||
|
@ -343,31 +369,42 @@ Sample Syndicate server script:
|
||||||
|
|
||||||
## msg
|
## msg
|
||||||
|
|
||||||
A utility that sends messages to `$SYNDICATE_ROUTE`.
|
A utility that parses its command-line arguments as Preserves and send them as messages to `$SYNDICATE_ROUTE`.
|
||||||
|
When called as `assert` (by a symlink or a rename) it will make assertions instead.
|
||||||
|
|
||||||
|
## PostgreSQL
|
||||||
|
|
||||||
## net_mapper
|
Readonly access to PostgreSQL databases.
|
||||||
|
Asserts rows as records in response to SQL query assertions.
|
||||||
|
Dynamic updates are not implemented.
|
||||||
|
|
||||||
Publishes ICMP packet round-trip-times. See [net_mapper.prs](./net_mapper.prs) for a protocol description. [Source](./src/net_mapper.nim).
|
|
||||||
|
|
||||||
Example script:
|
|
||||||
```
|
```
|
||||||
? <machine-dataspace ?machine> [
|
let ?postgreStep = <postgre {connection: [["host" "db.example.com"] ["dbname" "example"] ["user" "hackme"]]}>
|
||||||
$machine ? <rtt "10.0.33.136" ?min ?avg ?max> [
|
|
||||||
$log ! <log "-" { ping: { min: $min avg: $avg max: $max } }>
|
|
||||||
]
|
|
||||||
|
|
||||||
$config [
|
let ?tuplespace = dataspace
|
||||||
<require-service <daemon net_mapper>>
|
$tuplespace ? ?row [
|
||||||
<daemon net_mapper {
|
$log ! <log "-" { line: $row }>
|
||||||
argv: ["/bin/net_mapper"]
|
|
||||||
protocol: application/syndicate
|
|
||||||
}>
|
|
||||||
? <service-object <daemon net_mapper> ?cap> [
|
|
||||||
$cap { dataspace: $machine }
|
|
||||||
]
|
|
||||||
]
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
let ?resolver = dataspace
|
||||||
|
$resolver ? <accepted ?sqlspace> [
|
||||||
|
$sqlspace ? <sql-error ?msg ?context> [
|
||||||
|
$log ! <log "-" { line: $msg context: $context }>
|
||||||
|
]
|
||||||
|
$sqlspace <query [SELECT firstname FROM users] $tuplespace>
|
||||||
|
]
|
||||||
|
|
||||||
|
<require-service <daemon postgre-actor>>
|
||||||
|
$config ? <service-object <daemon postgre-actor> ?cap> [
|
||||||
|
$cap <resolve $postgreStep $resolver>
|
||||||
|
]
|
||||||
|
|
||||||
|
<daemon postgre-actor {
|
||||||
|
argv: [ "/bin/postgre-actor" ]
|
||||||
|
clearEnv: #t
|
||||||
|
protocol: application/syndicate
|
||||||
|
}>
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## preserve_process_environment
|
## preserve_process_environment
|
||||||
|
@ -375,6 +412,46 @@ Example script:
|
||||||
This utility serializes it's process environment to Preserves and prints it to stdout.
|
This utility serializes it's process environment to Preserves and prints it to stdout.
|
||||||
It can be used to feed the environment variables of a nested child of the Syndicate server back to the server. For example, to retreive the environmental variables that a desktop manager passed on to its children.
|
It can be used to feed the environment variables of a nested child of the Syndicate server back to the server. For example, to retreive the environmental variables that a desktop manager passed on to its children.
|
||||||
|
|
||||||
|
## SQLite
|
||||||
|
|
||||||
|
Readonly access to SQLite databases.
|
||||||
|
Asserts rows as records in response to SQL query assertions.
|
||||||
|
Dynamic updates are not implemented.
|
||||||
|
|
||||||
|
```
|
||||||
|
# Configuration example
|
||||||
|
let ?sqliteStep = <sqlite { database: "/var/db/stuff.db" }>
|
||||||
|
|
||||||
|
let ?tuplespace = dataspace
|
||||||
|
$tuplespace ? ?row [
|
||||||
|
$log ! <log "-" { line: $row }>
|
||||||
|
]
|
||||||
|
|
||||||
|
let ?resolver = dataspace
|
||||||
|
$resolver [
|
||||||
|
? <rejected ?detail> [
|
||||||
|
$log ! <log "-" { line: $detail }>
|
||||||
|
]
|
||||||
|
? <accepted ?sqlspace> [
|
||||||
|
$log ! <log "-" { sqlspace: $sqlspace }>
|
||||||
|
$sqlspace ? <sql-error ?msg ?context> [
|
||||||
|
$log ! <log "-" { line: $msg context: $context }>
|
||||||
|
]
|
||||||
|
$sqlspace <query [ SELECT local_display_name FROM contacts ] $tuplespace>
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
<require-service <daemon sqlite-actor>>
|
||||||
|
$config ? <service-object <daemon sqlite-actor> ?cap> [
|
||||||
|
$cap <resolve $sqliteStep $resolver>
|
||||||
|
]
|
||||||
|
|
||||||
|
<daemon sqlite-actor {
|
||||||
|
argv: [ "/bin/sqlite-actor" ]
|
||||||
|
clearEnv: #t
|
||||||
|
protocol: application/syndicate
|
||||||
|
}>
|
||||||
|
```
|
||||||
|
|
||||||
## syndump
|
## syndump
|
||||||
|
|
||||||
|
@ -385,3 +462,25 @@ Example
|
||||||
# Print patterns in use, filter down with AWK to only the published patterns.
|
# Print patterns in use, filter down with AWK to only the published patterns.
|
||||||
$ FS=':' syndump '<Observe ? _>' | awk -F : '/^+/ { print $2 }'
|
$ FS=':' syndump '<Observe ? _>' | awk -F : '/^+/ { print $2 }'
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## XSLT processor
|
||||||
|
|
||||||
|
Perform XML stylesheet transformations. For a given textual XSLT stylesheet and a textual XML document generate an abstract XML document in Preserves form. Inputs may be XML text or paths to XML files.
|
||||||
|
|
||||||
|
```
|
||||||
|
# Configuration example
|
||||||
|
let ?ds = dataspace
|
||||||
|
$ds [
|
||||||
|
? <xslt-transform "/stylesheet.xls" "/doc.xml" ?output> [
|
||||||
|
? <xml-translation ?text $output> [
|
||||||
|
$log ! <log "-" { xslt-output: $text }>
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
<require-service <daemon xslt_actor>>
|
||||||
|
? <service-object <daemon xslt_actor> ?cap> $cap [
|
||||||
|
<xml-translator { dataspace: $ds }>
|
||||||
|
<xslt { dataspace: $ds }>
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
8
Tupfile
8
Tupfile
|
@ -1,2 +1,8 @@
|
||||||
include_rules
|
include_rules
|
||||||
: lock.json |> !nim_cfg |> | ./<lock>
|
|
||||||
|
: sbom.json |> !sbom-to-nix |> | ./<lock>
|
||||||
|
run ./Tuprules.jq sbom.json
|
||||||
|
|
||||||
|
: foreach {bin} |> !assert_built |>
|
||||||
|
: &(BIN_DIR)/msg |> !symlink |> &(BIN_DIR)/beep
|
||||||
|
: &(BIN_DIR)/msg |> !symlink |> &(BIN_DIR)/assert
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
#! /usr/bin/env -S jq --raw-output --from-file
|
||||||
|
.metadata.component.properties as $props |
|
||||||
|
$props |
|
||||||
|
( map( select(.name | .[0:10] == "nim:binDir") ) +
|
||||||
|
map( select(.name | .[0:10] == "nim:srcDir") ) |
|
||||||
|
map( .value )
|
||||||
|
) + ["."] | .[0] as $binDir |
|
||||||
|
|
||||||
|
$props |
|
||||||
|
map( select(.name | .[0:8] == "nim:bin:") ) |
|
||||||
|
map( ": \($binDir)/\(.value).nim |> !nim_bin |> &(BIN_DIR)/\(.name[8:]) {bin}" ) |
|
||||||
|
join("\n")
|
|
@ -1,4 +1,7 @@
|
||||||
include ../syndicate-nim/depends.tup
|
include ../syndicate-nim/depends.tup
|
||||||
|
PROJECT_DIR = $(TUP_CWD)
|
||||||
|
|
||||||
NIM = $(DIRENV) $(NIM)
|
NIM = $(DIRENV) $(NIM)
|
||||||
NIM_FLAGS += --path:$(TUP_CWD)/../syndicate-nim/src
|
NIM_GROUPS += $(SYNDICATE_PROTOCOL)
|
||||||
NIM_GROUPS += $(TUP_CWD)/<lock>
|
NIM_GROUPS += $(PROJECT_DIR)/<lock>
|
||||||
|
NIM_GROUPS += $(PROJECT_DIR)/<schema>
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
version 1.
|
||||||
|
|
||||||
|
Base64Text = <base64 @txt string @bin bytes> .
|
||||||
|
Base64File = <base64-file @txt string @path string @size int> .
|
|
@ -0,0 +1,196 @@
|
||||||
|
{
|
||||||
|
lib,
|
||||||
|
stdenv,
|
||||||
|
fetchgit,
|
||||||
|
fetchzip,
|
||||||
|
runCommand,
|
||||||
|
xorg,
|
||||||
|
nim,
|
||||||
|
nimOverrides,
|
||||||
|
}:
|
||||||
|
|
||||||
|
let
|
||||||
|
fetchers = {
|
||||||
|
fetchzip =
|
||||||
|
{ url, sha256, ... }:
|
||||||
|
fetchzip {
|
||||||
|
name = "source";
|
||||||
|
inherit url sha256;
|
||||||
|
};
|
||||||
|
fetchgit =
|
||||||
|
{
|
||||||
|
fetchSubmodules ? false,
|
||||||
|
leaveDotGit ? false,
|
||||||
|
rev,
|
||||||
|
sha256,
|
||||||
|
url,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
fetchgit {
|
||||||
|
inherit
|
||||||
|
fetchSubmodules
|
||||||
|
leaveDotGit
|
||||||
|
rev
|
||||||
|
sha256
|
||||||
|
url
|
||||||
|
;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
filterPropertiesToAttrs =
|
||||||
|
prefix: properties:
|
||||||
|
lib.pipe properties [
|
||||||
|
(builtins.filter ({ name, ... }: (lib.strings.hasPrefix prefix name)))
|
||||||
|
(map (
|
||||||
|
{ name, value }:
|
||||||
|
{
|
||||||
|
name = lib.strings.removePrefix prefix name;
|
||||||
|
inherit value;
|
||||||
|
}
|
||||||
|
))
|
||||||
|
builtins.listToAttrs
|
||||||
|
];
|
||||||
|
|
||||||
|
buildNimCfg =
|
||||||
|
{ backend, components, ... }:
|
||||||
|
let
|
||||||
|
componentSrcDirs = map (
|
||||||
|
{ properties, ... }:
|
||||||
|
let
|
||||||
|
fodProps = filterPropertiesToAttrs "nix:fod:" properties;
|
||||||
|
fod = fetchers.${fodProps.method} fodProps;
|
||||||
|
srcDir = fodProps.srcDir or "";
|
||||||
|
in
|
||||||
|
if srcDir == "" then fod else "${fod}/${srcDir}"
|
||||||
|
) components;
|
||||||
|
in
|
||||||
|
runCommand "nim.cfg"
|
||||||
|
{
|
||||||
|
outputs = [
|
||||||
|
"out"
|
||||||
|
"src"
|
||||||
|
];
|
||||||
|
nativeBuildInputs = [ xorg.lndir ];
|
||||||
|
}
|
||||||
|
''
|
||||||
|
pkgDir=$src/pkg
|
||||||
|
cat << EOF >> $out
|
||||||
|
backend:${backend}
|
||||||
|
path:"$src"
|
||||||
|
path:"$pkgDir"
|
||||||
|
EOF
|
||||||
|
mkdir -p "$pkgDir"
|
||||||
|
${lib.strings.concatMapStrings (d: ''
|
||||||
|
lndir "${d}" "$pkgDir"
|
||||||
|
'') componentSrcDirs}
|
||||||
|
'';
|
||||||
|
|
||||||
|
buildCommands = lib.attrsets.mapAttrsToList (
|
||||||
|
output: input: ''
|
||||||
|
nim compile $nimFlags --out:${output} ${input}
|
||||||
|
''
|
||||||
|
);
|
||||||
|
|
||||||
|
installCommands = lib.attrsets.mapAttrsToList (
|
||||||
|
output: input: ''
|
||||||
|
install -Dt $out/bin ${output}
|
||||||
|
''
|
||||||
|
);
|
||||||
|
in
|
||||||
|
|
||||||
|
callerArg: sbomArg:
|
||||||
|
|
||||||
|
let
|
||||||
|
applySbom =
|
||||||
|
{
|
||||||
|
passthru ? { },
|
||||||
|
...
|
||||||
|
}@prevAttrs:
|
||||||
|
let
|
||||||
|
sbom = lib.attrsets.recursiveUpdate (
|
||||||
|
if builtins.isAttrs sbomArg then sbomArg else builtins.fromJSON (builtins.readFile sbomArg)
|
||||||
|
) passthru.sbom or { };
|
||||||
|
|
||||||
|
properties = # SBOM metadata.component.properties as an attrset.
|
||||||
|
lib.attrsets.recursiveUpdate (builtins.listToAttrs sbom.metadata.component.properties)
|
||||||
|
passthru.properties or { };
|
||||||
|
|
||||||
|
nimBin = # A mapping of Nim module file paths to names of programs.
|
||||||
|
lib.attrsets.recursiveUpdate (lib.pipe properties [
|
||||||
|
(lib.attrsets.filterAttrs (name: value: lib.strings.hasPrefix "nim:bin:" name))
|
||||||
|
(lib.attrsets.mapAttrs' (
|
||||||
|
name: value: {
|
||||||
|
name = lib.strings.removePrefix "nim:bin:" name;
|
||||||
|
value = "${properties."nim:binDir" or (properties."nim:srcDir" or ".")}/${value}";
|
||||||
|
}
|
||||||
|
))
|
||||||
|
]) passthru.nimBin or { };
|
||||||
|
in
|
||||||
|
{
|
||||||
|
strictDeps = true;
|
||||||
|
|
||||||
|
pname = prevAttrs.pname or sbom.metadata.component.name;
|
||||||
|
version = prevAttrs.version or sbom.metadata.component.version or null;
|
||||||
|
|
||||||
|
configurePhase =
|
||||||
|
prevAttrs.configurePhase or ''
|
||||||
|
runHook preConfigure
|
||||||
|
echo "nim.cfg << $nimCfg"
|
||||||
|
cat $nimCfg >> nim.cfg
|
||||||
|
cat << EOF >> nim.cfg
|
||||||
|
nimcache:"$NIX_BUILD_TOP/nimcache"
|
||||||
|
parallelBuild:$NIX_BUILD_CORES
|
||||||
|
EOF
|
||||||
|
runHook postConfigure
|
||||||
|
'';
|
||||||
|
|
||||||
|
buildPhase =
|
||||||
|
prevAttrs.buildPhase or ''
|
||||||
|
runHook preBuild
|
||||||
|
${lib.strings.concatLines (buildCommands nimBin)}
|
||||||
|
runHook postBuild
|
||||||
|
'';
|
||||||
|
|
||||||
|
installPhase =
|
||||||
|
prevAttrs.installPhase or ''
|
||||||
|
runHook preInstall
|
||||||
|
${lib.strings.concatLines (installCommands nimBin)}
|
||||||
|
runHook postInstall
|
||||||
|
'';
|
||||||
|
|
||||||
|
nativeBuildInputs = (prevAttrs.nativeBuildInputs or [ ]) ++ [ nim ];
|
||||||
|
|
||||||
|
nimCfg =
|
||||||
|
prevAttrs.nimCfg or (buildNimCfg {
|
||||||
|
backend = prevAttrs.nimBackend or properties."nim:backend" or "c";
|
||||||
|
inherit (sbom) components;
|
||||||
|
});
|
||||||
|
|
||||||
|
passthru = {
|
||||||
|
inherit sbom properties nimBin;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
applyOverrides =
|
||||||
|
prevAttrs:
|
||||||
|
builtins.foldl' (
|
||||||
|
prevAttrs:
|
||||||
|
{ name, ... }@component:
|
||||||
|
if (builtins.hasAttr name nimOverrides) then
|
||||||
|
prevAttrs // (nimOverrides.${name} component prevAttrs)
|
||||||
|
else
|
||||||
|
prevAttrs
|
||||||
|
) prevAttrs prevAttrs.passthru.sbom.components;
|
||||||
|
|
||||||
|
composition =
|
||||||
|
finalAttrs:
|
||||||
|
let
|
||||||
|
callerAttrs = if builtins.isAttrs callerArg then callerArg else callerArg finalAttrs;
|
||||||
|
sbomAttrs = callerAttrs // (applySbom callerAttrs);
|
||||||
|
overrideAttrs = sbomAttrs // (applyOverrides sbomAttrs);
|
||||||
|
in
|
||||||
|
overrideAttrs;
|
||||||
|
in
|
||||||
|
stdenv.mkDerivation composition
|
||||||
|
|
||||||
|
# TODO: Add an overrideSbom function into the result..
|
36
config.prs
36
config.prs
|
@ -1,6 +1,10 @@
|
||||||
version 1 .
|
version 1 .
|
||||||
embeddedType EntityRef.Cap .
|
embeddedType EntityRef.Cap .
|
||||||
|
|
||||||
|
Base64DecoderArguments = <base64-decoder {
|
||||||
|
dataspace: #:any
|
||||||
|
}>.
|
||||||
|
|
||||||
CacheArguments = <cache {
|
CacheArguments = <cache {
|
||||||
dataspace: #:any
|
dataspace: #:any
|
||||||
lifetime: float
|
lifetime: float
|
||||||
|
@ -15,36 +19,34 @@ JsonTranslatorArguments = <json-stdio-translator {
|
||||||
dataspace: #:any
|
dataspace: #:any
|
||||||
}>.
|
}>.
|
||||||
|
|
||||||
JsonTranslatorConnected = <connected @path string>.
|
TcpAddress = <tcp @host string @port int>.
|
||||||
|
UnixAddress = <unix @path string>.
|
||||||
|
|
||||||
JsonSocketTranslatorArguments = <json-socket-translator {
|
SocketAddress = TcpAddress / UnixAddress .
|
||||||
|
|
||||||
|
HttpClientArguments = <http-client {
|
||||||
dataspace: #:any
|
dataspace: #:any
|
||||||
socket: string
|
|
||||||
}>.
|
}>.
|
||||||
|
|
||||||
PostgreArguments = <postgre {
|
HttpDriverStep= <http-driver { }>.
|
||||||
|
|
||||||
|
JsonSocketTranslatorStep = <json-socket-translator {
|
||||||
|
socket: SocketAddress
|
||||||
|
}>.
|
||||||
|
|
||||||
|
PostgreStep = <postgre {
|
||||||
connection: [PostgreConnectionParameter ...]
|
connection: [PostgreConnectionParameter ...]
|
||||||
dataspace: #:any
|
|
||||||
}>.
|
}>.
|
||||||
PostgreConnectionParameter = [@key string @val string].
|
PostgreConnectionParameter = [@key string @val string].
|
||||||
|
|
||||||
|
PrinterStep = <printer {}> .
|
||||||
|
|
||||||
PulseArguments = <pulse {
|
PulseArguments = <pulse {
|
||||||
dataspace: #:any
|
dataspace: #:any
|
||||||
}>.
|
}>.
|
||||||
|
|
||||||
SqliteArguments = <sqlite {
|
SqliteStep = <sqlite {
|
||||||
database: string
|
database: string
|
||||||
dataspace: #:any
|
|
||||||
}>.
|
|
||||||
|
|
||||||
WebhooksArguments = <webhooks {
|
|
||||||
endpoints: {[string ...]: #:any ...:...}
|
|
||||||
listen: Tcp
|
|
||||||
}>.
|
|
||||||
|
|
||||||
WebsocketArguments = <websocket {
|
|
||||||
dataspace: #:any
|
|
||||||
url: string
|
|
||||||
}>.
|
}>.
|
||||||
|
|
||||||
XmlTranslatorArguments = <xml-translator {
|
XmlTranslatorArguments = <xml-translator {
|
||||||
|
|
27
default.nix
27
default.nix
|
@ -1,10 +1,17 @@
|
||||||
{ pkgs ? import <nixpkgs> { } }:
|
{
|
||||||
|
pkgs ? import <nixpkgs> { },
|
||||||
pkgs.buildNimPackage {
|
}:
|
||||||
name = "syndicate_utils";
|
with pkgs;
|
||||||
propagatedNativeBuildInputs = [ pkgs.pkg-config ];
|
let
|
||||||
propagatedBuildInputs =
|
buildNimSbom = pkgs.callPackage ./build-nim-sbom.nix { };
|
||||||
[ pkgs.postgresql pkgs.sqlite pkgs.libxml2 pkgs.libxslt ];
|
in
|
||||||
lockFile = ./lock.json;
|
buildNimSbom (finalAttrs: {
|
||||||
src = pkgs.lib.sources.cleanSource ./.;
|
src = if lib.inNixShell then null else lib.cleanSource ./.;
|
||||||
}
|
buildInputs = [
|
||||||
|
postgresql.out
|
||||||
|
sqlite
|
||||||
|
libxml2
|
||||||
|
libxslt
|
||||||
|
openssl
|
||||||
|
];
|
||||||
|
}) ./sbom.json
|
||||||
|
|
120
lock.json
120
lock.json
|
@ -1,12 +1,28 @@
|
||||||
{
|
{
|
||||||
"depends": [
|
"depends": [
|
||||||
|
{
|
||||||
|
"date": "2024-05-23T17:44:14+03:00",
|
||||||
|
"deepClone": false,
|
||||||
|
"fetchLFS": false,
|
||||||
|
"fetchSubmodules": true,
|
||||||
|
"hash": "sha256-qTRhHsOPNov1BQcm3P7NEkEPW6uh80XFfQRBdMp4o0Q=",
|
||||||
|
"leaveDotGit": false,
|
||||||
|
"method": "git",
|
||||||
|
"packages": [
|
||||||
|
"syndicate"
|
||||||
|
],
|
||||||
|
"path": "/nix/store/1lcxrap5n80hy1z4bcmsmdx83n4b9wjf-syndicate-nim",
|
||||||
|
"rev": "7ab4611824b676157523f2618e7893d5ac99e4f2",
|
||||||
|
"sha256": "0i53g3578h84gp2lbwx1mddhyh8jrpzdq9h70psqndlgqcg62d59",
|
||||||
|
"srcDir": "src",
|
||||||
|
"url": "https://git.syndicate-lang.org/ehmry/syndicate-nim.git"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"method": "fetchzip",
|
"method": "fetchzip",
|
||||||
"packages": [
|
"packages": [
|
||||||
"bigints"
|
"bigints"
|
||||||
],
|
],
|
||||||
"path": "/nix/store/jvrm392g8adfsgf36prgwkbyd7vh5jsw-source",
|
"path": "/nix/store/jvrm392g8adfsgf36prgwkbyd7vh5jsw-source",
|
||||||
"ref": "20231006",
|
|
||||||
"rev": "86ea14d31eea9275e1408ca34e6bfe9c99989a96",
|
"rev": "86ea14d31eea9275e1408ca34e6bfe9c99989a96",
|
||||||
"sha256": "15pcpmnk1bnw3k8769rjzcpg00nahyrypwbxs88jnwr4aczp99j4",
|
"sha256": "15pcpmnk1bnw3k8769rjzcpg00nahyrypwbxs88jnwr4aczp99j4",
|
||||||
"srcDir": "src",
|
"srcDir": "src",
|
||||||
|
@ -15,85 +31,107 @@
|
||||||
{
|
{
|
||||||
"method": "fetchzip",
|
"method": "fetchzip",
|
||||||
"packages": [
|
"packages": [
|
||||||
"hashlib"
|
"cps"
|
||||||
],
|
],
|
||||||
"path": "/nix/store/fav82xdbicvlk34nmcbl89zx99lr3mbs-source",
|
"path": "/nix/store/8gbhwni0akqskdb3qhn5nfgv6gkdz0vz-source",
|
||||||
"rev": "f9455d4be988e14e3dc7933eb7cc7d7c4820b7ac",
|
"rev": "c90530ac57f98a842b7be969115c6ef08bdcc564",
|
||||||
"sha256": "1sx6j952lj98629qfgr7ds5aipyw9d6lldcnnqs205wpj4pkcjb3",
|
"sha256": "0h8ghs2fqg68j3jdcg7grnxssmllmgg99kym2w0a3vlwca1zvr62",
|
||||||
"srcDir": "",
|
"srcDir": "",
|
||||||
"url": "https://github.com/ehmry/hashlib/archive/f9455d4be988e14e3dc7933eb7cc7d7c4820b7ac.tar.gz"
|
"url": "https://github.com/ehmry/cps/archive/c90530ac57f98a842b7be969115c6ef08bdcc564.tar.gz"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"method": "fetchzip",
|
"method": "fetchzip",
|
||||||
"packages": [
|
"packages": [
|
||||||
"illwill"
|
"getdns"
|
||||||
],
|
],
|
||||||
"path": "/nix/store/3lmm3z36qn4gz7bfa209zv0pqrpm3di9-source",
|
"path": "/nix/store/x9xmn7w4k6jg8nv5bnx148ibhnsfh362-source",
|
||||||
"ref": "v0.3.2",
|
"rev": "c73cbe288d9f9480586b8fa87f6d794ffb6a6ce6",
|
||||||
"rev": "1d12cb36ab7b76c31d2d25fa421013ecb382e625",
|
"sha256": "1sbgx2x51szr22i72n7c8jglnfmr8m7y7ga0v85d58fwadiv7g6b",
|
||||||
"sha256": "0f9yncl5gbdja18mrqf5ixrdgrh95k0khda923dm1jd1x1b7ar8z",
|
"srcDir": "src",
|
||||||
"srcDir": "",
|
"url": "https://git.sr.ht/~ehmry/getdns-nim/archive/c73cbe288d9f9480586b8fa87f6d794ffb6a6ce6.tar.gz"
|
||||||
"url": "https://github.com/johnnovak/illwill/archive/1d12cb36ab7b76c31d2d25fa421013ecb382e625.tar.gz"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"method": "fetchzip",
|
"method": "fetchzip",
|
||||||
"packages": [
|
"packages": [
|
||||||
"nimcrypto"
|
"nimcrypto"
|
||||||
],
|
],
|
||||||
"path": "/nix/store/zyr8zwh7vaiycn1s4r8cxwc71f2k5l0h-source",
|
"path": "/nix/store/fkrcpp8lzj2yi21na79xm63xk0ggnqsp-source",
|
||||||
"ref": "traditional-api",
|
"rev": "485f7b3cfa83c1beecc0e31be0e964d697aa74d7",
|
||||||
"rev": "602c5d20c69c76137201b5d41f788f72afb95aa8",
|
"sha256": "1h3dzdbc9kacwpi10mj73yjglvn7kbizj1x8qc9099ax091cj5xn",
|
||||||
"sha256": "1dmdmgb6b9m5f8dyxk781nnd61dsk3hdxqks7idk9ncnpj9fng65",
|
|
||||||
"srcDir": "",
|
"srcDir": "",
|
||||||
"url": "https://github.com/cheatfate/nimcrypto/archive/602c5d20c69c76137201b5d41f788f72afb95aa8.tar.gz"
|
"url": "https://github.com/cheatfate/nimcrypto/archive/485f7b3cfa83c1beecc0e31be0e964d697aa74d7.tar.gz"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"method": "fetchzip",
|
"method": "fetchzip",
|
||||||
"packages": [
|
"packages": [
|
||||||
"npeg"
|
"npeg"
|
||||||
],
|
],
|
||||||
"path": "/nix/store/ffkxmjmigfs7zhhiiqm0iw2c34smyciy-source",
|
"path": "/nix/store/xpn694ibgipj8xak3j4bky6b3k0vp7hh-source",
|
||||||
"ref": "1.2.1",
|
"rev": "ec0cc6e64ea4c62d2aa382b176a4838474238f8d",
|
||||||
"rev": "26d62fdc40feb84c6533956dc11d5ee9ea9b6c09",
|
"sha256": "1fi9ls3xl20bmv1ikillxywl96i9al6zmmxrbffx448gbrxs86kg",
|
||||||
"sha256": "0xpzifjkfp49w76qmaylan8q181bs45anmp46l4bwr3lkrr7bpwh",
|
|
||||||
"srcDir": "src",
|
"srcDir": "src",
|
||||||
"url": "https://github.com/zevv/npeg/archive/26d62fdc40feb84c6533956dc11d5ee9ea9b6c09.tar.gz"
|
"url": "https://github.com/zevv/npeg/archive/ec0cc6e64ea4c62d2aa382b176a4838474238f8d.tar.gz"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"method": "fetchzip",
|
"method": "fetchzip",
|
||||||
"packages": [
|
"packages": [
|
||||||
"preserves"
|
"preserves"
|
||||||
],
|
],
|
||||||
"path": "/nix/store/6nnn5di5vip1vladlb7z56rbw18d1y7j-source",
|
"path": "/nix/store/9zl4s2did00725n8ygbp37agvkskdhcx-source",
|
||||||
"ref": "20240208",
|
"rev": "1fee87590940761e288cf9ab3c7270832403b719",
|
||||||
"rev": "2825bceecf33a15b9b7942db5331a32cbc39b281",
|
"sha256": "1ny42rwr3yx52zwvkdg4lh54nxaxrmxdj9dlw3qarvvp2grfq4j2",
|
||||||
"sha256": "145vf46fy3wc52j6vs509fm9bi5lx7c53gskbkpcfbkv82l86dgk",
|
|
||||||
"srcDir": "src",
|
"srcDir": "src",
|
||||||
"url": "https://git.syndicate-lang.org/ehmry/preserves-nim/archive/2825bceecf33a15b9b7942db5331a32cbc39b281.tar.gz"
|
"url": "https://git.syndicate-lang.org/ehmry/preserves-nim/archive/1fee87590940761e288cf9ab3c7270832403b719.tar.gz"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"method": "fetchzip",
|
"method": "fetchzip",
|
||||||
"packages": [
|
"packages": [
|
||||||
"syndicate"
|
"stew"
|
||||||
],
|
],
|
||||||
"path": "/nix/store/y9f3j4m7vmhf8gbpkvqa77jvzrc5ynlm-source",
|
"path": "/nix/store/mqg8qzsbcc8xqabq2yzvlhvcyqypk72c-source",
|
||||||
"ref": "20240208",
|
"rev": "3c91b8694e15137a81ec7db37c6c58194ec94a6a",
|
||||||
"rev": "50a77995bcfe15e6062f54c6af0f55fba850c329",
|
"sha256": "17lfhfxp5nxvld78xa83p258y80ks5jb4n53152cdr57xk86y07w",
|
||||||
"sha256": "1avrk86c34qg39w8vlixsksli2gwgbsf29jhlap27ffzdbj2zbal",
|
"srcDir": "",
|
||||||
"srcDir": "src",
|
"url": "https://github.com/status-im/nim-stew/archive/3c91b8694e15137a81ec7db37c6c58194ec94a6a.tar.gz"
|
||||||
"url": "https://git.syndicate-lang.org/ehmry/syndicate-nim/archive/50a77995bcfe15e6062f54c6af0f55fba850c329.tar.gz"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"method": "fetchzip",
|
"method": "fetchzip",
|
||||||
"packages": [
|
"packages": [
|
||||||
"ws"
|
"sys"
|
||||||
],
|
],
|
||||||
"path": "/nix/store/zd51j4dphs6h1hyhdbzdv840c8813ai8-source",
|
"path": "/nix/store/syhxsjlsdqfap0hk4qp3s6kayk8cqknd-source",
|
||||||
"ref": "0.5.0",
|
"rev": "4ef3b624db86e331ba334e705c1aa235d55b05e1",
|
||||||
"rev": "9536bf99ddf5948db221ccb7bb3663aa238a8e21",
|
"sha256": "1q4qgw4an4mmmcbx48l6xk1jig1vc8p9cq9dbx39kpnb0890j32q",
|
||||||
"sha256": "0j8z9jlvzb1h60v7rryvh2wx6vg99lra6i62whf3fknc53l641fz",
|
|
||||||
"srcDir": "src",
|
"srcDir": "src",
|
||||||
"url": "https://github.com/treeform/ws/archive/9536bf99ddf5948db221ccb7bb3663aa238a8e21.tar.gz"
|
"url": "https://github.com/ehmry/nim-sys/archive/4ef3b624db86e331ba334e705c1aa235d55b05e1.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"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"date": "2024-05-22T06:09:38+02:00",
|
||||||
|
"deepClone": false,
|
||||||
|
"fetchLFS": false,
|
||||||
|
"fetchSubmodules": true,
|
||||||
|
"hash": "sha256-B3fMwgBpO2Ty8143k9V1cnHXa5K8i1+zN+eF/rBLMe0=",
|
||||||
|
"leaveDotGit": false,
|
||||||
|
"method": "git",
|
||||||
|
"packages": [
|
||||||
|
"solo5_dispatcher"
|
||||||
|
],
|
||||||
|
"path": "/nix/store/xqj48v4rqlffl1l94hi02szazj5gla8g-solo5_dispatcher",
|
||||||
|
"rev": "cc64ef99416b22b12e4a076d33de9e25a163e57d",
|
||||||
|
"sha256": "1v9i9fqgx1g76yrmz2xwj9mxfwbjfpar6dsyygr68fv9031cqxq7",
|
||||||
|
"srcDir": "pkg",
|
||||||
|
"url": "https://git.sr.ht/~ehmry/solo5_dispatcher"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,653 @@
|
||||||
|
{
|
||||||
|
"bomFormat": "CycloneDX",
|
||||||
|
"specVersion": "1.6",
|
||||||
|
"metadata": {
|
||||||
|
"component": {
|
||||||
|
"type": "application",
|
||||||
|
"bom-ref": "pkg:nim/syndicate_utils",
|
||||||
|
"name": "syndicate_utils",
|
||||||
|
"description": "Utilites for Syndicated Actors and Synit",
|
||||||
|
"version": "20240610",
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Emery Hemingway"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"licenses": [
|
||||||
|
{
|
||||||
|
"license": {
|
||||||
|
"id": "Unlicense"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"name": "nim:skipExt",
|
||||||
|
"value": "nim"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nim:bin:postgre-actor",
|
||||||
|
"value": "postgre_actor"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nim:bin:xslt-actor",
|
||||||
|
"value": "xslt_actor"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nim:bin:preserve-process-environment",
|
||||||
|
"value": "preserve_process_environment"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nim:bin:mintsturdyref",
|
||||||
|
"value": "mintsturdyref"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nim:bin:esc-printer-driver",
|
||||||
|
"value": "esc_printer_driver"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nim:bin:msg",
|
||||||
|
"value": "msg"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nim:bin:rofi-script-actor",
|
||||||
|
"value": "rofi_script_actor"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nim:bin:syndesizer",
|
||||||
|
"value": "syndesizer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nim:bin:http-client",
|
||||||
|
"value": "http_client"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nim:bin:mount-actor",
|
||||||
|
"value": "mount_actor"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nim:bin:syndump",
|
||||||
|
"value": "syndump"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nim:bin:sqlite-actor",
|
||||||
|
"value": "sqlite_actor"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nim:srcDir",
|
||||||
|
"value": "src"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nim:backend",
|
||||||
|
"value": "c"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"components": [
|
||||||
|
{
|
||||||
|
"type": "library",
|
||||||
|
"bom-ref": "pkg:nim/syndicate",
|
||||||
|
"name": "syndicate",
|
||||||
|
"version": "20240610",
|
||||||
|
"externalReferences": [
|
||||||
|
{
|
||||||
|
"url": "https://git.syndicate-lang.org/ehmry/syndicate-nim/archive/7bbcdb7e7705c2ab54ba0165565813d67aea48b0.tar.gz",
|
||||||
|
"type": "source-distribution"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://git.syndicate-lang.org/ehmry/syndicate-nim.git",
|
||||||
|
"type": "vcs"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"name": "nix:fod:method",
|
||||||
|
"value": "fetchzip"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nix:fod:path",
|
||||||
|
"value": "/nix/store/mldff990wpr0v9v5qh6ggqjmc2mn3n8g-source"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nix:fod:rev",
|
||||||
|
"value": "7bbcdb7e7705c2ab54ba0165565813d67aea48b0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nix:fod:sha256",
|
||||||
|
"value": "0mb3mrj5dkkqm0xp5hg84c5naaci4mi6mv2jjznfi6i7swp3f7vs"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nix:fod:url",
|
||||||
|
"value": "https://git.syndicate-lang.org/ehmry/syndicate-nim/archive/7bbcdb7e7705c2ab54ba0165565813d67aea48b0.tar.gz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nix:fod:ref",
|
||||||
|
"value": "20240610"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nix:fod:srcDir",
|
||||||
|
"value": "src"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "library",
|
||||||
|
"bom-ref": "pkg:nim/preserves",
|
||||||
|
"name": "preserves",
|
||||||
|
"version": "20240610",
|
||||||
|
"externalReferences": [
|
||||||
|
{
|
||||||
|
"url": "https://git.syndicate-lang.org/ehmry/preserves-nim/archive/560a6417a30a2dff63f24b62498e9fcac2de8354.tar.gz",
|
||||||
|
"type": "source-distribution"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://git.syndicate-lang.org/ehmry/preserves-nim.git",
|
||||||
|
"type": "vcs"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"name": "nix:fod:method",
|
||||||
|
"value": "fetchzip"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nix:fod:path",
|
||||||
|
"value": "/nix/store/0sszsmz84ppwqsgda8cmli4lfh2mjmin-source"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nix:fod:rev",
|
||||||
|
"value": "560a6417a30a2dff63f24b62498e9fcac2de8354"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nix:fod:sha256",
|
||||||
|
"value": "19r983fy7m54mlaj0adxdp8pxi1x8dp6phkcnr8rz5y5cwndfjx2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nix:fod:url",
|
||||||
|
"value": "https://git.syndicate-lang.org/ehmry/preserves-nim/archive/560a6417a30a2dff63f24b62498e9fcac2de8354.tar.gz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nix:fod:ref",
|
||||||
|
"value": "20240610"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nix:fod:srcDir",
|
||||||
|
"value": "src"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nix:fod:date",
|
||||||
|
"value": "2024-05-23T15:58:40+03:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nix:fod:hash",
|
||||||
|
"value": "sha256-JvdvLdPajDgIPbLblO0LbOm0wEp530fs8LYmgH885sk="
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "library",
|
||||||
|
"bom-ref": "pkg:nim/sys",
|
||||||
|
"name": "sys",
|
||||||
|
"version": "4ef3b624db86e331ba334e705c1aa235d55b05e1",
|
||||||
|
"externalReferences": [
|
||||||
|
{
|
||||||
|
"url": "https://github.com/ehmry/nim-sys/archive/4ef3b624db86e331ba334e705c1aa235d55b05e1.tar.gz",
|
||||||
|
"type": "source-distribution"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://github.com/ehmry/nim-sys.git",
|
||||||
|
"type": "vcs"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"name": "nix:fod:method",
|
||||||
|
"value": "fetchzip"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nix:fod:path",
|
||||||
|
"value": "/nix/store/syhxsjlsdqfap0hk4qp3s6kayk8cqknd-source"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nix:fod:rev",
|
||||||
|
"value": "4ef3b624db86e331ba334e705c1aa235d55b05e1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nix:fod:sha256",
|
||||||
|
"value": "1q4qgw4an4mmmcbx48l6xk1jig1vc8p9cq9dbx39kpnb0890j32q"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nix:fod:url",
|
||||||
|
"value": "https://github.com/ehmry/nim-sys/archive/4ef3b624db86e331ba334e705c1aa235d55b05e1.tar.gz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nix:fod:srcDir",
|
||||||
|
"value": "src"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "library",
|
||||||
|
"bom-ref": "pkg:nim/taps",
|
||||||
|
"name": "taps",
|
||||||
|
"version": "20240405",
|
||||||
|
"externalReferences": [
|
||||||
|
{
|
||||||
|
"url": "https://git.sr.ht/~ehmry/nim_taps/archive/8c8572cd971d1283e6621006b310993c632da247.tar.gz",
|
||||||
|
"type": "source-distribution"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://git.sr.ht/~ehmry/nim_taps",
|
||||||
|
"type": "vcs"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"name": "nix:fod:method",
|
||||||
|
"value": "fetchzip"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nix:fod:path",
|
||||||
|
"value": "/nix/store/6y14ia52kr7jyaa0izx37mlablmq9s65-source"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nix:fod:rev",
|
||||||
|
"value": "8c8572cd971d1283e6621006b310993c632da247"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nix:fod:sha256",
|
||||||
|
"value": "1dp166bv9x773jmfqppg5i3v3rilgff013vb11yzwcid9l7s3iy8"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nix:fod:url",
|
||||||
|
"value": "https://git.sr.ht/~ehmry/nim_taps/archive/8c8572cd971d1283e6621006b310993c632da247.tar.gz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nix:fod:ref",
|
||||||
|
"value": "20240405"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nix:fod:srcDir",
|
||||||
|
"value": "src"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "library",
|
||||||
|
"bom-ref": "pkg:nim/nimcrypto",
|
||||||
|
"name": "nimcrypto",
|
||||||
|
"version": "traditional-api",
|
||||||
|
"externalReferences": [
|
||||||
|
{
|
||||||
|
"url": "https://github.com/cheatfate/nimcrypto/archive/602c5d20c69c76137201b5d41f788f72afb95aa8.tar.gz",
|
||||||
|
"type": "source-distribution"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://github.com/cheatfate/nimcrypto",
|
||||||
|
"type": "vcs"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"name": "nix:fod:method",
|
||||||
|
"value": "fetchzip"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nix:fod:path",
|
||||||
|
"value": "/nix/store/zyr8zwh7vaiycn1s4r8cxwc71f2k5l0h-source"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nix:fod:rev",
|
||||||
|
"value": "602c5d20c69c76137201b5d41f788f72afb95aa8"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nix:fod:sha256",
|
||||||
|
"value": "1dmdmgb6b9m5f8dyxk781nnd61dsk3hdxqks7idk9ncnpj9fng65"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nix:fod:url",
|
||||||
|
"value": "https://github.com/cheatfate/nimcrypto/archive/602c5d20c69c76137201b5d41f788f72afb95aa8.tar.gz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nix:fod:ref",
|
||||||
|
"value": "traditional-api"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "library",
|
||||||
|
"bom-ref": "pkg:nim/npeg",
|
||||||
|
"name": "npeg",
|
||||||
|
"version": "1.2.2",
|
||||||
|
"externalReferences": [
|
||||||
|
{
|
||||||
|
"url": "https://github.com/zevv/npeg/archive/ec0cc6e64ea4c62d2aa382b176a4838474238f8d.tar.gz",
|
||||||
|
"type": "source-distribution"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://github.com/zevv/npeg.git",
|
||||||
|
"type": "vcs"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"name": "nix:fod:method",
|
||||||
|
"value": "fetchzip"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nix:fod:path",
|
||||||
|
"value": "/nix/store/xpn694ibgipj8xak3j4bky6b3k0vp7hh-source"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nix:fod:rev",
|
||||||
|
"value": "ec0cc6e64ea4c62d2aa382b176a4838474238f8d"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nix:fod:sha256",
|
||||||
|
"value": "1fi9ls3xl20bmv1ikillxywl96i9al6zmmxrbffx448gbrxs86kg"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nix:fod:url",
|
||||||
|
"value": "https://github.com/zevv/npeg/archive/ec0cc6e64ea4c62d2aa382b176a4838474238f8d.tar.gz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nix:fod:ref",
|
||||||
|
"value": "1.2.2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nix:fod:srcDir",
|
||||||
|
"value": "src"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "library",
|
||||||
|
"bom-ref": "pkg:nim/bigints",
|
||||||
|
"name": "bigints",
|
||||||
|
"version": "20231006",
|
||||||
|
"externalReferences": [
|
||||||
|
{
|
||||||
|
"url": "https://github.com/ehmry/nim-bigints/archive/86ea14d31eea9275e1408ca34e6bfe9c99989a96.tar.gz",
|
||||||
|
"type": "source-distribution"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://github.com/ehmry/nim-bigints.git",
|
||||||
|
"type": "vcs"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"name": "nix:fod:method",
|
||||||
|
"value": "fetchzip"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nix:fod:path",
|
||||||
|
"value": "/nix/store/jvrm392g8adfsgf36prgwkbyd7vh5jsw-source"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nix:fod:rev",
|
||||||
|
"value": "86ea14d31eea9275e1408ca34e6bfe9c99989a96"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nix:fod:sha256",
|
||||||
|
"value": "15pcpmnk1bnw3k8769rjzcpg00nahyrypwbxs88jnwr4aczp99j4"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nix:fod:url",
|
||||||
|
"value": "https://github.com/ehmry/nim-bigints/archive/86ea14d31eea9275e1408ca34e6bfe9c99989a96.tar.gz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nix:fod:ref",
|
||||||
|
"value": "20231006"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nix:fod:srcDir",
|
||||||
|
"value": "src"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "library",
|
||||||
|
"bom-ref": "pkg:nim/cps",
|
||||||
|
"name": "cps",
|
||||||
|
"version": "0.10.4",
|
||||||
|
"externalReferences": [
|
||||||
|
{
|
||||||
|
"url": "https://github.com/nim-works/cps/archive/2a4d771a715ba45cfba3a82fa625ae7ad6591c8b.tar.gz",
|
||||||
|
"type": "source-distribution"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://github.com/nim-works/cps",
|
||||||
|
"type": "vcs"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"name": "nix:fod:method",
|
||||||
|
"value": "fetchzip"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nix:fod:path",
|
||||||
|
"value": "/nix/store/m9vpcf3dq6z2h1xpi1vlw0ycxp91s5p7-source"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nix:fod:rev",
|
||||||
|
"value": "2a4d771a715ba45cfba3a82fa625ae7ad6591c8b"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nix:fod:sha256",
|
||||||
|
"value": "0c62k5wpq9z9mn8cd4rm8jjc4z0xmnak4piyj5dsfbyj6sbdw2bf"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nix:fod:url",
|
||||||
|
"value": "https://github.com/nim-works/cps/archive/2a4d771a715ba45cfba3a82fa625ae7ad6591c8b.tar.gz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nix:fod:ref",
|
||||||
|
"value": "0.10.4"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "library",
|
||||||
|
"bom-ref": "pkg:nim/stew",
|
||||||
|
"name": "stew",
|
||||||
|
"version": "3c91b8694e15137a81ec7db37c6c58194ec94a6a",
|
||||||
|
"externalReferences": [
|
||||||
|
{
|
||||||
|
"url": "https://github.com/status-im/nim-stew/archive/3c91b8694e15137a81ec7db37c6c58194ec94a6a.tar.gz",
|
||||||
|
"type": "source-distribution"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://github.com/status-im/nim-stew",
|
||||||
|
"type": "vcs"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"name": "nix:fod:method",
|
||||||
|
"value": "fetchzip"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nix:fod:path",
|
||||||
|
"value": "/nix/store/mqg8qzsbcc8xqabq2yzvlhvcyqypk72c-source"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nix:fod:rev",
|
||||||
|
"value": "3c91b8694e15137a81ec7db37c6c58194ec94a6a"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nix:fod:sha256",
|
||||||
|
"value": "17lfhfxp5nxvld78xa83p258y80ks5jb4n53152cdr57xk86y07w"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nix:fod:url",
|
||||||
|
"value": "https://github.com/status-im/nim-stew/archive/3c91b8694e15137a81ec7db37c6c58194ec94a6a.tar.gz"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "library",
|
||||||
|
"bom-ref": "pkg:nim/getdns",
|
||||||
|
"name": "getdns",
|
||||||
|
"version": "20230806",
|
||||||
|
"externalReferences": [
|
||||||
|
{
|
||||||
|
"url": "https://git.sr.ht/~ehmry/getdns-nim/archive/e4ae0992ed7c5540e6d498f3074d06c8f454a0b6.tar.gz",
|
||||||
|
"type": "source-distribution"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://git.sr.ht/~ehmry/getdns-nim",
|
||||||
|
"type": "vcs"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"name": "nix:fod:method",
|
||||||
|
"value": "fetchzip"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nix:fod:path",
|
||||||
|
"value": "/nix/store/j8i20k9aarzppg4p234449140nnnaycq-source"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nix:fod:rev",
|
||||||
|
"value": "e4ae0992ed7c5540e6d498f3074d06c8f454a0b6"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nix:fod:sha256",
|
||||||
|
"value": "1dp53gndr6d9s9601dd5ipkiq94j53hlx46mxv8gpr8nd98bqysg"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nix:fod:url",
|
||||||
|
"value": "https://git.sr.ht/~ehmry/getdns-nim/archive/e4ae0992ed7c5540e6d498f3074d06c8f454a0b6.tar.gz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nix:fod:ref",
|
||||||
|
"value": "20230806"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nix:fod:srcDir",
|
||||||
|
"value": "src"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "library",
|
||||||
|
"bom-ref": "pkg:nim/solo5_dispatcher",
|
||||||
|
"name": "solo5_dispatcher",
|
||||||
|
"version": "20240522",
|
||||||
|
"externalReferences": [
|
||||||
|
{
|
||||||
|
"url": "https://git.sr.ht/~ehmry/solo5_dispatcher/archive/cc64ef99416b22b12e4a076d33de9e25a163e57d.tar.gz",
|
||||||
|
"type": "source-distribution"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://git.sr.ht/~ehmry/solo5_dispatcher",
|
||||||
|
"type": "vcs"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"name": "nix:fod:method",
|
||||||
|
"value": "fetchzip"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nix:fod:path",
|
||||||
|
"value": "/nix/store/4jj467pg4hs6warhksb8nsxn9ykz8c7c-source"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nix:fod:rev",
|
||||||
|
"value": "cc64ef99416b22b12e4a076d33de9e25a163e57d"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nix:fod:sha256",
|
||||||
|
"value": "1v9i9fqgx1g76yrmz2xwj9mxfwbjfpar6dsyygr68fv9031cqxq7"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nix:fod:url",
|
||||||
|
"value": "https://git.sr.ht/~ehmry/solo5_dispatcher/archive/cc64ef99416b22b12e4a076d33de9e25a163e57d.tar.gz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nix:fod:ref",
|
||||||
|
"value": "20240522"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nix:fod:srcDir",
|
||||||
|
"value": "pkg"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"dependencies": [
|
||||||
|
{
|
||||||
|
"ref": "pkg:nim/syndicate_utils",
|
||||||
|
"dependsOn": [
|
||||||
|
"pkg:nim/syndicate"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ref": "pkg:nim/syndicate",
|
||||||
|
"dependsOn": [
|
||||||
|
"pkg:nim/nimcrypto",
|
||||||
|
"pkg:nim/preserves",
|
||||||
|
"pkg:nim/sys",
|
||||||
|
"pkg:nim/taps"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ref": "pkg:nim/preserves",
|
||||||
|
"dependsOn": [
|
||||||
|
"pkg:nim/npeg",
|
||||||
|
"pkg:nim/bigints"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ref": "pkg:nim/sys",
|
||||||
|
"dependsOn": [
|
||||||
|
"pkg:nim/cps",
|
||||||
|
"pkg:nim/stew"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ref": "pkg:nim/taps",
|
||||||
|
"dependsOn": [
|
||||||
|
"pkg:nim/getdns",
|
||||||
|
"pkg:nim/sys",
|
||||||
|
"pkg:nim/cps",
|
||||||
|
"pkg:nim/solo5_dispatcher"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ref": "pkg:nim/nimcrypto",
|
||||||
|
"dependsOn": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ref": "pkg:nim/npeg",
|
||||||
|
"dependsOn": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ref": "pkg:nim/bigints",
|
||||||
|
"dependsOn": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ref": "pkg:nim/cps",
|
||||||
|
"dependsOn": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ref": "pkg:nim/stew",
|
||||||
|
"dependsOn": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ref": "pkg:nim/getdns",
|
||||||
|
"dependsOn": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ref": "pkg:nim/solo5_dispatcher",
|
||||||
|
"dependsOn": [
|
||||||
|
"pkg:nim/cps"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
5
sql.prs
5
sql.prs
|
@ -2,4 +2,7 @@ version 1 .
|
||||||
|
|
||||||
# When asserted the actor reponds to @target rows as records
|
# When asserted the actor reponds to @target rows as records
|
||||||
# of the given label and row columns as record fields.
|
# 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>.
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
include_rules
|
|
||||||
: foreach *.nim | $(SYNDICATE_PROTOCOL) ./<schema> ./syndesizer/<checks> |> !nim_bin |> {bin}
|
|
||||||
: foreach {bin} |> !assert_built |>
|
|
||||||
: $(BIN_DIR)/msg |> cp %f %o |> $(BIN_DIR)/beep
|
|
|
@ -0,0 +1,111 @@
|
||||||
|
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
||||||
|
# SPDX-License-Identifier: Unlicense
|
||||||
|
|
||||||
|
## ESC/P printer control actor.
|
||||||
|
|
||||||
|
import
|
||||||
|
std/[cmdline, oserrors, posix, sequtils, sets],
|
||||||
|
pkg/sys/[files, ioqueue],
|
||||||
|
preserves, preserves/sugar,
|
||||||
|
syndicate, syndicate/relays,
|
||||||
|
syndicate/protocols/[gatekeeper, sturdy],
|
||||||
|
./private/esc_p
|
||||||
|
|
||||||
|
from pkg/sys/handles import FD
|
||||||
|
|
||||||
|
proc echo(args: varargs[string, `$`]) {.used.} =
|
||||||
|
stderr.writeLine(args)
|
||||||
|
|
||||||
|
type
|
||||||
|
HandleSet = HashSet[Handle]
|
||||||
|
|
||||||
|
Printer = ref object of Entity
|
||||||
|
device: AsyncFile
|
||||||
|
boldHandles, italicHandles, superscriptHandles, subscriptHandles: HandleSet
|
||||||
|
buffer: seq[byte]
|
||||||
|
isBusy: bool
|
||||||
|
|
||||||
|
proc flush(printer: Printer) {.asyncio.} =
|
||||||
|
printer.isBusy = true
|
||||||
|
while printer.buffer.len > 0:
|
||||||
|
let n = printer.device.write(printer.buffer)
|
||||||
|
if n > 0:
|
||||||
|
printer.buffer.delete(0..<n)
|
||||||
|
elif n < 0:
|
||||||
|
osLastError().osErrorMsg().quit()
|
||||||
|
printer.isBusy = false
|
||||||
|
|
||||||
|
proc write(printer: Printer; s: string) {.inline.} =
|
||||||
|
printer.buffer.add cast[seq[byte]](s)
|
||||||
|
if not printer.isBusy:
|
||||||
|
discard trampoline:
|
||||||
|
whelp printer.flush()
|
||||||
|
|
||||||
|
proc writeLine(printer: Printer; s: string) {.inline.} =
|
||||||
|
printer.write(s)
|
||||||
|
printer.write("\r\n")
|
||||||
|
|
||||||
|
method message(printer: Printer; t: Turn; a: AssertionRef) =
|
||||||
|
if a.value.isString:
|
||||||
|
printer.write(a.value.string)
|
||||||
|
# TODO: unicode?
|
||||||
|
# TODO: line breaks?
|
||||||
|
|
||||||
|
proc assert(printer: Printer; handles: var HandleSet; ctrl: string; h: Handle) =
|
||||||
|
if handles.len == 0: printer.write(ctrl)
|
||||||
|
handles.incl h
|
||||||
|
|
||||||
|
proc retract(printer: Printer; handles: var HandleSet; ctrl: string; h: Handle) =
|
||||||
|
handles.excl h
|
||||||
|
if handles.len == 0: printer.write(ctrl)
|
||||||
|
|
||||||
|
method publish(printer: Printer; t: Turn; a: AssertionRef; h: Handle) =
|
||||||
|
if a.value.isRecord("bold"):
|
||||||
|
printer.assert(printer.boldHandles, SelectBoldFont, h)
|
||||||
|
|
||||||
|
elif a.value.isRecord("italic"):
|
||||||
|
printer.assert(printer.italicHandles, SelectItalicFont, h)
|
||||||
|
|
||||||
|
elif a.value.isRecord("superscript"):
|
||||||
|
printer.assert(printer.superscriptHandles, SelectSuperScript, h)
|
||||||
|
|
||||||
|
elif a.value.isRecord("subscript"):
|
||||||
|
printer.assert(printer.subscriptHandles, SelectSubScript, h)
|
||||||
|
|
||||||
|
method retract(printer: Printer; t: Turn; h: Handle) =
|
||||||
|
if printer.boldHandles.contains h:
|
||||||
|
printer.retract(printer.boldHandles, CancelBoldFont, h)
|
||||||
|
|
||||||
|
elif printer.italicHandles.contains h:
|
||||||
|
printer.retract(printer.italicHandles, CanceItalicFont, h)
|
||||||
|
|
||||||
|
elif printer.superscriptHandles.contains h:
|
||||||
|
printer.retract(printer.superscriptHandles, CancelAltScript, h)
|
||||||
|
|
||||||
|
elif printer.subscriptHandles.contains h:
|
||||||
|
printer.retract(printer.subscriptHandles, CancelAltScript, h)
|
||||||
|
|
||||||
|
|
||||||
|
proc devicePath: string =
|
||||||
|
if paramCount() < 1:
|
||||||
|
quit "missing path to printer device file"
|
||||||
|
if paramCount() > 1:
|
||||||
|
quit "too many command line parameters"
|
||||||
|
paramStr(1)
|
||||||
|
|
||||||
|
proc openPrinter(turn: Turn): Printer =
|
||||||
|
new result
|
||||||
|
result.facet = turn.facet
|
||||||
|
let fd = posix.open(devicePath(), O_WRONLY or O_NONBLOCK, 0)
|
||||||
|
if fd < 0: osLastError().osErrorMsg().quit()
|
||||||
|
result.device = newAsyncFile(FD fd)
|
||||||
|
result.write(InitializePrinter)
|
||||||
|
|
||||||
|
runActor(devicePath()) do (turn: Turn):
|
||||||
|
let printer = openPrinter(turn)
|
||||||
|
resolveEnvironment(turn) do (turn: Turn; relay: Cap):
|
||||||
|
let pat = Resolve?:{0: matchRecord("printer"), 1: grab()}
|
||||||
|
during(turn, relay, pat) do (cont: Cap):
|
||||||
|
# Publish for any <printer> step.
|
||||||
|
discard publish(turn, cont, ResolvedAccepted(
|
||||||
|
responderSession: turn.newCap(printer)))
|
|
@ -0,0 +1,113 @@
|
||||||
|
# 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 toContent(body: Value; contentType: var string): string =
|
||||||
|
case contentType
|
||||||
|
of "application/json":
|
||||||
|
var stream = newStringStream()
|
||||||
|
writeText(stream, body, textJson)
|
||||||
|
return stream.data.move
|
||||||
|
of "application/preserves":
|
||||||
|
return cast[string](body.encode)
|
||||||
|
of "text/preserves":
|
||||||
|
return $body
|
||||||
|
else:
|
||||||
|
discard
|
||||||
|
|
||||||
|
case body.kind
|
||||||
|
of pkString:
|
||||||
|
result = body.string
|
||||||
|
if contentType == "":
|
||||||
|
contentType = "text/plain"
|
||||||
|
of pkByteString:
|
||||||
|
result = cast[string](body.bytes)
|
||||||
|
if contentType == "":
|
||||||
|
contentType = "application/octet-stream"
|
||||||
|
else:
|
||||||
|
raise newException(ValueError, "unknown content type")
|
||||||
|
|
||||||
|
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.body.toContent(contentType), headers
|
||||||
|
)
|
||||||
|
client.headers["content-type"] = contentType
|
||||||
|
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
|
|
@ -1,114 +0,0 @@
|
||||||
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
|
||||||
# SPDX-License-Identifier: Unlicense
|
|
||||||
|
|
||||||
## An actor for filesystem monitoring.
|
|
||||||
|
|
||||||
import std/[asyncdispatch, asyncfile, tables]
|
|
||||||
import posix, posix/inotify
|
|
||||||
import preserves
|
|
||||||
import syndicate, syndicate/[bags, relays]
|
|
||||||
import ./schema/inotify_actor
|
|
||||||
|
|
||||||
var IN_NONBLOCK {.importc, nodecl.}: cint
|
|
||||||
|
|
||||||
type
|
|
||||||
BootArgs {.preservesDictionary.} = object
|
|
||||||
dataspace: Cap
|
|
||||||
|
|
||||||
proc toMask(sym: Symbol): uint32 =
|
|
||||||
case sym.string
|
|
||||||
of "IN_ACCESS": IN_ACCESS
|
|
||||||
of "IN_MODIFY": IN_MODIFY
|
|
||||||
of "IN_ATTRIB": IN_ATTRIB
|
|
||||||
of "IN_CLOSE_WRITE": IN_CLOSE_WRITE
|
|
||||||
of "IN_CLOSE_NOWRITE": IN_CLOSE_NOWRITE
|
|
||||||
of "IN_CLOSE": IN_CLOSE
|
|
||||||
of "IN_OPEN": IN_OPEN
|
|
||||||
of "IN_MOVED_FROM": IN_MOVED_FROM
|
|
||||||
of "IN_MOVED_TO": IN_MOVED_TO
|
|
||||||
of "IN_MOVE": IN_MOVE
|
|
||||||
of "IN_CREATE": IN_CREATE
|
|
||||||
of "IN_DELETE": IN_DELETE
|
|
||||||
of "IN_DELETE_SELF": IN_DELETE_SELF
|
|
||||||
of "IN_MOVE_SELF": IN_MOVE_SELF
|
|
||||||
else: 0
|
|
||||||
|
|
||||||
func contains(event, bit: uint32): bool = (event and bit) != 0
|
|
||||||
|
|
||||||
iterator symbols(event: uint32): Symbol =
|
|
||||||
if event.contains IN_ACCESS:
|
|
||||||
yield Symbol"IN_ACCESS"
|
|
||||||
if event.contains IN_MODIFY:
|
|
||||||
yield Symbol"IN_MODIFY"
|
|
||||||
if event.contains IN_ATTRIB:
|
|
||||||
yield Symbol"IN_ATTRIB"
|
|
||||||
if event.contains IN_CLOSE_WRITE:
|
|
||||||
yield Symbol"IN_CLOSE_WRITE"
|
|
||||||
if event.contains IN_CLOSE_NOWRITE:
|
|
||||||
yield Symbol"IN_CLOSE_NOWRITE"
|
|
||||||
if event.contains IN_OPEN:
|
|
||||||
yield Symbol"IN_OPEN"
|
|
||||||
if event.contains IN_MOVED_FROM:
|
|
||||||
yield Symbol"IN_MOVED_FROM"
|
|
||||||
if event.contains IN_MOVED_TO:
|
|
||||||
yield Symbol"IN_MOVED_TO"
|
|
||||||
if event.contains IN_CREATE:
|
|
||||||
yield Symbol"IN_CREATE"
|
|
||||||
if event.contains IN_DELETE:
|
|
||||||
yield Symbol"IN_DELETE"
|
|
||||||
if event.contains IN_DELETE_SELF:
|
|
||||||
yield Symbol"IN_DELETE_SELF"
|
|
||||||
if event.contains IN_MOVE_SELF:
|
|
||||||
yield Symbol"IN_MOVE_SELF"
|
|
||||||
if event.contains (IN_CLOSE_WRITE or IN_CLOSE_NOWRITE):
|
|
||||||
yield Symbol"IN_CLOSE"
|
|
||||||
if event.contains (IN_MOVED_FROM or IN_MOVED_TO):
|
|
||||||
yield Symbol"IN_MOVE"
|
|
||||||
|
|
||||||
runActor("inotify_actor") do (root: Cap; turn: var Turn):
|
|
||||||
let buf = newSeq[byte](8192)
|
|
||||||
let eventPattern = ?Observe(pattern: !InotifyMessage) ?? { 0: grabLit(), 1: grabLit() }
|
|
||||||
connectStdio(turn, root)
|
|
||||||
during(turn, root, ?:BootArgs) do (ds: Cap):
|
|
||||||
let inf = inotify_init1(IN_NONBLOCK)
|
|
||||||
doAssert inf != -1, $inf & " - " & $strerror(errno)
|
|
||||||
var
|
|
||||||
registry = initTable[cint, string]()
|
|
||||||
watchBag: Bag[cint]
|
|
||||||
let
|
|
||||||
anf = newAsyncFile(AsyncFD inf)
|
|
||||||
facet = turn.facet
|
|
||||||
var fut: Future[int]
|
|
||||||
proc readEvents() {.gcsafe.} =
|
|
||||||
fut = readBuffer(anf, buf[0].addr, buf.len)
|
|
||||||
addCallback(fut, facet) do (turn: var Turn):
|
|
||||||
let n = read(fut)
|
|
||||||
doAssert n > 0
|
|
||||||
for event in inotify_events(buf[0].addr, n):
|
|
||||||
var msg = InotifyMessage(path: registry[event.wd], cookie: event.cookie.BiggestInt)
|
|
||||||
if event.len > 0:
|
|
||||||
let n = event.len
|
|
||||||
msg.name.setLen(n)
|
|
||||||
copyMem(msg.name[0].addr, event.name.addr, n)
|
|
||||||
for i, c in msg.name:
|
|
||||||
if c == '\0':
|
|
||||||
msg.name.setLen(i)
|
|
||||||
break
|
|
||||||
for sym in event.mask.symbols:
|
|
||||||
msg.event = sym
|
|
||||||
message(turn, ds, msg)
|
|
||||||
readEvents()
|
|
||||||
readEvents()
|
|
||||||
|
|
||||||
during(turn, ds, eventPattern) do (path: string, kind: Symbol):
|
|
||||||
let wd = inotify_add_watch(inf, path, kind.toMask or IN_MASK_ADD)
|
|
||||||
doAssert wd > 0, $strerror(errno)
|
|
||||||
registry[wd] = path
|
|
||||||
discard watchBag.change(wd, 1)
|
|
||||||
|
|
||||||
do:
|
|
||||||
if watchBag.change(wd, -1, clamp = true) == cdPresentToAbsent:
|
|
||||||
discard close(wd)
|
|
||||||
registry.del(wd)
|
|
||||||
do:
|
|
||||||
close(anf)
|
|
|
@ -7,8 +7,8 @@ when not defined(linux):
|
||||||
{.error: "this component only tested for Linux".}
|
{.error: "this component only tested for Linux".}
|
||||||
|
|
||||||
import std/oserrors
|
import std/oserrors
|
||||||
import preserves
|
import preserves, preserves/sugar
|
||||||
import syndicate, syndicate/relays
|
import syndicate
|
||||||
import ./schema/mountpoints
|
import ./schema/mountpoints
|
||||||
|
|
||||||
type BootArgs {.preservesDictionary.} = object
|
type BootArgs {.preservesDictionary.} = object
|
||||||
|
@ -20,12 +20,15 @@ proc mount(source, target, fsType: cstring; flags: culong; data: pointer): cint
|
||||||
proc umount(target: cstring): cint {.importc, header: "<sys/mount.h>".}
|
proc umount(target: cstring): cint {.importc, header: "<sys/mount.h>".}
|
||||||
## `umount(2)`
|
## `umount(2)`
|
||||||
|
|
||||||
runActor("mount_actor") do (turn: var Turn; root: Cap):
|
proc spawnMountActor*(turn: Turn; ds: Cap): Actor {.discardable.} =
|
||||||
|
spawnActor(turn, "mount_actor") do (turn: Turn):
|
||||||
let
|
let
|
||||||
targetPat = ?Observe(pattern: !Mountpoint) ?? { 1: grabLit() }
|
targetPat = observePattern(!Mountpoint, { @[%1]: grabLit() })
|
||||||
sourcePat = ?Observe(pattern: !Mountpoint) ?? { 0: grabLit(), 2: grabLit() }
|
sourcePat = observePattern(!Mountpoint, {
|
||||||
connectStdio(turn, root)
|
@[%0]: grabLit(),
|
||||||
during(turn, root, ?:BootArgs) do (ds: Cap):
|
@[%2]: grabLit(),
|
||||||
|
})
|
||||||
|
during(turn, ds, ?:BootArgs) do (ds: Cap):
|
||||||
during(turn, ds, targetPat) do (target: string):
|
during(turn, ds, targetPat) do (target: string):
|
||||||
during(turn, ds, sourcePat) do (source: string, fsType: string):
|
during(turn, ds, sourcePat) do (source: string, fsType: string):
|
||||||
var mountpoint = Mountpoint(
|
var mountpoint = Mountpoint(
|
||||||
|
@ -42,3 +45,9 @@ runActor("mount_actor") do (turn: var Turn; root: Cap):
|
||||||
discard publish(turn, ds, mountpoint)
|
discard publish(turn, ds, mountpoint)
|
||||||
do:
|
do:
|
||||||
discard umount(target)
|
discard umount(target)
|
||||||
|
|
||||||
|
when isMainModule:
|
||||||
|
import syndicate/relays
|
||||||
|
runActor("main") do (turn: Turn):
|
||||||
|
resolveEnvironment(turn) do (turn: Turn; ds: Cap):
|
||||||
|
discard spawnMountActor(turn, ds)
|
||||||
|
|
22
src/msg.nim
22
src/msg.nim
|
@ -1,20 +1,20 @@
|
||||||
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
||||||
# SPDX-License-Identifier: Unlicense
|
# SPDX-License-Identifier: Unlicense
|
||||||
|
|
||||||
import std/[asyncdispatch, sequtils, os]
|
import std/[sequtils, os, strutils]
|
||||||
import preserves, syndicate, syndicate/relays
|
import preserves, syndicate, syndicate/relays
|
||||||
|
|
||||||
proc main =
|
runActor("msg") do (turn: Turn):
|
||||||
let
|
let
|
||||||
route = envRoute()
|
|
||||||
data = map(commandLineParams(), parsePreserves)
|
data = map(commandLineParams(), parsePreserves)
|
||||||
|
cmd = paramStr(0).extractFilename.normalize
|
||||||
discard bootDataspace("msg") do (turn: var Turn; root: Cap):
|
resolveEnvironment(turn) do (turn: Turn; ds: Cap):
|
||||||
spawnRelays(turn, root)
|
case cmd
|
||||||
resolve(turn, root, route) do (turn: var Turn; ds: Cap):
|
of "assert":
|
||||||
|
for e in data:
|
||||||
|
publish(turn, ds, e)
|
||||||
|
else: # "msg"
|
||||||
for e in data:
|
for e in data:
|
||||||
message(turn, ds, e)
|
message(turn, ds, e)
|
||||||
|
sync(turn, ds) do (turn: Turn):
|
||||||
for _ in 1..2: poll()
|
stopActor(turn)
|
||||||
|
|
||||||
main()
|
|
||||||
|
|
|
@ -1,167 +0,0 @@
|
||||||
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
|
||||||
# SPDX-License-Identifier: Unlicense
|
|
||||||
|
|
||||||
## A ping utility for Syndicate.
|
|
||||||
|
|
||||||
import std/[asyncdispatch, asyncnet, monotimes, nativesockets, net, os, strutils, tables, times]
|
|
||||||
import preserves
|
|
||||||
import syndicate, syndicate/relays
|
|
||||||
|
|
||||||
import ./schema/net_mapper
|
|
||||||
|
|
||||||
#[
|
|
||||||
var
|
|
||||||
SOL_IP {.importc, nodecl, header: "<sys/socket.h>".}: int
|
|
||||||
IP_TTL {.importc, nodecl, header: "<netinet/in.h>".}: int
|
|
||||||
]#
|
|
||||||
|
|
||||||
proc toPreservesHook(address: IpAddress): Value = toPreserves($address)
|
|
||||||
|
|
||||||
proc fromPreservesHook(address: var IpAddress; pr: Value): bool =
|
|
||||||
try:
|
|
||||||
if pr.isString:
|
|
||||||
address = parseIpAddress(pr.string)
|
|
||||||
result = true
|
|
||||||
except ValueError: discard
|
|
||||||
|
|
||||||
when isMainModule:
|
|
||||||
# verify that the hook catches
|
|
||||||
var ip: IpAddress
|
|
||||||
assert fromPreservesHook(ip, toPreservesHook(ip))
|
|
||||||
|
|
||||||
type
|
|
||||||
IcmpHeader {.packed.} = object
|
|
||||||
`type`: uint8
|
|
||||||
code: uint8
|
|
||||||
checksum: uint16
|
|
||||||
|
|
||||||
IcmpEchoFields {.packed.} = object
|
|
||||||
header: IcmpHeader
|
|
||||||
identifier: array[2, byte]
|
|
||||||
sequenceNumber: uint16
|
|
||||||
|
|
||||||
IcmpEcho {.union.} = object
|
|
||||||
fields: IcmpEchoFields
|
|
||||||
buffer: array[8, uint8]
|
|
||||||
|
|
||||||
IcmpTypes = enum
|
|
||||||
icmpEchoReply = 0,
|
|
||||||
icmpEcho = 8,
|
|
||||||
|
|
||||||
proc initIcmpEcho(): IcmpEcho =
|
|
||||||
result.fields.header.`type` = uint8 icmpEcho
|
|
||||||
# doAssert urandom(result.fields.identifier) # Linux does this?
|
|
||||||
|
|
||||||
proc updateChecksum(msg: var IcmpEcho) =
|
|
||||||
var sum: uint32
|
|
||||||
msg.fields.header.checksum = 0
|
|
||||||
for n in cast[array[4, uint16]](msg.buffer): sum = sum + uint32(n)
|
|
||||||
while (sum and 0xffff0000'u32) != 0:
|
|
||||||
sum = (sum and 0xffff) + (sum shr 16)
|
|
||||||
msg.fields.header.checksum = not uint16(sum)
|
|
||||||
|
|
||||||
proc match(a, b: IcmpEchoFields): bool =
|
|
||||||
({a.header.type, b.header.type} == {uint8 icmpEcho, uint8 icmpEchoReply}) and
|
|
||||||
(a.header.code == b.header.code) and
|
|
||||||
(a.sequenceNumber == b.sequenceNumber)
|
|
||||||
|
|
||||||
type
|
|
||||||
Pinger = ref object
|
|
||||||
facet: Facet
|
|
||||||
ds: Cap
|
|
||||||
rtt: RoundTripTime
|
|
||||||
rttHandle: Handle
|
|
||||||
sum: Duration
|
|
||||||
count: int64
|
|
||||||
msg: IcmpEcho
|
|
||||||
socket: AsyncSocket
|
|
||||||
sad: Sockaddr_storage
|
|
||||||
sadLen: SockLen
|
|
||||||
interval: Duration
|
|
||||||
|
|
||||||
proc newPinger(address: IpAddress; facet: Facet; ds: Cap): Pinger =
|
|
||||||
result = Pinger(
|
|
||||||
facet: facet,
|
|
||||||
ds: ds,
|
|
||||||
rtt: RoundTripTime(address: $address),
|
|
||||||
msg: initIcmpEcho(),
|
|
||||||
socket: newAsyncSocket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP, false, true),
|
|
||||||
interval: initDuration(milliseconds = 500))
|
|
||||||
toSockAddr(address, Port 0, result.sad, result.sadLen)
|
|
||||||
# setSockOptInt(getFd socket, SOL_IP, IP_TTL, _)
|
|
||||||
|
|
||||||
proc close(ping: Pinger) = close(ping.socket)
|
|
||||||
|
|
||||||
proc sqr(dur: Duration): Duration =
|
|
||||||
let us = dur.inMicroseconds
|
|
||||||
initDuration(microseconds = us * us)
|
|
||||||
|
|
||||||
proc update(ping: Pinger; dur: Duration) {.inline.} =
|
|
||||||
let secs = dur.inMicroseconds.float / 1_000_000.0
|
|
||||||
if ping.count == 0: (ping.rtt.minimum, ping.rtt.maximum) = (secs, secs)
|
|
||||||
elif secs < ping.rtt.minimum: ping.rtt.minimum = secs
|
|
||||||
elif secs > ping.rtt.maximum: ping.rtt.maximum = secs
|
|
||||||
ping.sum = ping.sum + dur
|
|
||||||
inc ping.count
|
|
||||||
ping.rtt.average = inMicroseconds(ping.sum div ping.count).float / 1_000_000.0
|
|
||||||
|
|
||||||
proc exchangeEcho(ping: Pinger) {.async.} =
|
|
||||||
inc ping.msg.fields.sequenceNumber
|
|
||||||
# updateChecksum(ping.msg) # Linux does this?
|
|
||||||
let
|
|
||||||
a = getMonoTime()
|
|
||||||
r = sendto(ping.socket.getFd,
|
|
||||||
unsafeAddr ping.msg.buffer[0], ping.msg.buffer.len, 0,
|
|
||||||
cast[ptr SockAddr](unsafeAddr ping.sad), # neckbeard loser API
|
|
||||||
ping.sadLen)
|
|
||||||
if r == -1'i32:
|
|
||||||
let osError = osLastError()
|
|
||||||
raiseOSError(osError)
|
|
||||||
while true:
|
|
||||||
var
|
|
||||||
(data, address, _) = await recvFrom(ping.socket, 128)
|
|
||||||
b = getMonoTime()
|
|
||||||
if address != $ping.rtt.address:
|
|
||||||
stderr.writeLine "want ICMP from ", ping.rtt.address, " but received from ", address, " instead"
|
|
||||||
elif data.len >= ping.msg.buffer.len:
|
|
||||||
let
|
|
||||||
period = b - a
|
|
||||||
resp = cast[ptr IcmpEcho](unsafeAddr data[0])
|
|
||||||
if match(ping.msg.fields, resp.fields):
|
|
||||||
update(ping, period)
|
|
||||||
return
|
|
||||||
else:
|
|
||||||
stderr.writeLine "ICMP mismatch"
|
|
||||||
else:
|
|
||||||
stderr.writeLine "reply data has a bad length ", data.len
|
|
||||||
|
|
||||||
proc kick(ping: Pinger) {.gcsafe.} =
|
|
||||||
if not ping.socket.isClosed:
|
|
||||||
addTimer(ping.interval.inMilliseconds.int, oneshot = true) do (fd: AsyncFD) -> bool:
|
|
||||||
let fut = exchangeEcho(ping)
|
|
||||||
fut.addCallback do ():
|
|
||||||
if fut.failed and ping.rttHandle != Handle(0):
|
|
||||||
ping.facet.run do (turn: var Turn):
|
|
||||||
retract(turn, ping.rttHandle)
|
|
||||||
reset ping.rttHandle
|
|
||||||
else:
|
|
||||||
ping.facet.run do (turn: var Turn):
|
|
||||||
replace(turn, ping.ds, ping.rttHandle, ping.rtt)
|
|
||||||
if ping.interval < initDuration(seconds = 20):
|
|
||||||
ping.interval = ping.interval * 2
|
|
||||||
kick(ping)
|
|
||||||
|
|
||||||
type Args {.preservesDictionary.} = object
|
|
||||||
dataspace: Cap
|
|
||||||
|
|
||||||
runActor("net_mapper") do (root: Cap; turn: var Turn):
|
|
||||||
connectStdio(turn, root)
|
|
||||||
let rttObserver = ?Observe(pattern: !RoundTripTime) ?? {0: grabLit()}
|
|
||||||
during(turn, root, ?:Args) do (ds: Cap):
|
|
||||||
during(turn, ds, rttObserver) do (address: IpAddress):
|
|
||||||
var ping: Pinger
|
|
||||||
if address.family == IpAddressFamily.IPv4:
|
|
||||||
ping = newPinger(address, turn.facet, ds)
|
|
||||||
kick(ping)
|
|
||||||
do:
|
|
||||||
if not ping.isNil: close(ping)
|
|
|
@ -1,9 +1,10 @@
|
||||||
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
||||||
# SPDX-License-Identifier: Unlicense
|
# SPDX-License-Identifier: Unlicense
|
||||||
|
|
||||||
|
import
|
||||||
import preserves, syndicate, syndicate/relays
|
pkg/preserves,
|
||||||
import ../schema/[config, sql]
|
pkg/syndicate, pkg/syndicate/protocols/[gatekeeper, sturdy],
|
||||||
|
./schema/[config, sql]
|
||||||
|
|
||||||
{.passL: "-lpq".}
|
{.passL: "-lpq".}
|
||||||
|
|
||||||
|
@ -88,26 +89,51 @@ proc splitParams(params: StringPairs): (cstringArray, cstringArray) =
|
||||||
for i, _ in params: strings[i] = params[i][1]
|
for i, _ in params: strings[i] = params[i][1]
|
||||||
result[1] = allocCStringArray(strings)
|
result[1] = allocCStringArray(strings)
|
||||||
|
|
||||||
proc spawnPostgreActor*(turn: var Turn; root: Cap): Actor {.discardable.} =
|
proc renderSql(tokens: openarray[Value]): string =
|
||||||
spawn("postgre", turn) do (turn: var Turn):
|
for token in tokens:
|
||||||
during(turn, root, ?:PostgreArguments) do (params: StringPairs, ds: Cap):
|
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; relay: Cap): Actor {.discardable.} =
|
||||||
|
result = spawnActor(turn, "postgre") do (turn: Turn):
|
||||||
|
let pat = Resolve?:{ 0: PostgreStep.grabTypeFlat, 1: grab() }
|
||||||
|
during(turn, relay, pat) do (params: StringPairs, observer: Cap):
|
||||||
|
linkActor(turn, "postgre-conn") do (turn: Turn):
|
||||||
var
|
var
|
||||||
conn: PGconn
|
|
||||||
statusHandle: Handle
|
|
||||||
(keys, vals) = splitParams(params)
|
(keys, vals) = splitParams(params)
|
||||||
conn = PQconnectdbParams(keys, vals, 0)
|
conn = PQconnectdbParams(keys, vals, 0)
|
||||||
checkPointer(conn)
|
checkPointer(conn)
|
||||||
let
|
let
|
||||||
status = PQstatus(conn)
|
status = PQstatus(conn)
|
||||||
msg = $PQerrorMessage(conn)
|
msg = $PQerrorMessage(conn)
|
||||||
statusHandle = publish(turn, ds,
|
deallocCStringArray(keys)
|
||||||
initRecord("status", toSymbol($status), msg.toPreserves))
|
deallocCStringArray(vals)
|
||||||
|
onStop(turn) do (turn: Turn):
|
||||||
|
PQfinish(conn)
|
||||||
if status == CONNECTION_OK:
|
if status == CONNECTION_OK:
|
||||||
during(turn, ds, ?:Query) do (statement: string, target: Cap):
|
let ds = turn.newDataspace()
|
||||||
var res = PQexec(conn, statement)
|
discard publish(turn, ds, initRecord("status", toSymbol($status), msg.toPreserves))
|
||||||
var st = PQresultStatus(res)
|
during(turn, ds, ?:Query) do (statement: seq[Value], target: Cap):
|
||||||
discard publish(turn, ds, toRecord(
|
var text = renderSql statement
|
||||||
"error", statement, toSymbol($PQresStatus(st)), $PQresultErrorMessage(res)))
|
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:
|
if st == PGRES_TUPLES_OK or st == PGRES_SINGLE_TUPLE:
|
||||||
let tuples = PQntuples(res)
|
let tuples = PQntuples(res)
|
||||||
let fields = PQnfields(res)
|
let fields = PQnfields(res)
|
||||||
|
@ -117,15 +143,21 @@ proc spawnPostgreActor*(turn: var Turn; root: Cap): Actor {.discardable.} =
|
||||||
for f in 0..<fields:
|
for f in 0..<fields:
|
||||||
tupl[f] = toPreserves($PQgetvalue(res, r, f))
|
tupl[f] = toPreserves($PQgetvalue(res, r, f))
|
||||||
discard publish(turn, target, tupl)
|
discard publish(turn, target, tupl)
|
||||||
PQclear(res)
|
|
||||||
else:
|
else:
|
||||||
stderr.writeLine "refusing to do anything when status is ", status
|
discard publish(turn, ds, SqlError(
|
||||||
do:
|
msg: $PQresStatus(st),
|
||||||
deallocCStringArray(keys)
|
context: $PQresultErrorMessage(res),
|
||||||
deallocCStringArray(vals)
|
))
|
||||||
PQfinish(conn)
|
PQclear(res)
|
||||||
|
discard publish(turn, observer,
|
||||||
|
ResolvedAccepted(responderSession: ds))
|
||||||
|
else:
|
||||||
|
discard publish(turn, observer,
|
||||||
|
Rejected(detail: msg.toPreserves))
|
||||||
|
|
||||||
when isMainModule:
|
when isMainModule:
|
||||||
runActor("main") do (turn: var Turn; root: Cap):
|
import syndicate/relays
|
||||||
connectStdio(turn, root)
|
|
||||||
spawnPostgreActor(turn, root)
|
runActor("main") do (turn: Turn):
|
||||||
|
resolveEnvironment(turn) do (turn: Turn; relay: Cap):
|
||||||
|
spawnPostgreActor(turn, relay)
|
|
@ -0,0 +1,11 @@
|
||||||
|
const
|
||||||
|
ESC* = "\x1b"
|
||||||
|
InitializePrinter* = ESC & "@"
|
||||||
|
CancelLine* = ESC & "\x18"
|
||||||
|
SelectBoldFont* = ESC & "E"
|
||||||
|
CancelBoldFont* = ESC & "F"
|
||||||
|
SelectItalicFont* = ESC & "4"
|
||||||
|
CanceItalicFont* = ESC & "5"
|
||||||
|
SelectSuperScript* = ESC & "S0"
|
||||||
|
SelectSubScript* = ESC & "S1"
|
||||||
|
CancelAltScript* = ESC & "T"
|
|
@ -3,20 +3,15 @@
|
||||||
|
|
||||||
## See the rofi-script(5) manpage for documentation.
|
## See the rofi-script(5) manpage for documentation.
|
||||||
|
|
||||||
import std/[asyncdispatch, cmdline, envvars, strutils, tables]
|
import std/[cmdline, envvars, strutils, tables]
|
||||||
import preserves, syndicate, syndicate/relays
|
import preserves, syndicate, syndicate/relays
|
||||||
import ./schema/rofi
|
import ./schema/rofi
|
||||||
|
|
||||||
proc main =
|
if getEnv("ROFI_OUTSIDE") == "":
|
||||||
let
|
|
||||||
route = envRoute()
|
|
||||||
rofiPid = getEnv("ROFI_OUTSIDE")
|
|
||||||
if rofiPid == "":
|
|
||||||
quit("run this program in rofi")
|
quit("run this program in rofi")
|
||||||
|
|
||||||
runActor("rofi_script_actor") do (turn: var Turn; root: Cap):
|
runActor("rofi_script_actor") do (turn: Turn):
|
||||||
let rootFacet = turn.facet
|
resolveEnvironment(turn) do (turn: Turn; ds: Cap):
|
||||||
resolve(turn, root, route) do (turn: var Turn; ds: Cap):
|
|
||||||
case paramCount()
|
case paramCount()
|
||||||
of 0:
|
of 0:
|
||||||
let pat = ?:Options
|
let pat = ?:Options
|
||||||
|
@ -30,13 +25,7 @@ proc main =
|
||||||
if key.startsWith "ROFI_":
|
if key.startsWith "ROFI_":
|
||||||
select.environment[Symbol key] = val
|
select.environment[Symbol key] = val
|
||||||
message(turn, ds, select)
|
message(turn, ds, select)
|
||||||
# TODO: sync not implemented correctly
|
sync(turn, ds, stopActor)
|
||||||
# sync(turn, ds, stopActor)
|
|
||||||
callSoon do ():
|
|
||||||
waitFor sleepAsync(1)
|
|
||||||
quit()
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
quit("rofi passed an unexpected number of arguments")
|
quit("rofi passed an unexpected number of arguments")
|
||||||
|
|
||||||
main()
|
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
include_rules
|
include_rules
|
||||||
: foreach ../../*.prs |> !preserves_schema_nim |> %B.nim | ../<schema>
|
: foreach ../../*.prs |> !preserves-schema-nim |> %B.nim | $(PROJECT_DIR)/<schema>
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
|
||||||
|
import
|
||||||
|
preserves
|
||||||
|
|
||||||
|
type
|
||||||
|
Base64File* {.preservesRecord: "base64-file".} = object
|
||||||
|
`txt`*: string
|
||||||
|
`path`*: string
|
||||||
|
`size`*: BiggestInt
|
||||||
|
|
||||||
|
Base64Text* {.preservesRecord: "base64".} = object
|
||||||
|
`txt`*: string
|
||||||
|
`bin`*: seq[byte]
|
||||||
|
|
||||||
|
proc `$`*(x: Base64File | Base64Text): string =
|
||||||
|
`$`(toPreserves(x))
|
||||||
|
|
||||||
|
proc encode*(x: Base64File | Base64Text): seq[byte] =
|
||||||
|
encode(toPreserves(x))
|
|
@ -1,14 +1,13 @@
|
||||||
|
|
||||||
import
|
import
|
||||||
preserves, std/tables
|
preserves
|
||||||
|
|
||||||
type
|
type
|
||||||
WebsocketArgumentsField0* {.preservesDictionary.} = object
|
HttpClientArgumentsField0* {.preservesDictionary.} = object
|
||||||
`dataspace`* {.preservesEmbedded.}: EmbeddedRef
|
`dataspace`* {.preservesEmbedded.}: EmbeddedRef
|
||||||
`url`*: string
|
|
||||||
|
|
||||||
WebsocketArguments* {.preservesRecord: "websocket".} = object
|
HttpClientArguments* {.preservesRecord: "http-client".} = object
|
||||||
`field0`*: WebsocketArgumentsField0
|
`field0`*: HttpClientArgumentsField0
|
||||||
|
|
||||||
JsonTranslatorArgumentsField0* {.preservesDictionary.} = object
|
JsonTranslatorArgumentsField0* {.preservesDictionary.} = object
|
||||||
`argv`*: seq[string]
|
`argv`*: seq[string]
|
||||||
|
@ -17,15 +16,28 @@ type
|
||||||
JsonTranslatorArguments* {.preservesRecord: "json-stdio-translator".} = object
|
JsonTranslatorArguments* {.preservesRecord: "json-stdio-translator".} = object
|
||||||
`field0`*: JsonTranslatorArgumentsField0
|
`field0`*: JsonTranslatorArgumentsField0
|
||||||
|
|
||||||
JsonTranslatorConnected* {.preservesRecord: "connected".} = object
|
SocketAddressKind* {.pure.} = enum
|
||||||
`path`*: string
|
`TcpAddress`, `UnixAddress`
|
||||||
|
`SocketAddress`* {.preservesOr.} = object
|
||||||
|
case orKind*: SocketAddressKind
|
||||||
|
of SocketAddressKind.`TcpAddress`:
|
||||||
|
`tcpaddress`*: TcpAddress
|
||||||
|
|
||||||
JsonSocketTranslatorArgumentsField0* {.preservesDictionary.} = object
|
of SocketAddressKind.`UnixAddress`:
|
||||||
|
`unixaddress`*: UnixAddress
|
||||||
|
|
||||||
|
|
||||||
|
Base64DecoderArgumentsField0* {.preservesDictionary.} = object
|
||||||
`dataspace`* {.preservesEmbedded.}: EmbeddedRef
|
`dataspace`* {.preservesEmbedded.}: EmbeddedRef
|
||||||
`socket`*: string
|
|
||||||
|
|
||||||
JsonSocketTranslatorArguments* {.preservesRecord: "json-socket-translator".} = object
|
Base64DecoderArguments* {.preservesRecord: "base64-decoder".} = object
|
||||||
`field0`*: JsonSocketTranslatorArgumentsField0
|
`field0`*: Base64DecoderArgumentsField0
|
||||||
|
|
||||||
|
SqliteStepField0* {.preservesDictionary.} = object
|
||||||
|
`database`*: string
|
||||||
|
|
||||||
|
SqliteStep* {.preservesRecord: "sqlite".} = object
|
||||||
|
`field0`*: SqliteStepField0
|
||||||
|
|
||||||
XsltArgumentsField0* {.preservesDictionary.} = object
|
XsltArgumentsField0* {.preservesDictionary.} = object
|
||||||
`dataspace`* {.preservesEmbedded.}: EmbeddedRef
|
`dataspace`* {.preservesEmbedded.}: EmbeddedRef
|
||||||
|
@ -33,12 +45,11 @@ type
|
||||||
XsltArguments* {.preservesRecord: "xslt".} = object
|
XsltArguments* {.preservesRecord: "xslt".} = object
|
||||||
`field0`*: XsltArgumentsField0
|
`field0`*: XsltArgumentsField0
|
||||||
|
|
||||||
WebhooksArgumentsField0* {.preservesDictionary.} = object
|
JsonSocketTranslatorStepField0* {.preservesDictionary.} = object
|
||||||
`endpoints`*: Table[seq[string], EmbeddedRef]
|
`socket`*: SocketAddress
|
||||||
`listen`*: Tcp
|
|
||||||
|
|
||||||
WebhooksArguments* {.preservesRecord: "webhooks".} = object
|
JsonSocketTranslatorStep* {.preservesRecord: "json-socket-translator".} = object
|
||||||
`field0`*: WebhooksArgumentsField0
|
`field0`*: JsonSocketTranslatorStepField0
|
||||||
|
|
||||||
FileSystemUsageArgumentsField0* {.preservesDictionary.} = object
|
FileSystemUsageArgumentsField0* {.preservesDictionary.} = object
|
||||||
`dataspace`* {.preservesEmbedded.}: EmbeddedRef
|
`dataspace`* {.preservesEmbedded.}: EmbeddedRef
|
||||||
|
@ -46,12 +57,20 @@ type
|
||||||
FileSystemUsageArguments* {.preservesRecord: "file-system-usage".} = object
|
FileSystemUsageArguments* {.preservesRecord: "file-system-usage".} = object
|
||||||
`field0`*: FileSystemUsageArgumentsField0
|
`field0`*: FileSystemUsageArgumentsField0
|
||||||
|
|
||||||
SqliteArgumentsField0* {.preservesDictionary.} = object
|
HttpDriverStepField0* {.preservesDictionary.} = object
|
||||||
`database`*: string
|
|
||||||
`dataspace`* {.preservesEmbedded.}: EmbeddedRef
|
|
||||||
|
|
||||||
SqliteArguments* {.preservesRecord: "sqlite".} = object
|
HttpDriverStep* {.preservesRecord: "http-driver".} = object
|
||||||
`field0`*: SqliteArgumentsField0
|
`field0`*: HttpDriverStepField0
|
||||||
|
|
||||||
|
PostgreStepField0* {.preservesDictionary.} = object
|
||||||
|
`connection`*: seq[PostgreConnectionParameter]
|
||||||
|
|
||||||
|
PostgreStep* {.preservesRecord: "postgre".} = object
|
||||||
|
`field0`*: PostgreStepField0
|
||||||
|
|
||||||
|
TcpAddress* {.preservesRecord: "tcp".} = object
|
||||||
|
`host`*: string
|
||||||
|
`port`*: BiggestInt
|
||||||
|
|
||||||
CacheArgumentsField0* {.preservesDictionary.} = object
|
CacheArgumentsField0* {.preservesDictionary.} = object
|
||||||
`dataspace`* {.preservesEmbedded.}: EmbeddedRef
|
`dataspace`* {.preservesEmbedded.}: EmbeddedRef
|
||||||
|
@ -70,13 +89,6 @@ type
|
||||||
`key`*: string
|
`key`*: string
|
||||||
`val`*: string
|
`val`*: string
|
||||||
|
|
||||||
PostgreArgumentsField0* {.preservesDictionary.} = object
|
|
||||||
`connection`*: seq[PostgreConnectionParameter]
|
|
||||||
`dataspace`* {.preservesEmbedded.}: EmbeddedRef
|
|
||||||
|
|
||||||
PostgreArguments* {.preservesRecord: "postgre".} = object
|
|
||||||
`field0`*: PostgreArgumentsField0
|
|
||||||
|
|
||||||
PulseArgumentsField0* {.preservesDictionary.} = object
|
PulseArgumentsField0* {.preservesDictionary.} = object
|
||||||
`dataspace`* {.preservesEmbedded.}: EmbeddedRef
|
`dataspace`* {.preservesEmbedded.}: EmbeddedRef
|
||||||
|
|
||||||
|
@ -87,32 +99,46 @@ type
|
||||||
`host`*: string
|
`host`*: string
|
||||||
`port`*: BiggestInt
|
`port`*: BiggestInt
|
||||||
|
|
||||||
proc `$`*(x: WebsocketArguments | JsonTranslatorArguments |
|
UnixAddress* {.preservesRecord: "unix".} = object
|
||||||
JsonTranslatorConnected |
|
`path`*: string
|
||||||
JsonSocketTranslatorArguments |
|
|
||||||
|
PrinterStepField0* {.preservesDictionary.} = object
|
||||||
|
|
||||||
|
PrinterStep* {.preservesRecord: "printer".} = object
|
||||||
|
`field0`*: PrinterStepField0
|
||||||
|
|
||||||
|
proc `$`*(x: HttpClientArguments | JsonTranslatorArguments | SocketAddress |
|
||||||
|
Base64DecoderArguments |
|
||||||
|
SqliteStep |
|
||||||
XsltArguments |
|
XsltArguments |
|
||||||
WebhooksArguments |
|
JsonSocketTranslatorStep |
|
||||||
FileSystemUsageArguments |
|
FileSystemUsageArguments |
|
||||||
SqliteArguments |
|
HttpDriverStep |
|
||||||
|
PostgreStep |
|
||||||
|
TcpAddress |
|
||||||
CacheArguments |
|
CacheArguments |
|
||||||
XmlTranslatorArguments |
|
XmlTranslatorArguments |
|
||||||
PostgreConnectionParameter |
|
PostgreConnectionParameter |
|
||||||
PostgreArguments |
|
|
||||||
PulseArguments |
|
PulseArguments |
|
||||||
Tcp): string =
|
Tcp |
|
||||||
|
UnixAddress |
|
||||||
|
PrinterStep): string =
|
||||||
`$`(toPreserves(x))
|
`$`(toPreserves(x))
|
||||||
|
|
||||||
proc encode*(x: WebsocketArguments | JsonTranslatorArguments |
|
proc encode*(x: HttpClientArguments | JsonTranslatorArguments | SocketAddress |
|
||||||
JsonTranslatorConnected |
|
Base64DecoderArguments |
|
||||||
JsonSocketTranslatorArguments |
|
SqliteStep |
|
||||||
XsltArguments |
|
XsltArguments |
|
||||||
WebhooksArguments |
|
JsonSocketTranslatorStep |
|
||||||
FileSystemUsageArguments |
|
FileSystemUsageArguments |
|
||||||
SqliteArguments |
|
HttpDriverStep |
|
||||||
|
PostgreStep |
|
||||||
|
TcpAddress |
|
||||||
CacheArguments |
|
CacheArguments |
|
||||||
XmlTranslatorArguments |
|
XmlTranslatorArguments |
|
||||||
PostgreConnectionParameter |
|
PostgreConnectionParameter |
|
||||||
PostgreArguments |
|
|
||||||
PulseArguments |
|
PulseArguments |
|
||||||
Tcp): seq[byte] =
|
Tcp |
|
||||||
|
UnixAddress |
|
||||||
|
PrinterStep): seq[byte] =
|
||||||
encode(toPreserves(x))
|
encode(toPreserves(x))
|
||||||
|
|
|
@ -4,11 +4,15 @@ import
|
||||||
|
|
||||||
type
|
type
|
||||||
Query* {.preservesRecord: "query".} = object
|
Query* {.preservesRecord: "query".} = object
|
||||||
`statement`*: string
|
`statement`*: seq[Value]
|
||||||
`target`* {.preservesEmbedded.}: Value
|
`target`* {.preservesEmbedded.}: Value
|
||||||
|
|
||||||
proc `$`*(x: Query): string =
|
SqlError* {.preservesRecord: "sql-error".} = object
|
||||||
|
`msg`*: string
|
||||||
|
`context`*: string
|
||||||
|
|
||||||
|
proc `$`*(x: Query | SqlError): string =
|
||||||
`$`(toPreserves(x))
|
`$`(toPreserves(x))
|
||||||
|
|
||||||
proc encode*(x: Query): seq[byte] =
|
proc encode*(x: Query | SqlError): seq[byte] =
|
||||||
encode(toPreserves(x))
|
encode(toPreserves(x))
|
||||||
|
|
|
@ -0,0 +1,156 @@
|
||||||
|
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
||||||
|
# SPDX-License-Identifier: Unlicense
|
||||||
|
|
||||||
|
import
|
||||||
|
pkg/preserves,
|
||||||
|
pkg/syndicate, pkg/syndicate/protocols/[gatekeeper, sturdy],
|
||||||
|
./schema/[config, sql]
|
||||||
|
|
||||||
|
# Avoid Sqlite3 from the standard library because it is
|
||||||
|
# only held together by wishful thinking and dlload.
|
||||||
|
|
||||||
|
{.passC: staticExec("pkg-config --cflags sqlite3").}
|
||||||
|
{.passL: staticExec("pkg-config --libs sqlite3").}
|
||||||
|
|
||||||
|
{.pragma: sqlite3h, header: "sqlite3.h".}
|
||||||
|
|
||||||
|
var
|
||||||
|
SQLITE_VERSION_NUMBER {.importc, sqlite3h.}: cint
|
||||||
|
SQLITE_OK {.importc, sqlite3h.}: cint
|
||||||
|
SQLITE_ROW {.importc, sqlite3h.}: cint
|
||||||
|
SQLITE_DONE {.importc, sqlite3h.}: cint
|
||||||
|
SQLITE_OPEN_READONLY {.importc, sqlite3h.}: cint
|
||||||
|
|
||||||
|
const
|
||||||
|
SQLITE_INTEGER = 1
|
||||||
|
SQLITE_FLOAT = 2
|
||||||
|
SQLITE_TEXT = 3
|
||||||
|
SQLITE_BLOB = 4
|
||||||
|
# SQLITE_NULL = 5
|
||||||
|
|
||||||
|
type
|
||||||
|
Sqlite3 {.importc: "sqlite3", sqlite3h.} = distinct pointer
|
||||||
|
Stmt {.importc: "sqlite3_stmt", sqlite3h.} = distinct pointer
|
||||||
|
|
||||||
|
{.pragma: importSqlite3, importc: "sqlite3_$1", sqlite3h.}
|
||||||
|
|
||||||
|
proc libversion_number: cint {.importSqlite3.}
|
||||||
|
|
||||||
|
proc open_v2(filename: cstring; ppDb: ptr Sqlite3; flags: cint; zVfs: cstring): cint {.importSqlite3.}
|
||||||
|
proc close(ds: Sqlite3): int32 {.discardable, importSqlite3.}
|
||||||
|
|
||||||
|
proc errmsg(db: Sqlite3): cstring {.importSqlite3.}
|
||||||
|
|
||||||
|
proc prepare_v2(db: Sqlite3; zSql: cstring, nByte: cint; ppStmt: ptr Stmt; pzTail: ptr cstring): cint {.importSqlite3.}
|
||||||
|
|
||||||
|
proc step(para1: Stmt): cint {.importSqlite3.}
|
||||||
|
|
||||||
|
proc column_count(stmt: Stmt): int32 {.importSqlite3.}
|
||||||
|
proc column_blob(stmt: Stmt; col: cint): pointer {.importSqlite3.}
|
||||||
|
proc column_bytes(stmt: Stmt; col: cint): cint {.importSqlite3.}
|
||||||
|
proc column_double(stmt: Stmt; col: cint): float64 {.importSqlite3.}
|
||||||
|
proc column_int64(stmt: Stmt; col: cint): int64 {.importSqlite3.}
|
||||||
|
proc column_text(stmt: Stmt; col: cint): cstring {.importSqlite3.}
|
||||||
|
proc column_type(stmt: Stmt; col: cint): cint {.importSqlite3.}
|
||||||
|
proc finalize(stmt: Stmt): cint {.importSqlite3.}
|
||||||
|
|
||||||
|
doAssert libversion_number() == SQLITE_VERSION_NUMBER
|
||||||
|
|
||||||
|
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)
|
||||||
|
of SQLITE_INTEGER:
|
||||||
|
result = toPreserves(column_int64(stmt, col))
|
||||||
|
of SQLITE_FLOAT:
|
||||||
|
result = toPreserves(column_double(stmt, col))
|
||||||
|
of SQLITE_TEXT:
|
||||||
|
result = Value(kind: pkString, string: newString(column_bytes(stmt, col)))
|
||||||
|
if result.string.len > 0:
|
||||||
|
copyMem(addr result.string[0], column_text(stmt, col), result.string.len)
|
||||||
|
of SQLITE_BLOB:
|
||||||
|
result = Value(kind: pkByteString, bytes: newSeq[byte](column_bytes(stmt, col)))
|
||||||
|
if result.bytes.len > 0:
|
||||||
|
copyMem(addr result.bytes[0], column_blob(stmt, col), result.bytes.len)
|
||||||
|
else:
|
||||||
|
result = initRecord("null")
|
||||||
|
|
||||||
|
proc extractTuple(stmt: Stmt; arity: cint): Value =
|
||||||
|
result = initSequence(arity)
|
||||||
|
for col in 0..<arity: result[col] = extractValue(stmt, col)
|
||||||
|
|
||||||
|
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; relay: Cap): Actor {.discardable.} =
|
||||||
|
result = spawnActor(turn, "sqlite") do (turn: Turn):
|
||||||
|
let pat = Resolve?:{ 0: SqliteStep.grabTypeFlat, 1: grab() }
|
||||||
|
during(turn, relay, pat) do (path: string, observer: Cap):
|
||||||
|
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:
|
||||||
|
discard publish(turn, observer,
|
||||||
|
Rejected(detail: toPreserves($errmsg(db))))
|
||||||
|
else:
|
||||||
|
turn.onStop do (turn: Turn):
|
||||||
|
close(db)
|
||||||
|
stderr.writeLine("closed SQLite database ", path)
|
||||||
|
let ds = turn.newDataspace()
|
||||||
|
discard publish(turn, observer,
|
||||||
|
ResolvedAccepted(responderSession: ds))
|
||||||
|
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: Turn):
|
||||||
|
resolveEnvironment(turn) do (turn: Turn; relay: Cap):
|
||||||
|
spawnSqliteActor(turn, relay)
|
|
@ -3,42 +3,26 @@
|
||||||
|
|
||||||
## Syndicate multitool.
|
## Syndicate multitool.
|
||||||
|
|
||||||
import syndicate, syndicate/relays, syndicate/actors/timers
|
import syndicate, syndicate/relays, syndicate/drivers/timers
|
||||||
|
|
||||||
const
|
|
||||||
withPostgre* {.booldefine.}: bool = true
|
|
||||||
withSqlite* {.booldefine.}: bool = true
|
|
||||||
|
|
||||||
import ./syndesizer/[
|
import ./syndesizer/[
|
||||||
|
base64_decoder,
|
||||||
cache_actor,
|
cache_actor,
|
||||||
file_system_usage,
|
file_system_usage,
|
||||||
|
http_driver,
|
||||||
json_socket_translator,
|
json_socket_translator,
|
||||||
json_translator,
|
json_translator,
|
||||||
pulses,
|
pulses,
|
||||||
webhooks,
|
xml_translator]
|
||||||
websockets,
|
|
||||||
xml_translator,
|
|
||||||
xslt_actor]
|
|
||||||
|
|
||||||
when withPostgre:
|
runActor("syndesizer") do (turn: Turn):
|
||||||
import ./syndesizer/postgre_actor
|
resolveEnvironment(turn) do (turn: Turn; relay: Cap):
|
||||||
|
discard spawnTimerDriver(turn, relay)
|
||||||
when withSqlite:
|
discard spawnBase64Decoder(turn, relay)
|
||||||
import ./syndesizer/sqlite_actor
|
discard spawnCacheActor(turn, relay)
|
||||||
|
discard spawnFileSystemUsageActor(turn, relay)
|
||||||
runActor("syndesizer") do (turn: var Turn; root: Cap):
|
discard spawnHttpDriver(turn, relay)
|
||||||
connectStdio(turn, root)
|
discard spawnJsonSocketTranslator(turn, relay)
|
||||||
discard spawnTimers(turn, root)
|
discard spawnJsonStdioTranslator(turn, relay)
|
||||||
discard spawnCacheActor(turn, root)
|
discard spawnPulseActor(turn, relay)
|
||||||
discard spawnFileSystemUsageActor(turn, root)
|
discard spawnXmlTranslator(turn, relay)
|
||||||
discard spawnJsonSocketTranslator(turn, root)
|
|
||||||
discard spawnJsonStdioTranslator(turn, root)
|
|
||||||
discard spawnPulseActor(turn, root)
|
|
||||||
discard spawnWebhookActor(turn, root)
|
|
||||||
discard spawnWebsocketActor(turn, root)
|
|
||||||
discard spawnXmlTranslator(turn, root)
|
|
||||||
discard spawnXsltActor(turn, root)
|
|
||||||
when withPostgre:
|
|
||||||
discard spawnPostgreActor(turn, root)
|
|
||||||
when withSqlite:
|
|
||||||
discard spawnSqliteActor(turn, root)
|
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
include_rules
|
include_rules
|
||||||
: foreach *.nim | $(SYNDICATE_PROTOCOL) ../<schema> |> !nim_check |> | ./<checks>
|
: foreach *.nim |> !nim_bin |> {bin}
|
||||||
|
: foreach {bin} |> !assert_built |>
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
||||||
|
# SPDX-License-Identifier: Unlicense
|
||||||
|
|
||||||
|
import
|
||||||
|
std/[base64, os],
|
||||||
|
pkg/nimcrypto/blake2,
|
||||||
|
preserves, preserves/sugar, syndicate,
|
||||||
|
../schema/config,
|
||||||
|
../schema/base64 as schema
|
||||||
|
|
||||||
|
export Base64DecoderArguments
|
||||||
|
export schema
|
||||||
|
|
||||||
|
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 = 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 = observePattern(!Base64Text, { @[%1]: grabLit() })
|
||||||
|
during(turn, ds, encTextPat) do (bin: seq[byte]):
|
||||||
|
discard publish(turn, ds, Base64Text(
|
||||||
|
txt: encode(bin),
|
||||||
|
bin: bin,
|
||||||
|
))
|
||||||
|
|
||||||
|
let decFilePat = observePattern( !Base64File, { @[%0]: grabLit() })
|
||||||
|
during(turn, ds, decFilePat) do (txt: string):
|
||||||
|
var
|
||||||
|
bin = decode(txt)
|
||||||
|
digest = $blake2_256.digest(bin)
|
||||||
|
path = tmpDir / digest
|
||||||
|
writeFile(path, bin)
|
||||||
|
discard publish(turn, ds, Base64File(
|
||||||
|
txt: txt,
|
||||||
|
path: path,
|
||||||
|
size: bin.len,
|
||||||
|
))
|
||||||
|
|
||||||
|
when isMainModule:
|
||||||
|
import syndicate/relays
|
||||||
|
runActor("main") do (turn: Turn):
|
||||||
|
resolveEnvironment(turn) do (turn: Turn; ds: Cap):
|
||||||
|
spawnBase64Decoder(turn, ds)
|
|
@ -3,8 +3,8 @@
|
||||||
|
|
||||||
import std/times
|
import std/times
|
||||||
import preserves, syndicate,
|
import preserves, syndicate,
|
||||||
syndicate/[durings, relays],
|
syndicate/durings,
|
||||||
syndicate/actors/timers
|
syndicate/drivers/timers
|
||||||
|
|
||||||
import ../schema/config
|
import ../schema/config
|
||||||
|
|
||||||
|
@ -18,9 +18,9 @@ type CacheEntity {.final.} = ref object of Entity
|
||||||
pattern: Pattern
|
pattern: Pattern
|
||||||
lifetime: float64
|
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.
|
## 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?
|
# TODO: a seperate facet for every assertion, too much?
|
||||||
var ass = depattern(cache.pattern, ass.value.sequence)
|
var ass = depattern(cache.pattern, ass.value.sequence)
|
||||||
# Build an assertion with what he have of the pattern and capture.
|
# 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
|
stop(turn) # end this facet
|
||||||
|
|
||||||
proc isObserve(pat: Pattern): bool =
|
proc isObserve(pat: Pattern): bool =
|
||||||
pat.orKind == PatternKind.DCompound and
|
pat.orKind == PatternKind.group and
|
||||||
pat.dcompound.orKind == DCompoundKind.rec and
|
pat.group.type.orKind == GroupTypeKind.rec and
|
||||||
pat.dcompound.rec.label.isSymbol"Observe"
|
pat.group.type.rec.label.isSymbol"Observe"
|
||||||
|
|
||||||
proc spawnCacheActor*(turn: var Turn; root: Cap): Actor =
|
proc spawnCacheActor*(turn: Turn; root: Cap): Actor =
|
||||||
spawn("cache_actor", turn) do (turn: var Turn):
|
spawnActor(turn, "cache_actor") do (turn: Turn):
|
||||||
during(turn, root, ?:CacheArguments) do (ds: Cap, lifetime: float64):
|
during(turn, root, ?:CacheArguments) do (ds: Cap, lifetime: float64):
|
||||||
onPublish(turn, ds, ?:Observe) do (pat: Pattern, obs: Cap):
|
onPublish(turn, ds, ?:Observe) do (pat: Pattern, obs: Cap):
|
||||||
var cache: CacheEntity
|
var cache: CacheEntity
|
||||||
|
@ -51,7 +51,8 @@ proc spawnCacheActor*(turn: var Turn; root: Cap): Actor =
|
||||||
discard observe(turn, ds, pat, cache)
|
discard observe(turn, ds, pat, cache)
|
||||||
|
|
||||||
when isMainModule:
|
when isMainModule:
|
||||||
runActor("cache_actor") do (turn: var Turn; root: Cap):
|
import syndicate/relays
|
||||||
spawnTimers(turn, root)
|
runActor("main") do (turn: Turn):
|
||||||
connectStdio(turn, root)
|
resolveEnvironment(turn) do (turn: Turn; ds: Cap):
|
||||||
discard spawnCacheActor(turn, root)
|
discard spawnTimerDriver(turn, ds)
|
||||||
|
discard spawnCacheActor(turn, ds)
|
||||||
|
|
|
@ -2,15 +2,15 @@
|
||||||
# SPDX-License-Identifier: Unlicense
|
# SPDX-License-Identifier: Unlicense
|
||||||
|
|
||||||
import std/[dirs, os, paths]
|
import std/[dirs, os, paths]
|
||||||
import preserves
|
import preserves, preserves/sugar
|
||||||
import syndicate, syndicate/relays
|
import syndicate
|
||||||
|
|
||||||
import ../schema/[assertions, config]
|
import ../schema/[assertions, config]
|
||||||
|
|
||||||
proc spawnFileSystemUsageActor*(turn: var Turn; root: Cap): Actor {.discardable.} =
|
proc spawnFileSystemUsageActor*(turn: Turn; root: Cap): Actor {.discardable.} =
|
||||||
spawn("file-system-usage", turn) do (turn: var Turn):
|
spawn("file-system-usage", turn) do (turn: Turn):
|
||||||
during(turn, root, ?:FileSystemUsageArguments) do (ds: Cap):
|
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]):
|
during(turn, ds, pat) do (lit: Literal[string]):
|
||||||
var ass = FileSystemUsage(path: lit.value)
|
var ass = FileSystemUsage(path: lit.value)
|
||||||
if fileExists(ass.path): ass.size = getFileSize(ass.path)
|
if fileExists(ass.path): ass.size = getFileSize(ass.path)
|
||||||
|
@ -22,6 +22,7 @@ proc spawnFileSystemUsageActor*(turn: var Turn; root: Cap): Actor {.discardable.
|
||||||
# TODO: updates?
|
# TODO: updates?
|
||||||
|
|
||||||
when isMainModule:
|
when isMainModule:
|
||||||
runActor("main") do (turn: var Turn; root: Cap):
|
import syndicate/relays
|
||||||
connectStdio(turn, root)
|
runActor("main") do (turn: Turn):
|
||||||
discard spawnFileSystemUsageActor(turn, root)
|
resolveEnvironment(turn) do (turn: Turn; ds: Cap):
|
||||||
|
discard spawnFileSystemUsageActor(turn, ds)
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
||||||
|
# SPDX-License-Identifier: Unlicense
|
||||||
|
|
||||||
|
## Thin wrapper over `syndicate/drivers/http_driver`.
|
||||||
|
|
||||||
|
import
|
||||||
|
pkg/taps,
|
||||||
|
pkg/preserves,
|
||||||
|
pkg/syndicate,
|
||||||
|
pkg/syndicate/drivers/http_driver,
|
||||||
|
pkg/syndicate/protocols/[gatekeeper, sturdy],
|
||||||
|
../schema/config
|
||||||
|
|
||||||
|
proc spawnHttpDriver*(turn: Turn; relay: Cap): Actor {.discardable.} =
|
||||||
|
## Create a dataspace for the driver and to the gatekeeper dance.
|
||||||
|
spawnActor(turn, "http-driver") do (turn: Turn):
|
||||||
|
let pat = Resolve?:{ 0: HttpDriverStep.dropType }
|
||||||
|
during(turn, relay, pat):
|
||||||
|
let ds = turn.newDataspace()
|
||||||
|
http_driver.spawnHttpDriver(turn, ds)
|
||||||
|
# Spawn a shared driver.
|
||||||
|
let pat = Resolve?:{ 0: HttpDriverStep.dropType, 1: grab() }
|
||||||
|
during(turn, relay, pat) do (obs: Cap):
|
||||||
|
discard publish(turn, obs, ResolvedAccepted(responderSession: ds))
|
||||||
|
# Pass the shared driver dataspace.
|
||||||
|
|
||||||
|
when isMainModule:
|
||||||
|
import syndicate/relays
|
||||||
|
|
||||||
|
when defined(solo5):
|
||||||
|
import solo5
|
||||||
|
acquireDevices([("eth0", netBasic)], netAcquireHook)
|
||||||
|
|
||||||
|
proc envRoute: Route =
|
||||||
|
var pr = parsePreserves $solo5_start_info.cmdline
|
||||||
|
if result.fromPreserves pr:
|
||||||
|
return
|
||||||
|
elif pr.isSequence:
|
||||||
|
for e in pr:
|
||||||
|
if result.fromPreserves e:
|
||||||
|
return
|
||||||
|
quit("failed to parse command line for route to Syndicate gatekeeper")
|
||||||
|
|
||||||
|
runActor("main") do (turn: Turn):
|
||||||
|
let relay = newDataspace(turn)
|
||||||
|
spawnRelays(turn, relay)
|
||||||
|
resolve(turn, relay, envRoute(), spawnHttpDriver)
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
runActor("main") do (turn: Turn):
|
||||||
|
resolveEnvironment(turn) do (turn: Turn; relay: Cap):
|
||||||
|
spawnHttpDriver(turn, relay)
|
|
@ -0,0 +1,2 @@
|
||||||
|
define:ipv6Enabled
|
||||||
|
include:"std/assertions"
|
|
@ -1,39 +1,82 @@
|
||||||
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
||||||
# SPDX-License-Identifier: Unlicense
|
# SPDX-License-Identifier: Unlicense
|
||||||
|
|
||||||
import std/[asyncdispatch, asyncnet, json]
|
import
|
||||||
from std/nativesockets import AF_UNIX, SOCK_STREAM, Protocol
|
std/[json, options],
|
||||||
import preserves, preserves/jsonhooks, syndicate, syndicate/relays
|
pkg/sys/[ioqueue, sockets],
|
||||||
|
pkg/preserves, pkg/preserves/jsonhooks,
|
||||||
import ../schema/config, ../json_messages
|
pkg/syndicate, pkg/syndicate/protocols/[gatekeeper, sturdy],
|
||||||
|
../schema/[config, json_messages]
|
||||||
proc spawnJsonSocketTranslator*(turn: var Turn; root: Cap): Actor =
|
|
||||||
spawn("json-socket-translator", turn) do (turn: var Turn):
|
|
||||||
during(turn, root, ?:JsonSocketTranslatorArguments) do (ds: Cap, socketPath: string):
|
|
||||||
let socket = newAsyncSocket(
|
|
||||||
domain = AF_UNIX,
|
|
||||||
sockType = SOCK_STREAM,
|
|
||||||
protocol = cast[Protocol](0),
|
|
||||||
buffered = false,
|
|
||||||
)
|
|
||||||
addCallback(connectUnix(socket, socketPath), turn) do (turn: var Turn):
|
|
||||||
let a = JsonTranslatorConnected(path: socketPath)
|
|
||||||
discard publish(turn, ds, a)
|
|
||||||
|
|
||||||
let socketFacet = turn.facet
|
|
||||||
proc processOutput(fut: Future[string]) {.gcsafe.} =
|
|
||||||
run(socketFacet) do (turn: var Turn):
|
|
||||||
var data = fut.read.parseJson
|
|
||||||
message(turn, ds, RecvJson(data: data))
|
|
||||||
socket.recvLine.addCallback(processOutput)
|
|
||||||
socket.recvLine.addCallback(processOutput)
|
|
||||||
|
|
||||||
|
template translateSocketBody {.dirty.} =
|
||||||
|
# Template workaround for CPS and parameterized types.
|
||||||
|
var
|
||||||
|
guard = initGuard(facet)
|
||||||
|
dec = newBufferedDecoder(0)
|
||||||
|
buf = new string #TODO: get a pointer into the decoder
|
||||||
|
alive = true
|
||||||
|
proc kill(turn: Turn) =
|
||||||
|
alive = false
|
||||||
|
proc setup(turn: Turn) =
|
||||||
|
# Closure, not CPS.
|
||||||
onMessage(turn, ds, ?:SendJson) do (data: JsonNode):
|
onMessage(turn, ds, ?:SendJson) do (data: JsonNode):
|
||||||
asyncCheck(turn, send(socket, $data & "\n"))
|
if alive:
|
||||||
do:
|
discard trampoline:
|
||||||
close(socket)
|
whelp write(socket[], $data & "\n")
|
||||||
|
else:
|
||||||
|
stderr.writeLine "dropped send of ", data
|
||||||
|
discard publish(turn, observer, ResolvedAccepted(responderSession: ds))
|
||||||
|
# Resolve the <json-socket-translator { }> step.
|
||||||
|
onStop(facet, kill)
|
||||||
|
run(facet, setup)
|
||||||
|
while alive:
|
||||||
|
# TODO: parse buffer
|
||||||
|
buf[].setLen(0x4000)
|
||||||
|
let n = read(socket[], buf)
|
||||||
|
if n < 1:
|
||||||
|
stderr.writeLine "socket read returned ", n
|
||||||
|
else:
|
||||||
|
buf[].setLen(n)
|
||||||
|
dec.feed(buf[])
|
||||||
|
var data = dec.parse()
|
||||||
|
if data.isSome:
|
||||||
|
proc send(turn: Turn) =
|
||||||
|
# Closure, not CPS.
|
||||||
|
message(turn, ds, initRecord("recv", data.get))
|
||||||
|
run(facet, send)
|
||||||
|
stderr.writeLine "close socket ", sa
|
||||||
|
close(socket[])
|
||||||
|
|
||||||
|
proc translateSocket(facet: Facet; sa: TcpAddress; ds, observer: Cap) {.asyncio.} =
|
||||||
|
var
|
||||||
|
socket = new AsyncConn[Protocol.Tcp]
|
||||||
|
conn = connectTcpAsync(sa.host, Port sa.port)
|
||||||
|
socket[] = conn
|
||||||
|
translateSocketBody()
|
||||||
|
|
||||||
|
proc translateSocket(facet: Facet; sa: UnixAddress; ds, observer: Cap) {.asyncio.} =
|
||||||
|
var
|
||||||
|
socket = new AsyncConn[Protocol.Unix]
|
||||||
|
conn = connectUnixAsync(sa.path)
|
||||||
|
socket[] = conn
|
||||||
|
translateSocketBody()
|
||||||
|
|
||||||
|
proc spawnJsonSocketTranslator*(turn: Turn; relay: Cap): Actor {.discardable.} =
|
||||||
|
let pat = Resolve?:{ 0: JsonSocketTranslatorStep.grabTypeFlat, 1: grab() }
|
||||||
|
spawnActor(turn, "json-socket-translator") do (turn: Turn):
|
||||||
|
during(turn, relay, pat) do (sa: TcpAddress, observer: Cap):
|
||||||
|
linkActor(turn, "json-socket-translator") do (turn: Turn):
|
||||||
|
let ds = turn.newDataspace()
|
||||||
|
discard trampoline:
|
||||||
|
whelp translateSocket(turn.facet, sa, ds, observer)
|
||||||
|
during(turn, relay, pat) do (sa: UnixAddress, observer: Cap):
|
||||||
|
linkActor(turn, "json-socket-translator") do (turn: Turn):
|
||||||
|
let ds = turn.newDataspace()
|
||||||
|
discard trampoline:
|
||||||
|
whelp translateSocket(turn.facet, sa, ds, observer)
|
||||||
|
|
||||||
when isMainModule:
|
when isMainModule:
|
||||||
runActor("json_socket_translator") do (turn: var Turn; root: Cap):
|
import syndicate/relays
|
||||||
connectStdio(turn, root)
|
runActor("main") do (turn: Turn):
|
||||||
discard spawnJsonSocketTranslator(turn, root)
|
resolveEnvironment(turn) do (turn: Turn; relay: Cap):
|
||||||
|
spawnJsonSocketTranslator(turn, relay)
|
||||||
|
|
|
@ -3,10 +3,9 @@
|
||||||
|
|
||||||
import std/[json, osproc]
|
import std/[json, osproc]
|
||||||
import preserves
|
import preserves
|
||||||
import syndicate, syndicate/relays
|
import syndicate
|
||||||
|
|
||||||
import ../schema/config
|
import ../schema/[config, json_messages]
|
||||||
import ../json_messages
|
|
||||||
|
|
||||||
proc runChild(params: seq[string]): string =
|
proc runChild(params: seq[string]): string =
|
||||||
if params.len < 1:
|
if params.len < 1:
|
||||||
|
@ -20,14 +19,15 @@ proc runChild(params: seq[string]): string =
|
||||||
if result == "":
|
if result == "":
|
||||||
stderr.writeLine "no ouput"
|
stderr.writeLine "no ouput"
|
||||||
|
|
||||||
proc spawnJsonStdioTranslator*(turn: var Turn; root: Cap): Actor {.discardable.} =
|
proc spawnJsonStdioTranslator*(turn: Turn; root: Cap): Actor {.discardable.} =
|
||||||
spawn("json-stdio-translator", turn) do (turn: var Turn):
|
spawnActor(turn, "json-stdio-translator") do (turn: Turn):
|
||||||
during(turn, root, ?:JsonTranslatorArguments) do (argv: seq[string], ds: Cap):
|
during(turn, root, ?:JsonTranslatorArguments) do (argv: seq[string], ds: Cap):
|
||||||
var js = parseJson(runChild(argv))
|
var js = parseJson(runChild(argv))
|
||||||
message(turn, ds, RecvJson(data: js))
|
message(turn, ds, RecvJson(data: js))
|
||||||
discard publish(turn, ds, RecvJson(data: js))
|
discard publish(turn, ds, RecvJson(data: js))
|
||||||
|
|
||||||
when isMainModule:
|
when isMainModule:
|
||||||
runActor("main") do (turn: var Turn; root: Cap):
|
import syndicate/relays
|
||||||
connectStdio(turn, root)
|
runActor("main") do (turn: Turn):
|
||||||
spawnJsonStdioTranslator(turn, root)
|
resolveEnvironment(turn) do (turn: Turn; ds: Cap):
|
||||||
|
spawnJsonStdioTranslator(turn, ds)
|
||||||
|
|
|
@ -2,9 +2,8 @@
|
||||||
# SPDX-License-Identifier: Unlicense
|
# SPDX-License-Identifier: Unlicense
|
||||||
|
|
||||||
import std/[options, tables, times]
|
import std/[options, tables, times]
|
||||||
import preserves, syndicate,
|
import preserves, preserves/sugar
|
||||||
syndicate/relays,
|
import syndicate, syndicate/drivers/timers
|
||||||
syndicate/actors/timers
|
|
||||||
|
|
||||||
import ../schema/[assertions, config]
|
import ../schema/[assertions, config]
|
||||||
|
|
||||||
|
@ -18,7 +17,7 @@ type PulseEntity {.final.} = ref object of Entity
|
||||||
observePattern: Pattern
|
observePattern: Pattern
|
||||||
observing: bool
|
observing: bool
|
||||||
|
|
||||||
proc schedule(turn: var Turn; pulse: PulseEntity) =
|
proc schedule(turn: Turn; pulse: PulseEntity) =
|
||||||
## Schedule the next pulse.
|
## Schedule the next pulse.
|
||||||
## The next pulse will be schedule using the current time as
|
## The next pulse will be schedule using the current time as
|
||||||
## reference point and not the moment of the previous pulse.
|
## reference point and not the moment of the previous pulse.
|
||||||
|
@ -28,7 +27,7 @@ proc schedule(turn: var Turn; pulse: PulseEntity) =
|
||||||
observer: pulse.self,
|
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.
|
## Publish observers in reponse to <later-than …> assertions.
|
||||||
pulse.timers.target.retract(turn, pulse.timerHandle)
|
pulse.timers.target.retract(turn, pulse.timerHandle)
|
||||||
schedule(turn, pulse)
|
schedule(turn, pulse)
|
||||||
|
@ -37,7 +36,7 @@ method publish(pulse: PulseEntity; turn: var Turn; ass: AssertionRef; h: Handle)
|
||||||
pulse.target.publish(turn, a, h)
|
pulse.target.publish(turn, a, h)
|
||||||
pulse.target.sync(turn, pulse.self)
|
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.
|
## Retract observers in response to a sync message.
|
||||||
pulse.observing = false
|
pulse.observing = false
|
||||||
for h in pulse.observers.keys:
|
for h in pulse.observers.keys:
|
||||||
|
@ -47,7 +46,7 @@ type ProxyEntity {.final.} = ref object of Entity
|
||||||
## A proxy `Entity` that diverts observers to a `PulseEntity`.
|
## A proxy `Entity` that diverts observers to a `PulseEntity`.
|
||||||
pulse: 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.
|
## Proxy assertions that are not observations.
|
||||||
if proxy.pulse.observePattern.matches ass.value:
|
if proxy.pulse.observePattern.matches ass.value:
|
||||||
if proxy.pulse.observers.len == 0:
|
if proxy.pulse.observers.len == 0:
|
||||||
|
@ -56,7 +55,7 @@ method publish(proxy: ProxyEntity; turn: var Turn; ass: AssertionRef; h: Handle)
|
||||||
else:
|
else:
|
||||||
proxy.pulse.target.publish(turn, ass, h)
|
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.
|
## Retract proxied assertions.
|
||||||
var obs: AssertionRef
|
var obs: AssertionRef
|
||||||
if proxy.pulse.observers.pop(h, obs):
|
if proxy.pulse.observers.pop(h, obs):
|
||||||
|
@ -67,15 +66,15 @@ method retract(proxy: ProxyEntity; turn: var Turn; h: Handle) =
|
||||||
else:
|
else:
|
||||||
proxy.pulse.target.retract(turn, h)
|
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 mesages.
|
||||||
proxy.pulse.target.message(turn, v)
|
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 sync.
|
||||||
proxy.pulse.target.sync(turn, peer)
|
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
|
new result
|
||||||
result.pulse = PulseEntity(
|
result.pulse = PulseEntity(
|
||||||
target: ds.target,
|
target: ds.target,
|
||||||
|
@ -85,11 +84,11 @@ proc newProxyEntity(turn: var Turn; timers, ds: Cap; period: float): ProxyEntity
|
||||||
)
|
)
|
||||||
result.pulse.self = newCap(turn, result.pulse)
|
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
|
## Spawn an actor that retracts and re-asserts observers on
|
||||||
## a timed pulse. Requires a timer service on the `root` capability.
|
## a timed pulse. Requires a timer service on the `root` capability.
|
||||||
spawn("pulse", turn) do (turn: var Turn):
|
spawnActor(turn, "pulse") do (turn: Turn):
|
||||||
let grabPeriod = ?Observe(pattern: !Pulse) ?? { 0: grab() }
|
let grabPeriod = observePattern(!Pulse, { @[%0]: grab() })
|
||||||
during(turn, root, ?:PulseArguments) do (ds: Cap):
|
during(turn, root, ?:PulseArguments) do (ds: Cap):
|
||||||
during(turn, ds, grabPeriod) do (lit: Literal[float]):
|
during(turn, ds, grabPeriod) do (lit: Literal[float]):
|
||||||
if lit.value < 0.000_1:
|
if lit.value < 0.000_1:
|
||||||
|
@ -100,7 +99,7 @@ proc spawnPulseActor*(turn: var Turn; root: Cap): Actor =
|
||||||
discard publish(turn, ds, pulse)
|
discard publish(turn, ds, pulse)
|
||||||
|
|
||||||
when isMainModule:
|
when isMainModule:
|
||||||
runActor("main") do (turn: var Turn; root: Cap):
|
import syndicate/relays
|
||||||
spawnTimers(turn, root)
|
runActor("main") do (turn: Turn):
|
||||||
connectStdio(turn, root)
|
resolveEnvironment(turn) do (turn: Turn; ds: Cap):
|
||||||
discard spawnPulseActor(turn, root)
|
discard spawnPulseActor(turn, ds)
|
||||||
|
|
|
@ -1,113 +0,0 @@
|
||||||
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
|
||||||
# SPDX-License-Identifier: Unlicense
|
|
||||||
|
|
||||||
import preserves, syndicate, syndicate/relays
|
|
||||||
import ../schema/[config, sql]
|
|
||||||
|
|
||||||
# Avoid Sqlite3 from the standard library because it is
|
|
||||||
# only held together by wishful thinking and dlload.
|
|
||||||
|
|
||||||
{.passC: staticExec("pkg-config --cflags sqlite3").}
|
|
||||||
{.passL: staticExec("pkg-config --libs sqlite3").}
|
|
||||||
|
|
||||||
{.pragma: sqlite3h, header: "sqlite3.h".}
|
|
||||||
|
|
||||||
var
|
|
||||||
SQLITE_VERSION_NUMBER {.importc, sqlite3h.}: cint
|
|
||||||
SQLITE_OK {.importc, sqlite3h.}: cint
|
|
||||||
SQLITE_ROW {.importc, sqlite3h.}: cint
|
|
||||||
SQLITE_DONE {.importc, sqlite3h.}: cint
|
|
||||||
SQLITE_OPEN_READONLY {.importc, sqlite3h.}: cint
|
|
||||||
|
|
||||||
const
|
|
||||||
SQLITE_INTEGER = 1
|
|
||||||
SQLITE_FLOAT = 2
|
|
||||||
SQLITE_TEXT = 3
|
|
||||||
SQLITE_BLOB = 4
|
|
||||||
# SQLITE_NULL = 5
|
|
||||||
|
|
||||||
type
|
|
||||||
Sqlite3 {.importc: "sqlite3", sqlite3h.} = distinct pointer
|
|
||||||
Stmt {.importc: "sqlite3_stmt", sqlite3h.} = distinct pointer
|
|
||||||
|
|
||||||
{.pragma: importSqlite3, importc: "sqlite3_$1", sqlite3h.}
|
|
||||||
|
|
||||||
proc libversion_number: cint {.importSqlite3.}
|
|
||||||
|
|
||||||
proc open_v2(filename: cstring; ppDb: ptr Sqlite3; flags: cint; zVfs: cstring): cint {.importSqlite3.}
|
|
||||||
proc close(ds: Sqlite3): int32 {.discardable, importSqlite3.}
|
|
||||||
|
|
||||||
proc errmsg(db: Sqlite3): cstring {.importSqlite3.}
|
|
||||||
|
|
||||||
proc prepare_v2(db: Sqlite3; zSql: cstring, nByte: cint; ppStmt: ptr Stmt; pzTail: ptr cstring): cint {.importSqlite3.}
|
|
||||||
|
|
||||||
proc step(para1: Stmt): cint {.importSqlite3.}
|
|
||||||
|
|
||||||
proc column_count(stmt: Stmt): int32 {.importSqlite3.}
|
|
||||||
proc column_blob(stmt: Stmt; col: cint): pointer {.importSqlite3.}
|
|
||||||
proc column_bytes(stmt: Stmt; col: cint): cint {.importSqlite3.}
|
|
||||||
proc column_double(stmt: Stmt; col: cint): float64 {.importSqlite3.}
|
|
||||||
proc column_int64(stmt: Stmt; col: cint): int64 {.importSqlite3.}
|
|
||||||
proc column_text(stmt: Stmt; col: cint): cstring {.importSqlite3.}
|
|
||||||
proc column_type(stmt: Stmt; col: cint): cint {.importSqlite3.}
|
|
||||||
proc finalize(stmt: Stmt): cint {.importSqlite3.}
|
|
||||||
|
|
||||||
doAssert libversion_number() == SQLITE_VERSION_NUMBER
|
|
||||||
|
|
||||||
proc logError(db: Sqlite3; context: string) =
|
|
||||||
writeLine(stderr, errmsg(db), ": ", context)
|
|
||||||
|
|
||||||
proc extractValue(stmt: Stmt; col: cint): Value =
|
|
||||||
case column_type(stmt, col)
|
|
||||||
of SQLITE_INTEGER:
|
|
||||||
result = toPreserves(column_int64(stmt, col))
|
|
||||||
of SQLITE_FLOAT:
|
|
||||||
result = toPreserves(column_double(stmt, col))
|
|
||||||
of SQLITE_TEXT:
|
|
||||||
result = Value(kind: pkString, string: newString(column_bytes(stmt, col)))
|
|
||||||
if result.string.len > 0:
|
|
||||||
copyMem(addr result.string[0], column_text(stmt, col), result.string.len)
|
|
||||||
of SQLITE_BLOB:
|
|
||||||
result = Value(kind: pkByteString, bytes: newSeq[byte](column_bytes(stmt, col)))
|
|
||||||
if result.bytes.len > 0:
|
|
||||||
copyMem(addr result.bytes[0], column_blob(stmt, col), result.bytes.len)
|
|
||||||
else:
|
|
||||||
result = initRecord("null")
|
|
||||||
|
|
||||||
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):
|
|
||||||
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)
|
|
||||||
|
|
||||||
when isMainModule:
|
|
||||||
runActor("main") do (turn: var Turn; root: Cap):
|
|
||||||
connectStdio(turn, root)
|
|
||||||
spawnSqliteActor(turn, root)
|
|
|
@ -1,105 +0,0 @@
|
||||||
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
|
||||||
# SPDX-License-Identifier: Unlicense
|
|
||||||
|
|
||||||
## An actor for relaying Webhooks.
|
|
||||||
|
|
||||||
import std/[asyncdispatch, asynchttpserver, net, strutils, tables, uri]
|
|
||||||
|
|
||||||
import preserves, preserves/jsonhooks
|
|
||||||
import syndicate, syndicate/[bags, relays]
|
|
||||||
import syndicate/protocols/http
|
|
||||||
|
|
||||||
import ../schema/config
|
|
||||||
|
|
||||||
type
|
|
||||||
CapBag = Bag[Cap]
|
|
||||||
Endpoints = Table[seq[string], Cap]
|
|
||||||
|
|
||||||
func splitPath(s: string): seq[string] = s.strip(chars={'/'}).split('/')
|
|
||||||
|
|
||||||
proc toRecord(req: Request; seqnum: BiggestInt; path: seq[string]): Value =
|
|
||||||
## Convert a request value from the std/asynchttpserver module
|
|
||||||
## to a request type from syndicate/protocols/http.
|
|
||||||
var record: HttpRequest
|
|
||||||
record.sequenceNumber = seqnum
|
|
||||||
record.host = req.hostname
|
|
||||||
record.`method` = Symbol($req.reqMethod)
|
|
||||||
record.path = path
|
|
||||||
for key, val in req.headers.pairs:
|
|
||||||
record.headers[Symbol key] = val
|
|
||||||
for key, val in decodeQuery(req.url.query):
|
|
||||||
record.query[Symbol key] =
|
|
||||||
@[QueryValue(orKind: QueryValueKind.string, string: val)]
|
|
||||||
let contentType = req.headers.getOrDefault("content-type")
|
|
||||||
result = toPreserves record
|
|
||||||
if req.body.len > 0:
|
|
||||||
result[7] =
|
|
||||||
case contentType.toString
|
|
||||||
of "application/json":
|
|
||||||
req.body.parsePreserves
|
|
||||||
of "application/octet-stream":
|
|
||||||
cast[seq[byte]](req.body).toPreserves
|
|
||||||
else:
|
|
||||||
req.body.toPreserves
|
|
||||||
|
|
||||||
proc spawnWebhookActor*(turn: var Turn; root: Cap): Actor =
|
|
||||||
spawn("webhooks", turn) do (turn: var Turn):
|
|
||||||
let pat = grabRecord("webhooks", grabDictionary({ "listen": ?:config.Tcp }))
|
|
||||||
# Grab the details on listening for requests.
|
|
||||||
# Disregard endpoints so the server doesn't restart as those change.
|
|
||||||
during(turn, root, pat) do (host: string; port: Port):
|
|
||||||
let endpointsPat = grabRecord("webhooks", grabDictionary({
|
|
||||||
"listen": ?config.Tcp(host: host, port: BiggestInt port),
|
|
||||||
"endpoints": grab(),
|
|
||||||
}))
|
|
||||||
# construct a pattern for grabbing endpoints when the server is ready
|
|
||||||
var seqNum: BiggestInt
|
|
||||||
let facet = turn.facet
|
|
||||||
let endpoints = newTable[seq[string], CapBag]()
|
|
||||||
# use a bag so the same capability registered multiple
|
|
||||||
# times with the same path does not get duplicate messages
|
|
||||||
|
|
||||||
proc cb(req: Request): Future[void] =
|
|
||||||
inc(seqNum)
|
|
||||||
let path = req.url.path.splitPath
|
|
||||||
if not endpoints.hasKey path:
|
|
||||||
result = respond(req, Http404,
|
|
||||||
"no capabilities registered at $1\n" % [req.url.path])
|
|
||||||
else:
|
|
||||||
result = respond(req, Http200, "")
|
|
||||||
proc act(turn: var Turn) {.gcsafe.} =
|
|
||||||
let rec = req.toRecord(seqNum, path)
|
|
||||||
for cap in endpoints[path]:
|
|
||||||
message(turn, cap, rec)
|
|
||||||
run(facet, act)
|
|
||||||
|
|
||||||
let server = newAsyncHttpServer()
|
|
||||||
stderr.writeLine("listening for webhooks at ", host, ":", port)
|
|
||||||
if host.isIpAddress:
|
|
||||||
var ip = parseIpAddress host
|
|
||||||
case ip.family
|
|
||||||
of IPv6:
|
|
||||||
asyncCheck(turn, server.serve(port, cb, host, domain = AF_INET6))
|
|
||||||
of IPv4:
|
|
||||||
asyncCheck(turn, server.serve(port, cb, host, domain = AF_INET))
|
|
||||||
else:
|
|
||||||
asyncCheck(turn, server.serve(port, cb, host, domain = AF_INET6))
|
|
||||||
asyncCheck(turn, server.serve(port, cb, host, domain = AF_INET))
|
|
||||||
|
|
||||||
during(turn, root, endpointsPat) do (eps: Endpoints):
|
|
||||||
for path, cap in eps:
|
|
||||||
if not endpoints.hasKey path:
|
|
||||||
endpoints[path] = CapBag()
|
|
||||||
discard endpoints[path].change(cap, +1)
|
|
||||||
do:
|
|
||||||
for path, cap in eps:
|
|
||||||
discard endpoints[path].change(cap, -1)
|
|
||||||
|
|
||||||
do:
|
|
||||||
stderr.writeLine("closing for webhook server at ", host, ":", port)
|
|
||||||
close(server)
|
|
||||||
|
|
||||||
when isMainModule:
|
|
||||||
runActor("webhooks") do (turn: var Turn; root: Cap):
|
|
||||||
connectStdio(turn, root)
|
|
||||||
discard spawnWebhookActor(turn, root)
|
|
|
@ -1,55 +0,0 @@
|
||||||
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
|
||||||
# SPDX-License-Identifier: Unlicense
|
|
||||||
|
|
||||||
import std/[asyncdispatch, json]
|
|
||||||
import preserves
|
|
||||||
import syndicate, syndicate/relays
|
|
||||||
import ws
|
|
||||||
|
|
||||||
import ../schema/config, ../json_messages
|
|
||||||
|
|
||||||
type WebSocket = ws.WebSocket
|
|
||||||
# not the object from the transportAddress schema
|
|
||||||
|
|
||||||
proc spawnWebsocketActor*(turn: var Turn; root: Cap): Actor =
|
|
||||||
spawn("websocket-actor", turn) do (turn: var Turn):
|
|
||||||
during(turn, root, ?:WebsocketArguments) do (ds: Cap, url: string):
|
|
||||||
let facet = turn.facet
|
|
||||||
var
|
|
||||||
ws: WebSocket
|
|
||||||
connectedHandle: Handle
|
|
||||||
newWebSocket(url).addCallback(turn) do (turn: var Turn; sock: WebSocket):
|
|
||||||
ws = sock
|
|
||||||
connectedHandle = publish(turn, ds, initRecord("connected", url.toPreserves))
|
|
||||||
var fut: Future[(Opcode, string)]
|
|
||||||
proc recvMessage() {.gcsafe.} =
|
|
||||||
fut = receivePacket ws
|
|
||||||
addCallback(fut, facet) do (turn: var Turn):
|
|
||||||
let (opcode, data) = read fut
|
|
||||||
case opcode
|
|
||||||
of Text:
|
|
||||||
message(turn, ds,
|
|
||||||
RecvJson(data: data.parseJson))
|
|
||||||
of Binary:
|
|
||||||
message(turn, ds,
|
|
||||||
initRecord("recv", cast[seq[byte]](data).toPreserves))
|
|
||||||
of Ping:
|
|
||||||
asyncCheck(turn, ws.send(data, Pong))
|
|
||||||
of Pong, Cont:
|
|
||||||
discard
|
|
||||||
of Close:
|
|
||||||
retract(turn, connectedHandle)
|
|
||||||
stderr.writeLine "closed connection with ", url
|
|
||||||
stop(turn)
|
|
||||||
return
|
|
||||||
recvMessage()
|
|
||||||
recvMessage()
|
|
||||||
onMessage(turn, ds, ?:SendJson) do (data: JsonNode):
|
|
||||||
asyncCheck(turn, ws.send($data, Text))
|
|
||||||
do:
|
|
||||||
close(ws)
|
|
||||||
|
|
||||||
when isMainModule:
|
|
||||||
runActor("main") do (turn: var Turn; root: Cap):
|
|
||||||
connectStdio(turn, root)
|
|
||||||
discard spawnWebsocketActor(turn, root)
|
|
|
@ -2,7 +2,7 @@
|
||||||
# SPDX-License-Identifier: Unlicense
|
# SPDX-License-Identifier: Unlicense
|
||||||
|
|
||||||
import std/[options, parsexml, xmlparser, xmltree]
|
import std/[options, parsexml, xmlparser, xmltree]
|
||||||
import preserves, preserves/xmlhooks
|
import preserves, preserves/sugar, preserves/xmlhooks
|
||||||
import syndicate
|
import syndicate
|
||||||
|
|
||||||
import ../schema/[assertions, config]
|
import ../schema/[assertions, config]
|
||||||
|
@ -17,17 +17,18 @@ proc translatePreserves(pr: Value): XmlTranslation {.gcsafe.} =
|
||||||
var xn = result.pr.preservesTo(XmlNode)
|
var xn = result.pr.preservesTo(XmlNode)
|
||||||
if xn.isSome: result.xml = $get(xn)
|
if xn.isSome: result.xml = $get(xn)
|
||||||
|
|
||||||
proc spawnXmlTranslator*(turn: var Turn; root: Cap): Actor {.discardable.} =
|
proc spawnXmlTranslator*(turn: Turn; root: Cap): Actor {.discardable.} =
|
||||||
spawn("xml-translator", turn) do (turn: var Turn):
|
spawnActor(turn, "xml-translator") do (turn: Turn):
|
||||||
during(turn, root, ?:XmlTranslatorArguments) do (ds: Cap):
|
during(turn, root, ?:XmlTranslatorArguments) do (ds: Cap):
|
||||||
let obsPat = ?Observe(pattern: !XmlTranslation)
|
let xmlPat = observePattern(!XmlTranslation, {@[%0]:grab()})
|
||||||
during(turn, ds, obsPat ?? {0: grab()}) do (xs: Literal[string]):
|
during(turn, ds, xmlPat) do (xs: Literal[string]):
|
||||||
publish(turn, ds, translateXml(xs.value))
|
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))
|
publish(turn, ds, translatePreserves(pr.value))
|
||||||
|
|
||||||
when isMainModule:
|
when isMainModule:
|
||||||
import syndicate/relays
|
import syndicate/relays
|
||||||
runActor("main") do (turn: var Turn; root: Cap):
|
runActor("main") do (turn: Turn):
|
||||||
connectStdio(turn, root)
|
resolveEnvironment(turn) do (turn: Turn; ds: Cap):
|
||||||
spawnXmlTranslator(turn, root)
|
spawnXmlTranslator(turn, ds)
|
||||||
|
|
|
@ -1,133 +0,0 @@
|
||||||
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
|
||||||
# SPDX-License-Identifier: Unlicense
|
|
||||||
|
|
||||||
## This was all Tony's idea, except for the silly name.
|
|
||||||
|
|
||||||
import std/[asyncdispatch, os, terminal]
|
|
||||||
import preserves
|
|
||||||
import syndicate, syndicate/[durings, relays]
|
|
||||||
import illwill
|
|
||||||
|
|
||||||
proc exitProc() {.noconv.} =
|
|
||||||
illwillDeinit()
|
|
||||||
showCursor()
|
|
||||||
quit QuitSuccess
|
|
||||||
|
|
||||||
setControlCHook(exitProc)
|
|
||||||
|
|
||||||
proc parsePattern(pr: Value): Pattern =
|
|
||||||
let
|
|
||||||
dropSigil = initRecord("lit", "_".toSymbol)
|
|
||||||
grabSigil = initRecord("lit", "?".toSymbol)
|
|
||||||
var pr = grab(pr).toPreserves
|
|
||||||
apply(pr) do (pr: var Value):
|
|
||||||
if pr == dropSigil:
|
|
||||||
pr = initRecord("_")
|
|
||||||
elif pr == grabSigil:
|
|
||||||
pr = initRecord("bind", initRecord("_"))
|
|
||||||
doAssert result.fromPreserves(pr)
|
|
||||||
|
|
||||||
proc inputPattern: Pattern =
|
|
||||||
var args = commandLineParams()
|
|
||||||
if args.len != 1:
|
|
||||||
quit "expected a single pattern argument"
|
|
||||||
else:
|
|
||||||
var input = pop args
|
|
||||||
if input == "":
|
|
||||||
quit "expected Preserves Pattern on stdin"
|
|
||||||
else:
|
|
||||||
var pr: Value
|
|
||||||
try: pr = decodePreserves(input)
|
|
||||||
except ValueError: discard
|
|
||||||
try: pr = parsePreserves(input)
|
|
||||||
except ValueError: discard
|
|
||||||
if pr.isFalse:
|
|
||||||
quit "failed to parse Preserves argument"
|
|
||||||
result = parsePattern(pr)
|
|
||||||
|
|
||||||
type TermEntity {.final.} = ref object of Entity
|
|
||||||
pattern: Pattern
|
|
||||||
value: Value
|
|
||||||
|
|
||||||
method publish(te: TermEntity; turn: var Turn; v: AssertionRef; h: Handle) =
|
|
||||||
te.value = v.value
|
|
||||||
var termBuf = newTerminalBuffer(terminalWidth(), terminalHeight())
|
|
||||||
var y = 1
|
|
||||||
termBuf.write(1, y, $te.pattern, styleBright)
|
|
||||||
inc(y)
|
|
||||||
termBuf.drawHorizLine(0, termBuf.width(), y)
|
|
||||||
inc(y)
|
|
||||||
termBuf.write(0, y, $h, styleBright)
|
|
||||||
for i, e in te.value.sequence:
|
|
||||||
inc(y)
|
|
||||||
termBuf.write(1, y, $e)
|
|
||||||
termBuf.display()
|
|
||||||
|
|
||||||
method retract(te: TermEntity; turn: var Turn; h: Handle) =
|
|
||||||
var termBuf = newTerminalBuffer(terminalWidth(), terminalHeight())
|
|
||||||
var y = 1
|
|
||||||
termBuf.write(1, y, $te.pattern, styleDim)
|
|
||||||
inc y
|
|
||||||
termBuf.drawHorizLine(0, termBuf.width(), y, true)
|
|
||||||
inc(y)
|
|
||||||
termBuf.write(0, y, $h, styleBright)
|
|
||||||
if te.value.isSequence:
|
|
||||||
for i, e in te.value.sequence:
|
|
||||||
inc(y)
|
|
||||||
termBuf.write(1, y, $e)
|
|
||||||
else:
|
|
||||||
inc(y)
|
|
||||||
termBuf.write(1, y, $te.value)
|
|
||||||
termBuf.display()
|
|
||||||
|
|
||||||
type DumpEntity {.final.} = ref object of Entity
|
|
||||||
discard
|
|
||||||
|
|
||||||
method publish(dump: DumpEntity; turn: var Turn; ass: AssertionRef; h: Handle) =
|
|
||||||
stdout.writeLine($ass.value)
|
|
||||||
stdout.flushFile()
|
|
||||||
|
|
||||||
method message*(dump: DumpEntity; turn: var Turn; ass: AssertionRef) =
|
|
||||||
stdout.writeLine($ass.value)
|
|
||||||
stdout.flushFile()
|
|
||||||
|
|
||||||
proc exit {.noconv.} =
|
|
||||||
illwillDeinit()
|
|
||||||
showCursor()
|
|
||||||
quit()
|
|
||||||
|
|
||||||
setControlCHook(exit)
|
|
||||||
|
|
||||||
proc main =
|
|
||||||
let
|
|
||||||
route = envRoute()
|
|
||||||
pat = inputPattern()
|
|
||||||
|
|
||||||
if stdout.is_a_TTY:
|
|
||||||
illwillInit()
|
|
||||||
hideCursor()
|
|
||||||
|
|
||||||
discard bootDataspace("syndex_card") do (turn: var Turn; root: Cap):
|
|
||||||
resolve(turn, root, route) do (turn: var Turn; ds: Cap):
|
|
||||||
var termBuf = newTerminalBuffer(terminalWidth(), terminalHeight())
|
|
||||||
termBuf.write(1, 1, $pat, styleBright)
|
|
||||||
termBuf.drawHorizLine(1, termBuf.width(), 2)
|
|
||||||
termBuf.display()
|
|
||||||
|
|
||||||
discard observe(turn, ds, pat, TermEntity(pattern: pat))
|
|
||||||
|
|
||||||
while true:
|
|
||||||
try: poll()
|
|
||||||
except CatchableError:
|
|
||||||
illwillDeinit()
|
|
||||||
showCursor()
|
|
||||||
quit getCurrentExceptionMsg()
|
|
||||||
|
|
||||||
else:
|
|
||||||
let entity = DumpEntity()
|
|
||||||
runActor("syndex_card") do (root: Cap; turn: var Turn):
|
|
||||||
spawnRelays(turn, root)
|
|
||||||
resolve(turn, root, route) do (turn: var Turn; ds: Cap):
|
|
||||||
discard observe(turn, ds, pat, entity)
|
|
||||||
|
|
||||||
main()
|
|
|
@ -8,7 +8,7 @@ proc parsePattern(pr: Value): Pattern =
|
||||||
let
|
let
|
||||||
dropSigil = initRecord("lit", "_".toSymbol)
|
dropSigil = initRecord("lit", "_".toSymbol)
|
||||||
grabSigil = initRecord("lit", "?".toSymbol)
|
grabSigil = initRecord("lit", "?".toSymbol)
|
||||||
var pr = grab(pr).toPreserves
|
var pr = drop(pr).toPreserves
|
||||||
apply(pr) do (pr: var Value):
|
apply(pr) do (pr: var Value):
|
||||||
if pr == dropSigil:
|
if pr == dropSigil:
|
||||||
pr = initRecord("_")
|
pr = initRecord("_")
|
||||||
|
@ -36,38 +36,29 @@ proc toLine(values: seq[Value]; prefix: char): string =
|
||||||
add(result, $v)
|
add(result, $v)
|
||||||
add(result, '\n')
|
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
|
var values = ass.value.sequence
|
||||||
stdout.write(values.toLine('+'))
|
stdout.write(values.toLine('+'))
|
||||||
stdout.flushFile()
|
stdout.flushFile()
|
||||||
dump.assertions[h] = values
|
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]
|
var values: seq[Value]
|
||||||
if dump.assertions.pop(h, values):
|
if dump.assertions.pop(h, values):
|
||||||
stdout.write(values.toLine('-'))
|
stdout.write(values.toLine('-'))
|
||||||
stdout.flushFile()
|
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.write(ass.value.sequence.toLine('!'))
|
||||||
stdout.flushFile()
|
stdout.flushFile()
|
||||||
|
|
||||||
proc exitProc() {.noconv.} =
|
|
||||||
stdout.write('\n')
|
|
||||||
quit()
|
|
||||||
|
|
||||||
proc main =
|
proc main =
|
||||||
let
|
let
|
||||||
route = envRoute()
|
|
||||||
patterns = inputPatterns()
|
patterns = inputPatterns()
|
||||||
entity = DumpEntity()
|
entity = DumpEntity()
|
||||||
runActor("syndex_card") do (root: Cap; turn: var Turn):
|
runActor("syndex_card") do (turn: Turn):
|
||||||
|
resolveEnvironment(turn) do (turn: Turn; peer: Cap):
|
||||||
for pat in patterns:
|
for pat in patterns:
|
||||||
discard observe(turn, root, pat, entity)
|
discard observe(turn, peer, pat, entity)
|
||||||
spawnRelays(turn, root)
|
|
||||||
resolve(turn, root, route) do (turn: var Turn; ds: Cap):
|
|
||||||
for pat in patterns:
|
|
||||||
discard observe(turn, ds, pat, entity)
|
|
||||||
|
|
||||||
setControlCHook(exitProc)
|
|
||||||
main()
|
main()
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
# SPDX-License-Identifier: Unlicense
|
# SPDX-License-Identifier: Unlicense
|
||||||
|
|
||||||
import std/[os, strutils]
|
import std/[os, strutils]
|
||||||
import preserves, syndicate
|
import preserves, preserves/sugar, syndicate
|
||||||
import ../schema/[assertions, config]
|
import ./schema/[assertions, config]
|
||||||
|
|
||||||
{.passC: staticExec("pkg-config --cflags libxslt").}
|
{.passC: staticExec("pkg-config --cflags libxslt").}
|
||||||
{.passL: staticExec("pkg-config --libs libxslt").}
|
{.passL: staticExec("pkg-config --libs libxslt").}
|
||||||
|
@ -173,11 +173,11 @@ proc toPreservesHook*(xn: xmlNodePtr): Value =
|
||||||
preserveSiblings(items, xn)
|
preserveSiblings(items, xn)
|
||||||
items[0]
|
items[0]
|
||||||
|
|
||||||
proc spawnXsltActor*(turn: var Turn; root: Cap): Actor {.discardable.} =
|
proc spawnXsltActor*(turn: Turn; root: Cap): Actor {.discardable.} =
|
||||||
spawn("xslt", turn) do (turn: var Turn):
|
spawnActor(turn, "xslt") do (turn: Turn):
|
||||||
initLibXml()
|
initLibXml()
|
||||||
during(turn, root, ?:XsltArguments) do (ds: Cap):
|
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]):
|
during(turn, ds, sheetsPat) do (stylesheet: Literal[string], input: Literal[string]):
|
||||||
let cur = loadStylesheet(stylesheet.value)
|
let cur = loadStylesheet(stylesheet.value)
|
||||||
if cur.isNil:
|
if cur.isNil:
|
||||||
|
@ -206,6 +206,6 @@ proc spawnXsltActor*(turn: var Turn; root: Cap): Actor {.discardable.} =
|
||||||
|
|
||||||
when isMainModule:
|
when isMainModule:
|
||||||
import syndicate/relays
|
import syndicate/relays
|
||||||
runActor("main") do (turn: var Turn; root: Cap):
|
runActor("main") do (turn: Turn):
|
||||||
connectStdio(turn, root)
|
resolveEnvironment(turn) do (turn: Turn; ds: Cap):
|
||||||
spawnXsltActor(turn, root)
|
spawnXsltActor(turn, ds)
|
|
@ -1,13 +1,61 @@
|
||||||
# Package
|
# Emulate Nimble from CycloneDX data at sbom.json.
|
||||||
|
|
||||||
version = "20240209"
|
import std/json
|
||||||
author = "Emery Hemingway"
|
|
||||||
description = "Utilites for Syndicated Actors and Synit"
|
|
||||||
license = "unlicense"
|
|
||||||
srcDir = "src"
|
|
||||||
bin = @["mintsturdyref", "mount_actor", "msg", "net_mapper", "preserve_process_environment", "syndesizer", "syndex_card", "syndump"]
|
|
||||||
|
|
||||||
|
proc lookupComponent(sbom: JsonNode; bomRef: string): JsonNode =
|
||||||
|
for c in sbom{"components"}.getElems.items:
|
||||||
|
if c{"bom-ref"}.getStr == bomRef:
|
||||||
|
return c
|
||||||
|
result = newJNull()
|
||||||
|
|
||||||
# Dependencies
|
let
|
||||||
|
sbom = (getPkgDir() & "/sbom.json").readFile.parseJson
|
||||||
|
comp = sbom{"metadata", "component"}
|
||||||
|
bomRef = comp{"bom-ref"}.getStr
|
||||||
|
|
||||||
requires "nim >= 2.0.0", "illwill", "syndicate >= 20240208", "ws"
|
version = comp{"version"}.getStr
|
||||||
|
author = comp{"authors"}[0]{"name"}.getStr
|
||||||
|
description = comp{"description"}.getStr
|
||||||
|
license = comp{"licenses"}[0]{"license", "id"}.getStr
|
||||||
|
|
||||||
|
for prop in comp{"properties"}.getElems.items:
|
||||||
|
let (key, val) = (prop{"name"}.getStr, prop{"value"}.getStr)
|
||||||
|
case key
|
||||||
|
of "nim:skipDirs:":
|
||||||
|
add(skipDirs, val)
|
||||||
|
of "nim:skipFiles:":
|
||||||
|
add(skipFiles, val)
|
||||||
|
of "nim:skipExt":
|
||||||
|
add(skipExt, val)
|
||||||
|
of "nim:installDirs":
|
||||||
|
add(installDirs, val)
|
||||||
|
of "nim:installFiles":
|
||||||
|
add(installFiles, val)
|
||||||
|
of "nim:installExt":
|
||||||
|
add(installExt, val)
|
||||||
|
of "nim:binDir":
|
||||||
|
add(binDir, val)
|
||||||
|
of "nim:srcDir":
|
||||||
|
add(srcDir, val)
|
||||||
|
of "nim:backend":
|
||||||
|
add(backend, val)
|
||||||
|
else:
|
||||||
|
if key.startsWith "nim:bin:":
|
||||||
|
namedBin[key[8..key.high]] = val
|
||||||
|
|
||||||
|
for depend in sbom{"dependencies"}.items:
|
||||||
|
if depend{"ref"}.getStr == bomRef:
|
||||||
|
for depRef in depend{"dependsOn"}.items:
|
||||||
|
let dep = sbom.lookupComponent(depRef.getStr)
|
||||||
|
var spec = dep{"name"}.getStr
|
||||||
|
for extRef in dep{"externalReferences"}.elems:
|
||||||
|
if extRef{"type"}.getStr == "vcs":
|
||||||
|
spec = extRef{"url"}.getStr
|
||||||
|
break
|
||||||
|
let ver = dep{"version"}.getStr
|
||||||
|
if ver != "":
|
||||||
|
if ver.allCharsInSet {'0'..'9', '.'}: spec.add " == "
|
||||||
|
else: spec.add '#'
|
||||||
|
spec.add ver
|
||||||
|
requires spec
|
||||||
|
break
|
||||||
|
|
Loading…
Reference in New Issue