Rewrite for Syndicate-nim changes
This commit is contained in:
parent
3caf7d05fb
commit
73e63bdc77
|
@ -8,7 +8,8 @@ Matching patterns to actions is done with `action-handler` records:
|
||||||
```preserves
|
```preserves
|
||||||
<action-handler "foo://(.*):(.*)" $entity <krempel ["--host=$1" "--port=$2"]> >
|
<action-handler "foo://(.*):(.*)" $entity <krempel ["--host=$1" "--port=$2"]> >
|
||||||
```
|
```
|
||||||
|
|
||||||
In the preceding example the URI `foo://bar:42` would cause the message `<krempel ["--host=bar" "--port=42"]>` to be sent to `$entity`.
|
In the preceding example the URI `foo://bar:42` would cause the message `<krempel ["--host=bar" "--port=42"]>` to be sent to `$entity`.
|
||||||
|
|
||||||
|
See [handlers-example.pr](./handlers-example.pr) for more information.
|
||||||
|
|
||||||
The [protocol.nim](./src/protocol.nim) file is generated from the [protocol.prs](./protocol.prs) schema, a [Tupfile](https://gittup.org/tup/) file is provided to do this.
|
The [protocol.nim](./src/protocol.nim) file is generated from the [protocol.prs](./protocol.prs) schema, a [Tupfile](https://gittup.org/tup/) file is provided to do this.
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
; the <exec-space #!…> dataspace starts programs when it receives exec messages
|
||||||
|
? <exec-space ?execspace> [
|
||||||
|
|
||||||
|
<action-handler "file://(.*.pdf)" $execspace
|
||||||
|
<exec ["/run/current-system/sw/bin/mupdf" "$1"]>>
|
||||||
|
|
||||||
|
<action-handler "file://(.*.png)" $execspace
|
||||||
|
<exec ["/run/current-system/sw/bin/imv" "$1"]>>
|
||||||
|
|
||||||
|
<action-handler "(magnet:?.*xt=urn:btih.*)" $execspace
|
||||||
|
<exec ["/run/current-system/sw/bin/transmission-gtk" "$1"]>>
|
||||||
|
|
||||||
|
<action-handler "(tg://.*)" $execspace
|
||||||
|
<exec ["/run/current-system/sw/bin/telegram-desktop" "$1"]>>
|
||||||
|
|
||||||
|
<action-handler "https://twitter.com/(.*)" $execspace
|
||||||
|
<exec ["/run/current-system/sw/bin/firefox" "--new-tab" "https://nitter.net/$1"]>>
|
||||||
|
|
||||||
|
<action-handler "(http://.*)|(https://.*)" $execspace
|
||||||
|
<exec ["/run/current-system/sw/bin/firefox" "$1"]>>
|
||||||
|
|
||||||
|
]
|
||||||
|
|
||||||
|
; when mpv is available send it commands directly
|
||||||
|
? <mpv ?mpv> [
|
||||||
|
<action-handler "(.*mp4)|(.*mp3)" $mpv <play-file "$1">>
|
||||||
|
]
|
|
@ -1,7 +1,10 @@
|
||||||
version 1 .
|
version 1 .
|
||||||
|
|
||||||
XdgOpen = <xdg-open @uris [string ...]> .
|
XdgOpen = <xdg-open @uri string> .
|
||||||
|
|
||||||
|
UriRunnerConfig = {
|
||||||
|
handlerspace: #!any
|
||||||
|
urispace: #!any
|
||||||
|
} .
|
||||||
|
|
||||||
UriRunnerConfig = ListenOn / ActionHandler .
|
|
||||||
ListenOn = <listen-on @dataspace #!any> .
|
|
||||||
ActionHandler = <action-handler @pat string @entity #!any @action any> .
|
ActionHandler = <action-handler @pat string @entity #!any @action any> .
|
||||||
|
|
|
@ -1,32 +1,22 @@
|
||||||
|
|
||||||
import
|
import
|
||||||
std/typetraits, preserves
|
preserves
|
||||||
|
|
||||||
type
|
type
|
||||||
XdgOpen* {.preservesRecord: "xdg-open".} = object
|
XdgOpen* {.preservesRecord: "xdg-open".} = object
|
||||||
`uris`*: seq[string]
|
`uri`*: string
|
||||||
|
|
||||||
UriRunnerConfigKind* {.pure.} = enum
|
UriRunnerConfig* {.preservesDictionary.} = object
|
||||||
`ListenOn`, `ActionHandler`
|
`handlerspace`* {.preservesEmbedded.}: Preserve[void]
|
||||||
`UriRunnerConfig`* {.preservesOr.} = object
|
`urispace`* {.preservesEmbedded.}: Preserve[void]
|
||||||
case orKind*: UriRunnerConfigKind
|
|
||||||
of UriRunnerConfigKind.`ListenOn`:
|
|
||||||
`listenon`*: ListenOn
|
|
||||||
|
|
||||||
of UriRunnerConfigKind.`ActionHandler`:
|
|
||||||
`actionhandler`*: ActionHandler
|
|
||||||
|
|
||||||
|
|
||||||
ListenOn* {.preservesRecord: "listen-on".} = object
|
|
||||||
`dataspace`* {.preservesEmbedded.}: Preserve[void]
|
|
||||||
|
|
||||||
ActionHandler* {.preservesRecord: "action-handler".} = object
|
ActionHandler* {.preservesRecord: "action-handler".} = object
|
||||||
`pat`*: string
|
`pat`*: string
|
||||||
`entity`* {.preservesEmbedded.}: Preserve[void]
|
`entity`* {.preservesEmbedded.}: Preserve[void]
|
||||||
`action`*: Preserve[void]
|
`action`*: Preserve[void]
|
||||||
|
|
||||||
proc `$`*(x: XdgOpen | UriRunnerConfig | ListenOn | ActionHandler): string =
|
proc `$`*(x: XdgOpen | UriRunnerConfig | ActionHandler): string =
|
||||||
`$`(toPreserve(x))
|
`$`(toPreserve(x))
|
||||||
|
|
||||||
proc encode*(x: XdgOpen | UriRunnerConfig | ListenOn | ActionHandler): seq[byte] =
|
proc encode*(x: XdgOpen | UriRunnerConfig | ActionHandler): seq[byte] =
|
||||||
encode(toPreserve(x))
|
encode(toPreserve(x))
|
||||||
|
|
|
@ -1,11 +1,15 @@
|
||||||
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
||||||
# SPDX-License-Identifier: Unlicense
|
# SPDX-License-Identifier: Unlicense
|
||||||
|
|
||||||
import std/[asyncdispatch, re]
|
import std/[re, tables]
|
||||||
import preserves, syndicate
|
import preserves, syndicate, syndicate/patterns
|
||||||
import ./protocol
|
import ./protocol
|
||||||
|
|
||||||
type RegexAction = tuple[regex: Regex; entity: Ref; action: Assertion]
|
# importing std/re doesn't actually link against PCRE
|
||||||
|
{.passC: staticExec("pkg-config --cflags libpcre").}
|
||||||
|
{.passL: staticExec("pkg-config --libs libpcre").}
|
||||||
|
|
||||||
|
type RegexAction = tuple[regex: Regex, entity: Ref, action: Assertion]
|
||||||
|
|
||||||
proc rewrite(result: var Assertion; uri: string; regex: Regex) =
|
proc rewrite(result: var Assertion; uri: string; regex: Regex) =
|
||||||
proc op(pr: var Assertion) =
|
proc op(pr: var Assertion) =
|
||||||
|
@ -15,24 +19,36 @@ proc rewrite(result: var Assertion; uri: string; regex: Regex) =
|
||||||
|
|
||||||
runActor("main") do (root: Ref; turn: var Turn):
|
runActor("main") do (root: Ref; turn: var Turn):
|
||||||
connectStdio(root, turn)
|
connectStdio(root, turn)
|
||||||
var handlers: seq[RegexAction]
|
during(turn, root, ?UriRunnerConfig) do (handlerspace: Ref, urispace: Ref):
|
||||||
|
|
||||||
onPublish(turn, root, ?ActionHandler) do (pat: string; entity: Ref; response: Assertion):
|
# sanity chek
|
||||||
handlers.add (re(pat, {reIgnoreCase, reStudy}), entity, response,)
|
onMessage(turn, handlerspace, ?XdgOpen) do (uri: string):
|
||||||
|
raiseAssert "got a message in the wrong dataspace!"
|
||||||
|
onPublish(turn, urispace, ?ActionHandler) do (pat: string; entity: Ref; act: Assertion):
|
||||||
|
raiseAssert "got an assertion in the wrong dataspace!"
|
||||||
|
|
||||||
during(turn, root, ?ListenOn) do (ds: Ref):
|
during(turn, handlerspace, dropType(ActionHandler)) do:
|
||||||
onMessage(turn, ds, ?XdgOpen) do (uris: seq[string]):
|
|
||||||
for uri in uris:
|
var handlers: Table[Handle, RegexAction]
|
||||||
var matched: bool
|
during(turn, handlerspace, ?ActionHandler) do (pat: string; entity: Ref; act: Assertion):
|
||||||
for handler in handlers:
|
# `duringHandle` is a symbol exposed by the `during` macro
|
||||||
|
handlers[duringHandle] = (re(pat, {reIgnoreCase, reStudy}), entity, act,)
|
||||||
|
do:
|
||||||
|
del(handlers, duringHandle)
|
||||||
|
|
||||||
|
onMessage(turn, urispace, ?XdgOpen) do (uri: string):
|
||||||
|
assert len(handlers) > 0
|
||||||
|
var matched = false
|
||||||
|
for handler in handlers.values:
|
||||||
if match(uri, handler.regex):
|
if match(uri, handler.regex):
|
||||||
matched = true
|
matched = true
|
||||||
var response = handler.action
|
var action = handler.action
|
||||||
rewrite(response, uri, handler.regex)
|
try:
|
||||||
message(turn, handler.entity, response)
|
rewrite(action, uri, handler.regex)
|
||||||
|
message(turn, handler.entity, action)
|
||||||
|
except CatchableError:
|
||||||
|
stderr.writeLine "rewrite failed on ", action
|
||||||
if not matched:
|
if not matched:
|
||||||
stderr.writeLine "no actions matched for ", uri
|
stderr.writeLine "no actions matched for ", uri
|
||||||
do:
|
|
||||||
# The Syndicate server retracts all assertions when
|
|
||||||
# the config is rewritten.
|
|
||||||
handlers.setLen 0
|
|
||||||
|
|
|
@ -1,24 +1,29 @@
|
||||||
# SPDX-FileCopyrightText: ☭ 2022 Emery Hemingway
|
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
||||||
# SPDX-License-Identifier: Unlicense
|
# SPDX-License-Identifier: Unlicense
|
||||||
|
|
||||||
|
## Facsimile of xdg-open from xdg-utils.
|
||||||
|
## https://portland.freedesktop.org/doc/xdg-open.html
|
||||||
|
|
||||||
import std/[asyncdispatch, os]
|
import std/[asyncdispatch, os]
|
||||||
from std/sequtils import map
|
import preserves, syndicate, syndicate/capabilities
|
||||||
import syndicate, syndicate/capabilities
|
|
||||||
import ./protocol
|
import ./protocol
|
||||||
|
|
||||||
proc unixSocketPath: string =
|
proc publishUri(turn: var Turn; ds: Ref) =
|
||||||
result = getEnv("SYNDICATE_SOCK")
|
for arg in commandLineParams():
|
||||||
if result == "":
|
var xo = XdgOpen(uri: arg)
|
||||||
result = getEnv("XDG_RUNTIME_DIR", "/run/user/1000") / "dataspace"
|
if fileExists(xo.uri):
|
||||||
|
xo.uri = "file://" & absolutePath(arg)
|
||||||
|
message(turn, ds, xo)
|
||||||
|
stop(turn)
|
||||||
|
|
||||||
bootDataspace("main") do (root: Ref; turn: var Turn):
|
proc unixSocketPath: Unix =
|
||||||
connectUnix(turn, unixSocketPath(), mint()) do (turn: var Turn; ds: Ref):
|
result.path = getEnv("SYNDICATE_SOCK")
|
||||||
var uris = commandLineParams().map do (param: string) -> string:
|
if result.path == "":
|
||||||
if fileExists param:
|
result.path = getEnv("XDG_RUNTIME_DIR", "/run/user/1000") / "dataspace"
|
||||||
"file://" & absolutePath(param)
|
|
||||||
else:
|
|
||||||
param
|
|
||||||
message(turn, ds, XdgOpen(uris: uris))
|
|
||||||
|
|
||||||
for i in 0..7: poll(20)
|
bootDataspace("xdg-open") do (root: Ref; turn: var Turn):
|
||||||
|
let cap = mint().toPreserve(Ref)
|
||||||
|
connect(turn, unixSocketPath(), cap, publishUri)
|
||||||
|
|
||||||
|
for i in 0..4: poll(20)
|
||||||
# A hack to exit
|
# A hack to exit
|
||||||
|
|
|
@ -1,68 +1,30 @@
|
||||||
; Expose a dataspace over a unix socket
|
|
||||||
let ?socketspace = dataspace
|
|
||||||
<require-service <relay-listener <unix "/run/user/1000/dataspace"> $gatekeeper>>
|
|
||||||
<bind "syndicate" #x"" $socketspace>
|
|
||||||
|
|
||||||
; Create a new dataspace for receiving "exec" messages
|
|
||||||
; from the uri_runner
|
|
||||||
let ?execspace = dataspace
|
|
||||||
$execspace ?? <exec ?argv> $config [
|
|
||||||
let ?id = timestamp
|
|
||||||
let ?facet = facet
|
|
||||||
let ?d = <uri_runner-exec $id $argv>
|
|
||||||
<run-service <daemon $d>>
|
|
||||||
<daemon $d {
|
|
||||||
argv: $argv,
|
|
||||||
env: { WAYLAND_DISPLAY: "wayland-1" DISPLAY: ":0" XDG_CACHE_HOME: "/home/cache" }
|
|
||||||
readyOnStart: #f,
|
|
||||||
restart: =never,
|
|
||||||
}>
|
|
||||||
? <service-state <daemon $d> complete> [$facet ! stop]
|
|
||||||
? <service-state <daemon $d> failed> [$facet ! stop]
|
|
||||||
]
|
|
||||||
|
|
||||||
; Start the uri_runner
|
|
||||||
<require-service <daemon uri_runner>>
|
<require-service <daemon uri_runner>>
|
||||||
|
|
||||||
|
; configure the daemon when it is built
|
||||||
|
; (this is an artifact of the author's build system)
|
||||||
|
? <built uri_runner ?path ?hash> [
|
||||||
<daemon uri_runner {
|
<daemon uri_runner {
|
||||||
argv: "uri_runner"
|
argv: [$path]
|
||||||
protocol: text/syndicate
|
protocol: application/syndicate
|
||||||
|
env: { BUILD_SUM: $hash }
|
||||||
}>
|
}>
|
||||||
|
]
|
||||||
|
|
||||||
? <service-object <daemon uri_runner> ?cap> $cap [
|
; grab a dataspace for observing <xdg-open …> messages
|
||||||
|
? <socketspace ?socketspace> [
|
||||||
|
|
||||||
; send configuration to uri_runner
|
; log xdg-open messages
|
||||||
<listen-on $socketspace>
|
$socketspace ?? <xdg-open ?stuff> [
|
||||||
|
$log ! <log "-" { line: <xdg-open $stuff> }>
|
||||||
|
]
|
||||||
|
|
||||||
; When http* is matched send a message to $execspace
|
; configure the uri_runner
|
||||||
; that indicates the Syndicate server should start Firefox.
|
? <service-object <daemon uri_runner> ?cap> [
|
||||||
<action-handler "(http://.*|https://.*|.*html)" $execspace
|
$cap {
|
||||||
<exec ["/bin/firefox" "--new-tab" "$1"]>>
|
; watch the config dataspace for handler configuration
|
||||||
|
handlerspace: $config
|
||||||
; Capture a pattern within a pattern
|
urispace: $socketspace
|
||||||
<action-handler "https://twitter.com/(.*)" $execspace
|
}
|
||||||
<exec ["/bin/firefox" "--new-tab" "https://nitter.net/$1"]>>
|
|
||||||
|
|
||||||
; Local file-system paths should always be prefixed
|
|
||||||
; by file:// but that can be removed after matching
|
|
||||||
<action-handler "file://(.*.pdf)" $execspace
|
|
||||||
<exec ["/bin/mupdf" "$1"]>>
|
|
||||||
|
|
||||||
<action-handler "(magnet:?.*xt=urn:btih.*)" $execspace
|
|
||||||
<exec ["/bin/transmission-remote-gtk" "$1"]>>
|
|
||||||
|
|
||||||
<action-handler "(tox:.*)|uri:(tox:.*)" $execspace
|
|
||||||
<exec ["/bin/qtox" "$1"]>>
|
|
||||||
|
|
||||||
<action-handler "(gemini://.*|file:///.*.gmi)" $execspace
|
|
||||||
<exec ["/bin/lagrange" "$1"]>>
|
|
||||||
|
|
||||||
<action-handler "(https://.*\\.twitch.tv/.*)" $execspace
|
|
||||||
<exec ["/bin/streamlink" "-p" "mpv" "$1"]>>
|
|
||||||
|
|
||||||
; when the mpv-transator is available send it commands directly
|
|
||||||
$config ? <service-object <daemon mpv-translator> ?mpv> $mpv [
|
|
||||||
$cap <action-handler "(.*\\.avi|.*\\.mkv|.*mp4|.*ogg|.*youtu.*|.*\\.m3u8|.*webm)" $mpv
|
|
||||||
<mpv 1 { "command": ["loadfile" "$1" "append-play"] }>>
|
|
||||||
]
|
]
|
||||||
|
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# Package
|
# Package
|
||||||
|
|
||||||
version = "20230506"
|
version = "20230518"
|
||||||
author = "Emery"
|
author = "Emery"
|
||||||
description = "A better xdg-open"
|
description = "A better xdg-open"
|
||||||
license = "Unlicense"
|
license = "Unlicense"
|
||||||
|
@ -10,4 +10,4 @@ bin = @[ "uri_runner", "xdg_open"]
|
||||||
|
|
||||||
# Dependencies
|
# Dependencies
|
||||||
|
|
||||||
requires "nim >= 1.6.4", "syndicate >= 1.3.0"
|
requires "nim >= 1.6.4", "syndicate >= 20230518"
|
||||||
|
|
Loading…
Reference in New Issue