Compare commits
No commits in common. "trunk" and "cd8ae554bc6ad10aee5ea5fd9f2def6f5f4ffd91" have entirely different histories.
trunk
...
cd8ae554bc
|
@ -1 +1,2 @@
|
|||
/nim.cfg
|
||||
uri_runner
|
||||
xdg_open
|
||||
|
|
18
README.md
18
README.md
|
@ -1,15 +1,9 @@
|
|||
# Syndicated Open
|
||||
# xd-open-ng
|
||||
|
||||
An [open command](https://en.wikipedia.org/wiki/Open_(process)) implementation that uses [Syndicate](http://syndicate-lang.org/) and PCRE pattern matching to open URIs.
|
||||
An `xdg-open` replacement that uses Syndicate and PCRE pattern matching to open URIs.
|
||||
|
||||
There are two utilites, `open` and `uri_runner`. The former connects to a shared Syndicate dataspace using a route at `$SYNDICATE_ROUTE` 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.
|
||||
There are two utilites, `uri_runner` and `xdg-open`; both expect to be able to connect to a shared Syndicate dataspace via a UNIX socket at `$SYNDICATE_SOCK` otherwise `$XDG_RUNTIME_DIR/dataspace`. The `xdg-open` component 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 is receives configuration, see [uri_runnner.pr](./uri_runnner.pr) as an example.
|
||||
|
||||
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.
|
||||
## TODO
|
||||
- Pattern back-references in commands
|
||||
- Fallback commands?
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
include ../syndicate-nim/depends.tup
|
||||
NIM_FLAGS += --path:$(TUP_CWD)/../syndicate-nim/src
|
||||
NIM_GROUPS += $(TUP_CWD)/<lock>
|
|
@ -0,0 +1,127 @@
|
|||
{
|
||||
"nodes": {
|
||||
"flake-utils": {
|
||||
"locked": {
|
||||
"lastModified": 1623875721,
|
||||
"narHash": "sha256-A8BU7bjS5GirpAUv4QA+QnJ4CceLHkcXdRp4xITDB0s=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "f7e004a55b120c02ecb6219596820fcd32ca8772",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nimble": {
|
||||
"inputs": {
|
||||
"nixpkgs": "nixpkgs"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1645925055,
|
||||
"narHash": "sha256-fbP0/1djwAtpNVkAe0OAZRU/0rfQswxn7Nr4ac7OiyY=",
|
||||
"owner": "nix-community",
|
||||
"repo": "flake-nimble",
|
||||
"rev": "5bbaa6321652cfa31c69dc5246bd407d12f75a5e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"id": "nimble",
|
||||
"type": "indirect"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1641555862,
|
||||
"narHash": "sha256-AJK1Q5djPXs/ba3K6gHsVAer9yDPVLic78ZIDsFSkHU=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "d1acd89e0116ff88eba80541027429fc922612e9",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"id": "nixpkgs",
|
||||
"type": "indirect"
|
||||
}
|
||||
},
|
||||
"nixpkgs_2": {
|
||||
"locked": {
|
||||
"lastModified": 1636723036,
|
||||
"narHash": "sha256-zFuu3TTj/NLq3NPJ8pprlmkOpjAoW3AWQNac3UFkCG8=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "50a1ef0f59752623af884f5085e3d5e8bd806c77",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "50a1ef0f59752623af884f5085e3d5e8bd806c77",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs_3": {
|
||||
"locked": {
|
||||
"lastModified": 1628186154,
|
||||
"narHash": "sha256-r2d0wvywFnL9z4iptztdFMhaUIAaGzrSs7kSok0PgmE=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "06552b72346632b6943c8032e57e702ea12413bf",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"nimble": "nimble",
|
||||
"syndicate": "syndicate"
|
||||
}
|
||||
},
|
||||
"rust": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils",
|
||||
"nixpkgs": "nixpkgs_3"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1630548746,
|
||||
"narHash": "sha256-SLzeWhEN0kRDTzL+AHfDxwbYEZm09UEcdIcPDdOIZew=",
|
||||
"owner": "oxalica",
|
||||
"repo": "rust-overlay",
|
||||
"rev": "00f6332f8c690480c7b7137fb8b1f069e7b2e18a",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "oxalica",
|
||||
"repo": "rust-overlay",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"syndicate": {
|
||||
"inputs": {
|
||||
"nixpkgs": "nixpkgs_2",
|
||||
"rust": "rust"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1645916341,
|
||||
"narHash": "sha256-vzGl+qKrVv1FaySUtb/eJGK7zPjIJT5/3Iv/xFhkkTs=",
|
||||
"ref": "trunk",
|
||||
"rev": "0f6d38dfa16c2250376c996f9b13e96bea92a45d",
|
||||
"revCount": 19,
|
||||
"type": "git",
|
||||
"url": "https://git.syndicate-lang.org/ehmry/syndicate-flake.git"
|
||||
},
|
||||
"original": {
|
||||
"id": "syndicate",
|
||||
"type": "indirect"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
{
|
||||
description = "xdg-open-ng";
|
||||
|
||||
outputs = { self, nimble, syndicate }:
|
||||
let inherit (syndicate.inputs.nixpkgs) lib;
|
||||
in {
|
||||
|
||||
defaultPackage =
|
||||
lib.mapAttrs (system: pkgs: pkgs.xdg-open-ng) self.packages;
|
||||
packages = lib.mapAttrs (system: pkgs:
|
||||
let nimblePackages = nimble.packages.${system};
|
||||
in {
|
||||
xdg-open-ng = pkgs.nimPackages.buildNimPackage rec {
|
||||
pname = "xdg-open";
|
||||
version = self.lastModifiedDate;
|
||||
src = self;
|
||||
propagatedBuildInputs = [
|
||||
(nimblePackages.nimsha2.overrideAttrs (attrs: {
|
||||
src = pkgs.fetchFromGitHub {
|
||||
owner = "jangko";
|
||||
repo = "nimSHA2";
|
||||
rev = "b8f666069dff1ed0c5142dd1ca692f0e71434716";
|
||||
hash = "sha256-Wqb3mQ7638UOTze71mf6WMyGiw9qTwhbJiGGb+9OR2k=";
|
||||
};
|
||||
}))
|
||||
nimblePackages.preserves
|
||||
(nimblePackages.syndicate.overrideAttrs
|
||||
(attrs: { doCheck = false; }))
|
||||
];
|
||||
postInstall = "mv $out/bin/xdg_open $out/bin/xdg-open";
|
||||
};
|
||||
}) { inherit (nimble.legacyPackages) x86_64-linux; };
|
||||
|
||||
nixosModule = self.nixosModules.xdg-open-ng;
|
||||
nixosModules.xdg-open-ng = import ./nixos-module.nix;
|
||||
};
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
; 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 "(file:///.*.html|http://.*|https://.*)" $execspace
|
||||
<exec ["/run/current-system/sw/bin/firefox" "$#"]>>
|
||||
|
||||
<action-handler "mailto:(.*)" $execspace
|
||||
<exec ["/run/current-system/sw/bin/astroid" "--mailto" "$#"]>>
|
||||
]
|
||||
|
||||
; when mpv is available send it commands directly
|
||||
? <mpv ?mpv> [
|
||||
<action-handler "(.*mp4)|(.*mp3)" $mpv <play-file "$1">>
|
||||
]
|
|
@ -1 +0,0 @@
|
|||
{"depends":[{"method":"fetchzip","packages":["hashlib"],"path":"/nix/store/v03nzlpdgbfxd2zhcnkfbkq01d5kqxcl-source","rev":"84e0247555e4488594975900401baaf5bbbfb53","sha256":"1pfczsv8kl36qpv543f93d2y2vgz2acckssfap7l51s2x62m6qwx","srcDir":"","url":"https://github.com/khchen/hashlib/archive/84e0247555e4488594975900401baaf5bbbfb53.tar.gz"},{"method":"fetchzip","packages":["nimcrypto"],"path":"/nix/store/zyr8zwh7vaiycn1s4r8cxwc71f2k5l0h-source","ref":"traditional-api","rev":"602c5d20c69c76137201b5d41f788f72afb95aa8","sha256":"1dmdmgb6b9m5f8dyxk781nnd61dsk3hdxqks7idk9ncnpj9fng65","srcDir":"","url":"https://github.com/cheatfate/nimcrypto/archive/602c5d20c69c76137201b5d41f788f72afb95aa8.tar.gz"},{"method":"fetchzip","packages":["npeg"],"path":"/nix/store/ffkxmjmigfs7zhhiiqm0iw2c34smyciy-source","ref":"1.2.1","rev":"26d62fdc40feb84c6533956dc11d5ee9ea9b6c09","sha256":"0xpzifjkfp49w76qmaylan8q181bs45anmp46l4bwr3lkrr7bpwh","srcDir":"src","url":"https://github.com/zevv/npeg/archive/26d62fdc40feb84c6533956dc11d5ee9ea9b6c09.tar.gz"},{"method":"fetchzip","packages":["preserves"],"path":"/nix/store/nrcpzf9hx70kry3gwhrdzcs3qicjncjh-source","ref":"20231021","rev":"edece399be70818208bf2263c30cb2bcf435bbff","sha256":"0xmw35wmw3a4lja9q4qvlvpxv3xk0hnkjg4fwfw6f3inh6zfiqki","srcDir":"src","url":"https://git.syndicate-lang.org/ehmry/preserves-nim/archive/edece399be70818208bf2263c30cb2bcf435bbff.tar.gz"},{"method":"fetchzip","packages":["syndicate"],"path":"/nix/store/1y3nnpp2mhxqmdb3xh4c4k5k5l9hhqk3-source","ref":"20231019","rev":"57b99b20e7db1b97b1cb9c6df574bd13983c26fc","sha256":"1kgb3a78igs37xkmv8cbaxa17qdjf2h43vdmpda517c9086ggsn5","srcDir":"src","url":"https://git.syndicate-lang.org/ehmry/syndicate-nim/archive/57b99b20e7db1b97b1cb9c6df574bd13983c26fc.tar.gz"}]}
|
|
@ -0,0 +1,39 @@
|
|||
{ config, lib, pkgs, ... }:
|
||||
|
||||
with lib;
|
||||
let
|
||||
programCfg = config.programs.xdg-open-ng;
|
||||
settingsFormat = pkgs.formats.preserves { };
|
||||
prCfgFile = settingsFormat.generate "xdg-open.pr" programCfg.settings;
|
||||
pkg = programCfg.package;
|
||||
in {
|
||||
options.programs.xdg-open-ng = {
|
||||
|
||||
enable = mkEnableOption "xdg-open-ng";
|
||||
|
||||
package = mkOption {
|
||||
type = types.package;
|
||||
default = pkgs.xdg-open-ng;
|
||||
defaultText = literalExpression "pkgs.xdg-open-ng";
|
||||
};
|
||||
|
||||
settings = mkOption {
|
||||
type = settingsFormat.type;
|
||||
default = [ ];
|
||||
defaultText = literalExpression ''
|
||||
[ [ ".*mkv|.*mp4" [ "${pkgs.mpv}/bin/mpv" "$#" ] ]
|
||||
[ "gemini://.*|file:///.*.gmi" [ "${pkgs.kristall}/bin/kristall" "$#" ]; ]
|
||||
[ "tox:.*" [ "${pkgs.qtox}/bin/qtox" "$#" ] ]
|
||||
]
|
||||
'';
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
config = mkIf programCfg.enable {
|
||||
|
||||
environment.systemPackages = [ pkg ];
|
||||
};
|
||||
|
||||
meta.maintainers = with lib.maintainers; [ ehmry ];
|
||||
}
|
15
protocol.prs
15
protocol.prs
|
@ -1,16 +1,9 @@
|
|||
version 1 .
|
||||
|
||||
Authority = [ @username any @password any @host any @port any ] .
|
||||
XdgOpen = <xdg-open @uris [string ...]> .
|
||||
|
||||
; Uniform Resource Identifier https://datatracker.ietf.org/doc/html/rfc3986
|
||||
Uri = <uri @scheme string @auth Authority @path any @query any @fragment any> .
|
||||
Action = [@pat string @cmd [string ...] ] .
|
||||
|
||||
; Broadcast this to open a URI.
|
||||
Open = <open @uri Uri>.
|
||||
Attrs = { actions: [Action ...] } .
|
||||
|
||||
UriRunnerConfig = {
|
||||
handlerspace: #!any
|
||||
urispace: #!any
|
||||
} .
|
||||
|
||||
ActionHandler = <action-handler @pat string @entity #!any @action any> .
|
||||
UriRunnerConfig = <config @attrs Attrs> .
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
{ pkgs ? import <nixpkgs> { } }:
|
||||
|
||||
pkgs.nim2Packages.buildNimPackage {
|
||||
name = "dummy";
|
||||
nativeBuildInputs = [ pkgs.pkg-config ];
|
||||
buildInputs = [ pkgs.pcre ];
|
||||
}
|
|
@ -1,6 +1,2 @@
|
|||
include_rules
|
||||
: foreach ../*.prs |> !preserves_schema_nim |> | {common}
|
||||
: common.nim |> !nim_check |> {common}
|
||||
: uri_runner.nim | $(SYNDICATE_PROTOCOL) {common} |> !nim_bin |> {bin}
|
||||
: open.nim | $(SYNDICATE_PROTOCOL) {common} |> !nim_bin |> {bin}
|
||||
: foreach {bin} |> !assert_built |>
|
||||
: foreach ../*.prs |> !preserves_schema_nim |> %B.nim
|
||||
|
|
|
@ -1,48 +0,0 @@
|
|||
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
||||
# SPDX-License-Identifier: Unlicense
|
||||
|
||||
import std/uri
|
||||
import preserves
|
||||
|
||||
proc toPreserveOrNull(s: string; E: typedesc): Preserve[E] =
|
||||
if s != "": result = toPreserve(s, E)
|
||||
|
||||
proc toPreserveHook*(u: Uri; E: typedesc): Preserve[E] =
|
||||
## Best-effort preservation of the `Uri` type.
|
||||
runnableExamples:
|
||||
from std/unittest import check
|
||||
import std/uri
|
||||
import preserves
|
||||
var u = parseUri"https://bidstonobservatory.org"
|
||||
check $(u.toPreserve) == """<uri "https" [#f #f "bidstonobservatory.org" #f] #f #f #f>"""
|
||||
initRecord("uri",
|
||||
u.scheme.toPreserve(E),
|
||||
toPreserve([
|
||||
u.username.toPreserveOrNull(E),
|
||||
u.password.toPreserveOrNull(E),
|
||||
u.hostname.toPreserveOrNull(E),
|
||||
u.port.toPreserveOrNull(E),
|
||||
]),
|
||||
u.path.toPreserveOrNull(E),
|
||||
u.query.toPreserveOrNull(E),
|
||||
u.anchor.toPreserveOrNull(E),
|
||||
)
|
||||
|
||||
proc fromPreserveHook*[E](u: var Uri; pr: Preserve[E]): bool =
|
||||
## Best-effort preservation of the `Uri` type.
|
||||
runnableExamples:
|
||||
import std/uri
|
||||
import preserves
|
||||
var u: Uri
|
||||
doAssert fromPreserveHook(u, parsePreserves"""<uri "https" [#f #f "bidstonobservatory.org" #f] #f #f #f>""")
|
||||
if pr.isRecord("uri", 5):
|
||||
result = fromPreserve(u.scheme, pr[0])
|
||||
if result:
|
||||
if pr[1].isSequence and pr[1].sequence.len == 4:
|
||||
discard fromPreserve(u.username, pr[1][0])
|
||||
discard fromPreserve(u.password, pr[1][1])
|
||||
discard fromPreserve(u.hostname, pr[1][2])
|
||||
discard fromPreserve(u.port, pr[1][3])
|
||||
discard fromPreserve(u.path, pr[2])
|
||||
discard fromPreserve(u.query, pr[3])
|
||||
discard fromPreserve(u.anchor, pr[4])
|
25
src/open.nim
25
src/open.nim
|
@ -1,25 +0,0 @@
|
|||
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
||||
# SPDX-License-Identifier: Unlicense
|
||||
|
||||
import std/[asyncdispatch, os, uri]
|
||||
import preserves, syndicate, syndicate/relays, syndicate/capabilities
|
||||
import ./protocol, ./common
|
||||
|
||||
proc publishUri(turn: var Turn; ds: Cap) =
|
||||
for arg in commandLineParams():
|
||||
if fileExists(arg):
|
||||
message(turn, ds, Open(uri: protocol.Uri(
|
||||
scheme: "file", path: arg.absolutePath.toPreserve)))
|
||||
else:
|
||||
message(turn, ds, initRecord("open", arg.parseUri.toPreserve))
|
||||
stop(turn)
|
||||
|
||||
proc main =
|
||||
let route = envRoute()
|
||||
discard bootDataspace("open") do (root: Cap; turn: var Turn):
|
||||
resolve(turn, root, route, publishUri)
|
||||
|
||||
for i in 0..4: poll(20)
|
||||
# A hack to exit
|
||||
|
||||
main()
|
|
@ -1,36 +1,29 @@
|
|||
|
||||
import
|
||||
preserves
|
||||
std/typetraits, preserves
|
||||
|
||||
type
|
||||
Open* {.preservesRecord: "open".} = object
|
||||
`uri`*: Uri
|
||||
XdgOpen* {.preservesRecord: "xdg-open".} = object
|
||||
`data`*: seq[string]
|
||||
|
||||
UriRunnerConfig* {.preservesDictionary.} = object
|
||||
`handlerspace`* {.preservesEmbedded.}: Preserve[void]
|
||||
`urispace`* {.preservesEmbedded.}: Preserve[void]
|
||||
Attrs*[E] {.preservesDictionary.} = ref object
|
||||
`actions`*: seq[Action]
|
||||
|
||||
Authority* {.preservesTuple.} = object
|
||||
`username`*: Preserve[void]
|
||||
`password`*: Preserve[void]
|
||||
`host`*: Preserve[void]
|
||||
`port`*: Preserve[void]
|
||||
UriRunnerConfig*[E] {.preservesRecord: "config".} = ref object
|
||||
`attrs`*: Attrs[E]
|
||||
|
||||
Uri* {.preservesRecord: "uri".} = object
|
||||
`scheme`*: string
|
||||
`auth`*: Authority
|
||||
`path`*: Preserve[void]
|
||||
`query`*: Preserve[void]
|
||||
`fragment`*: Preserve[void]
|
||||
|
||||
ActionHandler* {.preservesRecord: "action-handler".} = object
|
||||
Action* {.preservesTuple.} = object
|
||||
`pat`*: string
|
||||
`entity`* {.preservesEmbedded.}: Preserve[void]
|
||||
`action`*: Preserve[void]
|
||||
`cmd`*: seq[string]
|
||||
|
||||
proc `$`*(x: Open | UriRunnerConfig | Authority | Uri | ActionHandler): string =
|
||||
proc `$`*[E](x: Attrs[E] | UriRunnerConfig[E]): string =
|
||||
`$`(toPreserve(x, E))
|
||||
|
||||
proc encode*[E](x: Attrs[E] | UriRunnerConfig[E]): seq[byte] =
|
||||
encode(toPreserve(x, E))
|
||||
|
||||
proc `$`*(x: XdgOpen | Action): string =
|
||||
`$`(toPreserve(x))
|
||||
|
||||
proc encode*(x: Open | UriRunnerConfig | Authority | Uri | ActionHandler): seq[
|
||||
byte] =
|
||||
proc encode*(x: XdgOpen | Action): seq[byte] =
|
||||
encode(toPreserve(x))
|
||||
|
|
|
@ -1,49 +1,57 @@
|
|||
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
||||
# SPDX-FileCopyrightText: ☭ 2021 Emery Hemingway
|
||||
# SPDX-License-Identifier: Unlicense
|
||||
|
||||
import std/[re, tables, uri]
|
||||
import preserves, syndicate, syndicate/relays, syndicate/patterns
|
||||
import ./protocol, ./common
|
||||
import std/[asyncdispatch, deques, re, streams, strutils, os, osproc]
|
||||
import preserves
|
||||
import syndicate, syndicate/capabilities
|
||||
import ./protocol
|
||||
|
||||
# importing std/re doesn't actually link against PCRE
|
||||
{.passC: staticExec("pkg-config --cflags libpcre").}
|
||||
{.passL: staticExec("pkg-config --libs libpcre").}
|
||||
proc unixSocketPath: string =
|
||||
result = getEnv("SYNDICATE_SOCK")
|
||||
if result == "":
|
||||
result = getEnv("XDG_RUNTIME_DIR", "/run/user/1000") / "dataspace"
|
||||
|
||||
type RegexAction = tuple[regex: Regex, entity: Cap, action: Assertion]
|
||||
proc mintCap: SturdyRef =
|
||||
var key: array[16, byte]
|
||||
mint(key, "syndicate")
|
||||
|
||||
proc rewrite(result: var Assertion; uri: string; regex: Regex) {.gcsafe.} =
|
||||
proc op(pr: var Assertion) {.gcsafe.} =
|
||||
if pr.isString:
|
||||
pr.string = replacef(uri, regex, pr.string)
|
||||
apply(result, op)
|
||||
bootDataspace("main") do (ds: Ref; turn: var Turn):
|
||||
var
|
||||
actions: seq[tuple[regex: Regex; cmd: string; args: seq[string]]]
|
||||
children: Deque[Process]
|
||||
|
||||
runActor("main") do (root: Cap; turn: var Turn):
|
||||
connectStdio(turn, root)
|
||||
let handlers = newTable[Handle, RegexAction]()
|
||||
during(turn, root, ?UriRunnerConfig) do (handlerspace: Cap, urispace: Cap):
|
||||
connectStdio(ds, turn)
|
||||
|
||||
during(turn, handlerspace, dropType(ActionHandler)) do:
|
||||
onPublish(turn, ds, ?UriRunnerConfig[Ref]) do (cfg: seq[Action]):
|
||||
actions.setLen 0
|
||||
for act in cfg:
|
||||
if act.cmd.len < 2:
|
||||
stderr.writeLine "ignoring ", act
|
||||
else:
|
||||
actions.add (re(act.pat, {reIgnoreCase, reStudy}), act.cmd[0], act.cmd[1..act.cmd.high])
|
||||
stderr.writeLine "actions updated"
|
||||
|
||||
during(turn, handlerspace, ?ActionHandler) do (pat: string; entity: Cap; 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, Open ? {0:grab()}) do (u: uri.Uri):
|
||||
let uri = $u
|
||||
assert len(handlers) > 0
|
||||
var matched = false
|
||||
for handler in handlers.values:
|
||||
if match(uri, handler.regex):
|
||||
connectUnix(turn, unixSocketPath(), mintCap()) do (turn: var Turn; a: Assertion) -> TurnAction:
|
||||
let ds = unembed a
|
||||
onMessage(turn, ds, ?XdgOpen) do (uris: seq[string]):
|
||||
while children.len > 0 and not children.peekFirst.running:
|
||||
var child = children.popFirst()
|
||||
if child.peekExitCode != 0:
|
||||
stderr.writeLine child.errorStream.readAll
|
||||
close child
|
||||
# TODO check children on a timer?
|
||||
for uri in uris:
|
||||
var matched: bool
|
||||
for act in actions:
|
||||
if match(uri, act.regex):
|
||||
matched = true
|
||||
var action = handler.action
|
||||
try:
|
||||
rewrite(action, uri, handler.regex)
|
||||
message(turn, handler.entity, action)
|
||||
except CatchableError:
|
||||
stderr.writeLine "rewrite failed on ", action
|
||||
var args = newSeq[string](act.args.len)
|
||||
for i, arg in act.args:
|
||||
args[i] = replace(arg, "\\1", uri)
|
||||
var child = startProcess(
|
||||
command = act.cmd, args = args, options = {})
|
||||
children.addLast child
|
||||
if not matched:
|
||||
stderr.writeLine "no actions matched for ", uri
|
||||
do:
|
||||
clear(handlers)
|
||||
stderr.writeLine "no patterns matched for ", uri
|
||||
|
||||
runForever()
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
# SPDX-FileCopyrightText: ☭ 2021 Emery Hemingway
|
||||
# SPDX-License-Identifier: Unlicense
|
||||
|
||||
import std/[asyncdispatch, os]
|
||||
import preserves
|
||||
import syndicate, syndicate/capabilities
|
||||
import ./protocol
|
||||
|
||||
proc unixSocketPath: string =
|
||||
result = getEnv("SYNDICATE_SOCK")
|
||||
if result == "":
|
||||
result = getEnv("XDG_RUNTIME_DIR", "/run/user/1000") / "dataspace"
|
||||
|
||||
proc mintCap: SturdyRef =
|
||||
var key: array[16, byte]
|
||||
mint(key, "syndicate")
|
||||
|
||||
type XdgOpen {.preservesRecord: "xdg-open".} = object
|
||||
args: seq[string]
|
||||
|
||||
bootDataspace("main") do (ds: Ref; turn: var Turn):
|
||||
let mainFacet = turn.facet
|
||||
connectUnix(turn, unixSocketPath(), mintCap()) do (turn: var Turn; a: Assertion) -> TurnAction:
|
||||
let ds = unembed a
|
||||
message(turn, ds, XdgOpen(args: commandLineParams()))
|
||||
stop(turn, mainFacet)
|
||||
|
||||
for i in 0..7: poll(20)
|
||||
# A hack to exit
|
|
@ -1,13 +0,0 @@
|
|||
# Package
|
||||
|
||||
version = "20231021"
|
||||
author = "Emery"
|
||||
description = "Syndicated open command"
|
||||
license = "Unlicense"
|
||||
srcDir = "src"
|
||||
bin = @[ "open", "uri_runner"]
|
||||
|
||||
|
||||
# Dependencies
|
||||
|
||||
requires "nim >= 1.6.4", "syndicate >= 20231021"
|
|
@ -0,0 +1,12 @@
|
|||
let
|
||||
syndicate = builtins.getFlake "syndicate";
|
||||
nixpkgs = builtins.getFlake "nixpkgs";
|
||||
pkgs = nixpkgs.legacyPackages.x86_64-linux;
|
||||
|
||||
toPreserves = syndicate.lib.generators.toPreserves { };
|
||||
|
||||
kristall = "${pkgs.kristall}/bin/kristall";
|
||||
|
||||
cfg = { "gemini://.*|file:///.*.gmi'" = [ kristall ]; };
|
||||
|
||||
in pkgs.writeText "cfg.pr" (toPreserves cfg)
|
|
@ -1,28 +1,17 @@
|
|||
; 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 }
|
||||
}>
|
||||
]
|
||||
|
||||
; grab a dataspace for observing <open …> messages
|
||||
? <socketspace ?socketspace> [
|
||||
|
||||
; log open messages
|
||||
$socketspace ?? <open ?stuff> [
|
||||
$log ! <log "-" { line: <open $stuff> }>
|
||||
]
|
||||
|
||||
; configure the uri_runner
|
||||
? <service-object <daemon uri_runner> ?cap> [
|
||||
$cap {
|
||||
; watch the config dataspace for handler configuration
|
||||
handlerspace: $config
|
||||
urispace: $socketspace
|
||||
}
|
||||
]
|
||||
|
||||
<require-service <daemon uri_runner>>
|
||||
|
||||
<daemon uri_runner {
|
||||
argv: ["/home/repo/syndicate/xdg_open_ng/src/uri_runner"]
|
||||
dir: "/tmp"
|
||||
protocol: text/syndicate
|
||||
clearEnv: #f
|
||||
}>
|
||||
|
||||
? <service-object <daemon uri_runner> ?cap> [
|
||||
$cap <config { actions: [
|
||||
["gemini://.*|file:///.*.gmi", ["/run/current-system/sw/bin/kristall" "\\1"]]
|
||||
["http://.*|https://.*|.*html", ["/run/current-system/sw/bin/firefox" "\\1"]]
|
||||
["tox:.*|uri:tox:.*", ["/run/current-system/sw/bin/qtox" "\\1"]]
|
||||
[".*mkv", ["/run/current-system/sw/bin/mpv" "\\1"]]
|
||||
] } >
|
||||
]
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
# Package
|
||||
|
||||
version = "0.1.0"
|
||||
author = "Emery"
|
||||
description = "A better xdg-open"
|
||||
license = "Unlicense"
|
||||
srcDir = "src"
|
||||
bin = @[ "uri_runner", "xdg_open"]
|
||||
|
||||
|
||||
# Dependencies
|
||||
|
||||
requires "nim >= 1.6.4", "syndicate"
|
Loading…
Reference in New Issue