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
|
||||
<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`.
|
||||
|
||||
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.
|
||||
|
|
|
@ -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 .
|
||||
|
||||
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> .
|
||||
|
|
|
@ -1,32 +1,22 @@
|
|||
|
||||
import
|
||||
std/typetraits, preserves
|
||||
preserves
|
||||
|
||||
type
|
||||
XdgOpen* {.preservesRecord: "xdg-open".} = object
|
||||
`uris`*: seq[string]
|
||||
`uri`*: string
|
||||
|
||||
UriRunnerConfigKind* {.pure.} = enum
|
||||
`ListenOn`, `ActionHandler`
|
||||
`UriRunnerConfig`* {.preservesOr.} = object
|
||||
case orKind*: UriRunnerConfigKind
|
||||
of UriRunnerConfigKind.`ListenOn`:
|
||||
`listenon`*: ListenOn
|
||||
|
||||
of UriRunnerConfigKind.`ActionHandler`:
|
||||
`actionhandler`*: ActionHandler
|
||||
|
||||
|
||||
ListenOn* {.preservesRecord: "listen-on".} = object
|
||||
`dataspace`* {.preservesEmbedded.}: Preserve[void]
|
||||
UriRunnerConfig* {.preservesDictionary.} = object
|
||||
`handlerspace`* {.preservesEmbedded.}: Preserve[void]
|
||||
`urispace`* {.preservesEmbedded.}: Preserve[void]
|
||||
|
||||
ActionHandler* {.preservesRecord: "action-handler".} = object
|
||||
`pat`*: string
|
||||
`entity`* {.preservesEmbedded.}: Preserve[void]
|
||||
`action`*: Preserve[void]
|
||||
|
||||
proc `$`*(x: XdgOpen | UriRunnerConfig | ListenOn | ActionHandler): string =
|
||||
proc `$`*(x: XdgOpen | UriRunnerConfig | ActionHandler): string =
|
||||
`$`(toPreserve(x))
|
||||
|
||||
proc encode*(x: XdgOpen | UriRunnerConfig | ListenOn | ActionHandler): seq[byte] =
|
||||
proc encode*(x: XdgOpen | UriRunnerConfig | ActionHandler): seq[byte] =
|
||||
encode(toPreserve(x))
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
||||
# SPDX-License-Identifier: Unlicense
|
||||
|
||||
import std/[asyncdispatch, re]
|
||||
import preserves, syndicate
|
||||
import std/[re, tables]
|
||||
import preserves, syndicate, syndicate/patterns
|
||||
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 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):
|
||||
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):
|
||||
handlers.add (re(pat, {reIgnoreCase, reStudy}), entity, response,)
|
||||
# sanity chek
|
||||
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):
|
||||
onMessage(turn, ds, ?XdgOpen) do (uris: seq[string]):
|
||||
for uri in uris:
|
||||
var matched: bool
|
||||
for handler in handlers:
|
||||
during(turn, handlerspace, dropType(ActionHandler)) do:
|
||||
|
||||
var handlers: Table[Handle, RegexAction]
|
||||
during(turn, handlerspace, ?ActionHandler) do (pat: string; entity: Ref; act: Assertion):
|
||||
# `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):
|
||||
matched = true
|
||||
var response = handler.action
|
||||
rewrite(response, uri, handler.regex)
|
||||
message(turn, handler.entity, response)
|
||||
var action = handler.action
|
||||
try:
|
||||
rewrite(action, uri, handler.regex)
|
||||
message(turn, handler.entity, action)
|
||||
except CatchableError:
|
||||
stderr.writeLine "rewrite failed on ", action
|
||||
if not matched:
|
||||
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
|
||||
|
||||
## Facsimile of xdg-open from xdg-utils.
|
||||
## https://portland.freedesktop.org/doc/xdg-open.html
|
||||
|
||||
import std/[asyncdispatch, os]
|
||||
from std/sequtils import map
|
||||
import syndicate, syndicate/capabilities
|
||||
import preserves, syndicate, syndicate/capabilities
|
||||
import ./protocol
|
||||
|
||||
proc unixSocketPath: string =
|
||||
result = getEnv("SYNDICATE_SOCK")
|
||||
if result == "":
|
||||
result = getEnv("XDG_RUNTIME_DIR", "/run/user/1000") / "dataspace"
|
||||
proc publishUri(turn: var Turn; ds: Ref) =
|
||||
for arg in commandLineParams():
|
||||
var xo = XdgOpen(uri: arg)
|
||||
if fileExists(xo.uri):
|
||||
xo.uri = "file://" & absolutePath(arg)
|
||||
message(turn, ds, xo)
|
||||
stop(turn)
|
||||
|
||||
bootDataspace("main") do (root: Ref; turn: var Turn):
|
||||
connectUnix(turn, unixSocketPath(), mint()) do (turn: var Turn; ds: Ref):
|
||||
var uris = commandLineParams().map do (param: string) -> string:
|
||||
if fileExists param:
|
||||
"file://" & absolutePath(param)
|
||||
else:
|
||||
param
|
||||
message(turn, ds, XdgOpen(uris: uris))
|
||||
proc unixSocketPath: Unix =
|
||||
result.path = getEnv("SYNDICATE_SOCK")
|
||||
if result.path == "":
|
||||
result.path = getEnv("XDG_RUNTIME_DIR", "/run/user/1000") / "dataspace"
|
||||
|
||||
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
|
||||
|
|
|
@ -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>
|
||||
<require-service <daemon uri_runner>>
|
||||
|
||||
; 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,
|
||||
; 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 {
|
||||
argv: [$path]
|
||||
protocol: application/syndicate
|
||||
env: { BUILD_SUM: $hash }
|
||||
}>
|
||||
? <service-state <daemon $d> complete> [$facet ! stop]
|
||||
? <service-state <daemon $d> failed> [$facet ! stop]
|
||||
]
|
||||
|
||||
; Start the uri_runner
|
||||
<require-service <daemon uri_runner>>
|
||||
<daemon uri_runner {
|
||||
argv: "uri_runner"
|
||||
protocol: text/syndicate
|
||||
}>
|
||||
; grab a dataspace for observing <xdg-open …> messages
|
||||
? <socketspace ?socketspace> [
|
||||
|
||||
? <service-object <daemon uri_runner> ?cap> $cap [
|
||||
; log xdg-open messages
|
||||
$socketspace ?? <xdg-open ?stuff> [
|
||||
$log ! <log "-" { line: <xdg-open $stuff> }>
|
||||
]
|
||||
|
||||
; send configuration to uri_runner
|
||||
<listen-on $socketspace>
|
||||
|
||||
; When http* is matched send a message to $execspace
|
||||
; that indicates the Syndicate server should start Firefox.
|
||||
<action-handler "(http://.*|https://.*|.*html)" $execspace
|
||||
<exec ["/bin/firefox" "--new-tab" "$1"]>>
|
||||
|
||||
; Capture a pattern within a pattern
|
||||
<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"] }>>
|
||||
; configure the uri_runner
|
||||
? <service-object <daemon uri_runner> ?cap> [
|
||||
$cap {
|
||||
; watch the config dataspace for handler configuration
|
||||
handlerspace: $config
|
||||
urispace: $socketspace
|
||||
}
|
||||
]
|
||||
|
||||
]
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# Package
|
||||
|
||||
version = "20230506"
|
||||
version = "20230518"
|
||||
author = "Emery"
|
||||
description = "A better xdg-open"
|
||||
license = "Unlicense"
|
||||
|
@ -10,4 +10,4 @@ bin = @[ "uri_runner", "xdg_open"]
|
|||
|
||||
# Dependencies
|
||||
|
||||
requires "nim >= 1.6.4", "syndicate >= 1.3.0"
|
||||
requires "nim >= 1.6.4", "syndicate >= 20230518"
|
||||
|
|
Loading…
Reference in New Issue