diff --git a/README.md b/README.md index 32839d9..67ae874 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,14 @@ # xdg-open-ng -An `xdg-open` replacement that uses Syndicate and PCRE pattern matching to open URIs. +An [xdg-open](https://portland.freedesktop.org/doc/xdg-open.html) replacement that uses [Syndicate](http://syndicate-lang.org/) and PCRE pattern matching to open URIs. -There are two utilites, `xdg-open` and `uri_runner`. The former connects to a shared Syndicate dataspace via a UNIX socket at `$SYNDICATE_SOCK` otherwise `$XDG_RUNTIME_DIR/dataspace` and as has no other configuration. The `uri_runner` component is intended to be managed by the [Syndicate server](https://git.syndicate-lang.org/syndicate-lang/syndicate-rs) thru which it receives configuration, see [uri_runner.pr](./uri_runner.pr) as an example. +There are two utilites, `xdg-open` and `uri_runner`. The former connects to a shared Syndicate dataspace via a UNIX socket at `$SYNDICATE_SOCK` otherwise `$XDG_RUNTIME_DIR/dataspace` and has no other configuration. The `uri_runner` component is intended to be managed by the [Syndicate server](https://git.syndicate-lang.org/syndicate-lang/syndicate-rs) thru which it receives configuration, see [uri_runner.pr](./uri_runner.pr) as an example. + +Matching patterns to actions is done with `action-handler` records: +```preserves + > +``` + +In the preceding example the URI `foo://bar:42` would cause the message `` to be sent to `$entity`. 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. - -## TODO -- Fallback commands? - -## Examples - -| Type | Rule | -|-|-| -| Bittorrent | | -| Gemini | | -| PDF | | -| Tox | | -| Twatter | | -| Video | | diff --git a/exec.prs b/exec.prs deleted file mode 100644 index 8760498..0000000 --- a/exec.prs +++ /dev/null @@ -1,23 +0,0 @@ -; Copied from ../syndicate-rs/syndicate-server/protocols/schemas/externalServices.prs -version 1 . - -Exec = . - -CommandLine = @shell string / @full FullCommandLine . -FullCommandLine = [@program string @args string ...] . - -RestartPolicy = -/ ; Whether the process terminates normally or abnormally, restart it - ; without affecting any peer processes within the service. - =always -/ ; If the process terminates normally, leave everything alone; if it - ; terminates abnormally, restart it without affecting peers. - @onError =on-error -/ ; If the process terminates normally, leave everything alone; if it - ; terminates abnormally, restart the whole daemon (all processes - ; within the daemon). - =all -/ ; Treat both normal and abnormal termination as normal termination; that is, never restart, - ; and enter state "complete" even if the process fails. - =never -. diff --git a/protocol.prs b/protocol.prs index 3d1b7bb..e0756ab 100644 --- a/protocol.prs +++ b/protocol.prs @@ -4,4 +4,4 @@ XdgOpen = . UriRunnerConfig = ListenOn / ActionHandler . ListenOn = . -ActionHandler = . +ActionHandler = . diff --git a/src/exec.nim b/src/exec.nim deleted file mode 100644 index 60a9717..0000000 --- a/src/exec.nim +++ /dev/null @@ -1,32 +0,0 @@ - -import - std/typetraits, preserves - -type - CommandLineKind* {.pure.} = enum - `shell`, `full` - CommandLineShell* = string - `CommandLine`* {.preservesOr.} = object - case orKind*: CommandLineKind - of CommandLineKind.`shell`: - `shell`*: CommandLineShell - - of CommandLineKind.`full`: - `full`*: FullCommandLine - - - Exec* {.preservesRecord: "exec".} = object - `argv`*: CommandLine - `restartPolicy`*: RestartPolicy - - `RestartPolicy`* {.preservesOr, pure.} = enum - `always`, `onError`, `all`, `never` - FullCommandLine* {.preservesTuple.} = object - `program`*: string - `args`* {.preservesTupleTail.}: seq[string] - -proc `$`*(x: CommandLine | Exec | FullCommandLine): string = - `$`(toPreserve(x)) - -proc encode*(x: CommandLine | Exec | FullCommandLine): seq[byte] = - encode(toPreserve(x)) diff --git a/src/protocol.nim b/src/protocol.nim index 1316823..a342e00 100644 --- a/src/protocol.nim +++ b/src/protocol.nim @@ -14,24 +14,26 @@ type `listenon`*: ListenOn[E] of UriRunnerConfigKind.`ActionHandler`: - `actionhandler`*: ActionHandler + `actionhandler`*: ActionHandler[E] ListenOn*[E] {.preservesRecord: "listen-on".} = ref object `dataspace`*: Preserve[E] - ActionHandler* {.preservesRecord: "action-handler".} = object + ActionHandler*[E] {.preservesRecord: "action-handler".} = ref object `pat`*: string - `cmd`*: seq[string] + `action`*: Preserve[E] + `entity`*: Preserve[E] -proc `$`*[E](x: UriRunnerConfig[E] | ListenOn[E]): string = +proc `$`*[E](x: UriRunnerConfig[E] | ListenOn[E] | ActionHandler[E]): string = `$`(toPreserve(x, E)) -proc encode*[E](x: UriRunnerConfig[E] | ListenOn[E]): seq[byte] = +proc encode*[E](x: UriRunnerConfig[E] | ListenOn[E] | ActionHandler[E]): seq[ + byte] = encode(toPreserve(x, E)) -proc `$`*(x: XdgOpen | ActionHandler): string = +proc `$`*(x: XdgOpen): string = `$`(toPreserve(x)) -proc encode*(x: XdgOpen | ActionHandler): seq[byte] = +proc encode*(x: XdgOpen): seq[byte] = encode(toPreserve(x)) diff --git a/src/uri_runner.nim b/src/uri_runner.nim index c97884e..54742ab 100644 --- a/src/uri_runner.nim +++ b/src/uri_runner.nim @@ -3,52 +3,39 @@ import std/[asyncdispatch, re] import preserves, syndicate -import ./protocol, ./exec +import ./protocol + +type RegexAction = tuple[regex: Regex; entity: Ref; action: Assertion] + +proc rewrite(result: var Assertion; uri: string; regex: Regex) = + proc op(pr: var Assertion) = + if pr.isString: + pr.string = replacef(uri, regex, pr.string) + apply(result, op) bootDataspace("main") do (root: Ref; turn: var Turn): - var actions: seq[tuple[regex: Regex; cmd: string; args: seq[Assertion]]] + var handlers: seq[RegexAction] connectStdio(root, turn) - onPublish(turn, root, ?ActionHandler) do (pat: string; cmd: seq[Assertion]): - if cmd.len < 2: - stderr.writeLine "ignoring ", $cmd, " for ", pat - else: - if cmd[0].isString: - var act = (re(pat, {reIgnoreCase, reStudy}), cmd[0].string, cmd[1..cmd.high],) - actions.add act - else: - stderr.writeLine "not a valid program specification: ", cmd[0] + onPublish(turn, root, ?ActionHandler[Ref]) do (pat: string; entity: Ref; response: Assertion): + handlers.add (re(pat, {reIgnoreCase, reStudy}), entity, response,) - during(turn, root, ?ListenOn[Ref]) do (a: Assertion): - let ds = unembed a + during(turn, root, ?ListenOn[Ref]) do (ds: Ref): onMessage(turn, ds, ?XdgOpen) do (uris: seq[string]): for uri in uris: var matched: bool - for act in actions: - if match(uri, act.regex): + for handler in handlers: + if match(uri, handler.regex): matched = true - var args = newSeq[string](act.args.len) - for i, arg in act.args: - if arg.isString: - args[i] = replacef(uri, act.regex, arg.string) - elif arg.isInteger: - if arg.int == 0: - args[i] = uri - else: - args[i] = replacef(uri, act.regex, "$" & $arg.int) - message(turn, root, Exec( - argv: CommandLine( - orKind: CommandLineKind.full, - full: FullCommandLine( - program: act.cmd, - args: args)), - restartPolicy: RestartPolicy.never)) + var response = handler.action + rewrite(response, uri, handler.regex) + message(turn, handler.entity, response) if not matched: stderr.writeLine "no actions matched for ", uri do: # The Syndicate server retracts all assertions when # the config is rewritten. - actions.setLen 0 + handlers.setLen 0 runForever() diff --git a/uri_runner.pr b/uri_runner.pr index dcb07ff..edbca7f 100644 --- a/uri_runner.pr +++ b/uri_runner.pr @@ -1,50 +1,68 @@ ; Expose a dataspace over a unix socket -let ?root_ds = dataspace +let ?socketspace = dataspace $gatekeeper>> - + +; Create a new dataspace for receiving "exec" messages +; from the uri_runner +let ?execspace = dataspace +$execspace ?? $config [ + let ?id = timestamp + let ?facet = facet + let ?d = + > + + ? complete> [$facet ! stop] + ? failed> [$facet ! stop] +] + +; Start the uri_runner > - -? ?cap> [ +? ?cap> $cap [ ; send configuration to uri_runner - $cap [ - + - ; Here the "0" argument is replaced with the whole URI asserted by xdg-open. - + ; When http* is matched send a message to $execspace + ; that indicates the Syndicate server should start Firefox. + > - ; An argument can be a reference to a capture. - + ; Capture a pattern within a pattern + > - ; An argument can contain a reference to a capture using the $i notation. - + ; Local file-system paths should always be prefixed + ; by file:// but that can be removed after matching + > - - + > - ; filesystem paths are always prefixed with file:// - - ] + > - ; uri_runner sends messages to the server to start handler applications - $cap ?? [ - let ?id = timestamp - let ?facet = facet - let ?d = - $config > - $config - $config ? complete> [$facet ! stop] - $config ? failed> [$facet ! stop] + > + + > + + ; when the mpv-transator is available send it commands directly + $config ? ?mpv> $mpv [ + $cap > ] ] diff --git a/xdg_open_ng.nimble b/xdg_open_ng.nimble index 68bdf83..2268568 100644 --- a/xdg_open_ng.nimble +++ b/xdg_open_ng.nimble @@ -1,6 +1,6 @@ # Package -version = "0.4.0" +version = "99999999" author = "Emery" description = "A better xdg-open" license = "Unlicense"