WIP: Refactor to read from an hcl config file and expr rules
Need to write some documentation
This commit is contained in:
parent
6d8fcee025
commit
a546764845
2
Makefile
2
Makefile
@ -8,7 +8,7 @@ default: test
|
|||||||
|
|
||||||
.PHONY: test
|
.PHONY: test
|
||||||
test: $(BIN)
|
test: $(BIN)
|
||||||
./$(BIN) https://duck.com/
|
./$(BIN) -config ./sample-config.hcl https://duck.com/
|
||||||
|
|
||||||
$(BIN): *.go
|
$(BIN): *.go
|
||||||
go build .
|
go build .
|
||||||
|
173
config.go
173
config.go
@ -1,129 +1,108 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/antonmedv/expr"
|
||||||
|
"github.com/hashicorp/hcl/v2/hclsimple"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO: This is a total work in progress. It generally works execept it's ugly and regex strings aren't parsed correctly
|
func fileExists(path string) bool {
|
||||||
|
if _, err := os.Stat(path); os.IsNotExist(err) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// Config is the full set of configuration required
|
// Config is the full set of configuration required
|
||||||
type Config struct {
|
type Config struct {
|
||||||
DefaultBrowserCommand []string
|
DefaultBrowserCommand []string `hcl:"default_browser_cmd"`
|
||||||
Rules []BrowserRuleConfig
|
Rules []BrowserRuleConfig `hcl:"rule,block"`
|
||||||
}
|
|
||||||
|
|
||||||
func (r *BrowserRule) UnmarshalJSON(data []byte) error {
|
|
||||||
var values struct {
|
|
||||||
command string
|
|
||||||
args []string
|
|
||||||
matchHostname []string
|
|
||||||
matchHostRegexp []string
|
|
||||||
matchRegexp []string
|
|
||||||
}
|
|
||||||
if err := json.Unmarshal(data, &values); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// BrowserRuleConfig represents a BrowserRule
|
// BrowserRuleConfig represents a BrowserRule
|
||||||
type BrowserRuleConfig struct {
|
type BrowserRuleConfig struct {
|
||||||
BrowserCommand []string
|
Name string `hcl:"name,label"`
|
||||||
Matchers []MatcherConfig
|
BrowserCommand []string `hcl:"browser_cmd"`
|
||||||
|
MatchExpr string `hcl:"match"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// MatcherConfig represents a single MatchFunc
|
func MakeMatchFunc(rule string) MatchFunc {
|
||||||
type MatcherConfig struct {
|
return func(dest url.URL) bool {
|
||||||
Kind string
|
env := map[string]interface{}{
|
||||||
Targets []string
|
"matchHostname": matchHostname,
|
||||||
|
"matchHostRegexp": matchHostRegexp,
|
||||||
|
"matchRegexp": matchRegexp,
|
||||||
|
"matchAny": matchAny,
|
||||||
|
// Building custom matchers
|
||||||
|
"MatchFunc": func(foundMatch bool) MatchFunc {
|
||||||
|
return func(_ url.URL) bool {
|
||||||
|
return foundMatch
|
||||||
}
|
}
|
||||||
|
},
|
||||||
func parseConfig(jsonBytes []byte) (Config, error) {
|
"hostname": dest.Hostname(),
|
||||||
var c Config
|
"url": dest.String(),
|
||||||
err := json.Unmarshal(jsonBytes, &c)
|
|
||||||
return c, err
|
|
||||||
}
|
}
|
||||||
|
matchFunc, err := expr.Eval(rule, env)
|
||||||
// GetRules returns all rules parsed from a JSON string
|
|
||||||
func GetRules(jsonBytes []byte) (rules []BrowserRule, err error) {
|
|
||||||
c, err := parseConfig(jsonBytes)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, rule := range c.Rules {
|
return matchFunc.(MatchFunc)(dest)
|
||||||
if len(rule.BrowserCommand) < 1 {
|
|
||||||
err = fmt.Errorf("Must provide a browser command")
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
browserCommand := rule.BrowserCommand[0]
|
|
||||||
args := rule.BrowserCommand[1:]
|
|
||||||
|
|
||||||
matchers := []MatchFunc{}
|
|
||||||
for _, matcher := range rule.Matchers {
|
|
||||||
switch matcher.Kind {
|
|
||||||
case "matchHostname":
|
|
||||||
fmt.Println("Match hostname for ", matcher.Targets)
|
|
||||||
matchers = append(matchers, matchHostname(matcher.Targets...))
|
|
||||||
case "matchHostRegexp":
|
|
||||||
matchers = append(matchers, matchHostRegexp(matcher.Targets...))
|
|
||||||
case "matchRegexp":
|
|
||||||
matchers = append(matchers, matchRegexp(matcher.Targets...))
|
|
||||||
default:
|
|
||||||
err = fmt.Errorf("Unknown matcher kind %s", matcher.Kind)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var matcher MatchFunc
|
|
||||||
if len(matchers) == 1 {
|
|
||||||
matcher = matchers[0]
|
|
||||||
} else {
|
|
||||||
matcher = matchAny(matchers...)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rules = append(
|
func LoadConfig(path string) ([]BrowserRule, error) {
|
||||||
rules,
|
rules := []BrowserRule{}
|
||||||
BrowserRule{
|
var config Config
|
||||||
matcher: matcher,
|
err := hclsimple.DecodeFile(path, nil, &config)
|
||||||
command: browserCommand,
|
if err != nil {
|
||||||
args: args,
|
return nil, err
|
||||||
},
|
}
|
||||||
BrowserRule{
|
|
||||||
|
for _, rule := range config.Rules {
|
||||||
|
rules = append(rules, BrowserRule{
|
||||||
|
command: rule.BrowserCommand[0],
|
||||||
|
args: rule.BrowserCommand[1:],
|
||||||
|
matcher: MakeMatchFunc(rule.MatchExpr),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add default browser rule to the end
|
||||||
|
rules = append(rules, BrowserRule{
|
||||||
|
command: config.DefaultBrowserCommand[0],
|
||||||
|
args: config.DefaultBrowserCommand[1:],
|
||||||
matcher: defaultBrowser,
|
matcher: defaultBrowser,
|
||||||
command: c.DefaultBrowserCommand[0],
|
})
|
||||||
args: c.DefaultBrowserCommand[1:],
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
|
return rules, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
func DefaultConfigPath() (string, error) {
|
||||||
|
appName := "browser-ruler"
|
||||||
|
|
||||||
|
// Check XDG_CONFIG_HOME
|
||||||
|
dir, err := os.UserConfigDir()
|
||||||
|
if err == nil {
|
||||||
|
configPath := filepath.Join(dir, appName, "config.hcl")
|
||||||
|
if fileExists(configPath) {
|
||||||
|
return configPath, nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetTestRules() ([]BrowserRule, error) {
|
dir, err = os.UserHomeDir()
|
||||||
jsonBytes := []byte(`{
|
if err != nil {
|
||||||
"DefaultBrowserCommand": ["chromium-browser"],
|
return "", fmt.Errorf("Could not find a config dir or home dir to look for config file. Specify one with -config: %w", err)
|
||||||
"Rules": [
|
|
||||||
{
|
|
||||||
"BrowserCommand": ["firefox"],
|
|
||||||
"Matchers": [
|
|
||||||
{
|
|
||||||
"Kind": "matchHostname",
|
|
||||||
"Targets": [
|
|
||||||
"iamthefij.com"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Kind": "matchHostRegexp",
|
|
||||||
"Targets": [
|
|
||||||
".*\.iamthefij\.com$",
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
]
|
|
||||||
|
configPath := filepath.Join(dir, "."+appName+".hcl")
|
||||||
|
if fileExists(configPath) {
|
||||||
|
return configPath, nil
|
||||||
}
|
}
|
||||||
]
|
|
||||||
}`)
|
return "", fmt.Errorf("no config file found in config dir or home dir")
|
||||||
return GetRules(jsonBytes)
|
|
||||||
}
|
}
|
||||||
|
5
go.mod
5
go.mod
@ -1,3 +1,8 @@
|
|||||||
module git.iamthefij.com/iamthefij/browser-ruler
|
module git.iamthefij.com/iamthefij/browser-ruler
|
||||||
|
|
||||||
go 1.16
|
go 1.16
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/antonmedv/expr v1.9.0 // indirect
|
||||||
|
github.com/hashicorp/hcl/v2 v2.11.1 // indirect
|
||||||
|
)
|
||||||
|
73
go.sum
Normal file
73
go.sum
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
|
||||||
|
github.com/agext/levenshtein v1.2.1 h1:QmvMAjj2aEICytGiWzmxoE0x2KZvE0fvmqMOfy2tjT8=
|
||||||
|
github.com/agext/levenshtein v1.2.1/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=
|
||||||
|
github.com/antonmedv/expr v1.9.0 h1:j4HI3NHEdgDnN9p6oI6Ndr0G5QryMY0FNxT4ONrFDGU=
|
||||||
|
github.com/antonmedv/expr v1.9.0/go.mod h1:5qsM3oLGDND7sDmQGDXHkYfkjYMUX14qsgqmHhwGEk8=
|
||||||
|
github.com/apparentlymart/go-dump v0.0.0-20180507223929-23540a00eaa3/go.mod h1:oL81AME2rN47vu18xqj1S1jPIPuN7afo62yKTNn3XMM=
|
||||||
|
github.com/apparentlymart/go-textseg v1.0.0 h1:rRmlIsPEEhUTIKQb7T++Nz/A5Q6C9IuX2wFoYVvnCs0=
|
||||||
|
github.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/Nj9VFpLOpjS5yuumk=
|
||||||
|
github.com/apparentlymart/go-textseg/v13 v13.0.0 h1:Y+KvPE1NYz0xl601PVImeQfFyEy6iT90AvPUL1NNfNw=
|
||||||
|
github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo=
|
||||||
|
github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
|
||||||
|
github.com/gdamore/tcell v1.3.0/go.mod h1:Hjvr+Ofd+gLglo7RYKxxnzCBmev3BzsS67MebKS4zMM=
|
||||||
|
github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
|
||||||
|
github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||||
|
github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg=
|
||||||
|
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
|
github.com/hashicorp/hcl/v2 v2.11.1 h1:yTyWcXcm9XB0TEkyU/JCRU6rYy4K+mgLtzn2wlrJbcc=
|
||||||
|
github.com/hashicorp/hcl/v2 v2.11.1/go.mod h1:FwWsfWEjyV/CMj8s/gqAuiviY72rJ1/oayI9WftqcKg=
|
||||||
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
|
github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k=
|
||||||
|
github.com/lucasb-eyer/go-colorful v1.0.2/go.mod h1:0MS4r+7BZKSJ5mw4/S5MPN+qHFF1fYclkSPilDOKW0s=
|
||||||
|
github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||||
|
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||||
|
github.com/mattn/go-runewidth v0.0.8/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||||
|
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 h1:DpOJ2HYzCv8LZP15IdmG+YdwD2luVPHITV96TkirNBM=
|
||||||
|
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
|
||||||
|
github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/rivo/tview v0.0.0-20200219210816-cd38d7432498/go.mod h1:6lkG1x+13OShEf0EaOCaTQYyB7d5nSbb181KtjlS+84=
|
||||||
|
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||||
|
github.com/sanity-io/litter v1.2.0/go.mod h1:JF6pZUFgu2Q0sBZ+HSV35P8TVPI1TTzEwyu9FXAw2W4=
|
||||||
|
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||||
|
github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/testify v0.0.0-20161117074351-18a02ba4a312/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
|
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||||
|
github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=
|
||||||
|
github.com/vmihailenco/msgpack/v4 v4.3.12/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4=
|
||||||
|
github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI=
|
||||||
|
github.com/zclconf/go-cty v1.2.0/go.mod h1:hOPWgoHbaTUnI5k4D2ld+GRpFJSCe6bCM7m1q/N4PQ8=
|
||||||
|
github.com/zclconf/go-cty v1.8.0 h1:s4AvqaeQzJIu3ndv4gVIhplVD0krU+bgrcLSVUnaWuA=
|
||||||
|
github.com/zclconf/go-cty v1.8.0/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk=
|
||||||
|
github.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b/go.mod h1:ZRKQfBXbGkpdV6QMzT3rU1kSTAnfu1dO8dPKjYprgj8=
|
||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||||
|
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190502175342-a43fa875dd82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
|
golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ=
|
||||||
|
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||||
|
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
27
main.go
27
main.go
@ -100,14 +100,14 @@ func noop(dest url.URL) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleURL(urlString string) error {
|
func handleURL(browserRules []BrowserRule, urlString string) error {
|
||||||
dest, err := url.Parse(urlString)
|
dest, err := url.Parse(urlString)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to parse url: %s, %w", urlString, err)
|
return fmt.Errorf("failed to parse url: %s, %w", urlString, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var matched bool
|
var matched bool
|
||||||
for _, rule := range rules() {
|
for _, rule := range browserRules {
|
||||||
matched, err = rule.MaybeLaunch(*dest)
|
matched, err = rule.MaybeLaunch(*dest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed launching browser: %w", err)
|
return fmt.Errorf("failed launching browser: %w", err)
|
||||||
@ -122,11 +122,32 @@ func handleURL(urlString string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
configPath := flag.String("config", "", "Path to config file, otherwise it will default to $XDG_CONFIG_HOME/browser-ruler/config.hcl or $HOME/.browser-ruler.hcl")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
|
// Read config path from option or find default
|
||||||
|
if *configPath == "" {
|
||||||
|
defaultConfigPath, err := DefaultConfigPath()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Failed to get a path to any config files")
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
configPath = &defaultConfigPath
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read rules from config path
|
||||||
|
browserRules, err := LoadConfig(*configPath)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Could not read config from path %s\n", *configPath)
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get urls from args
|
||||||
urls := flag.Args()
|
urls := flag.Args()
|
||||||
|
|
||||||
|
// For each url, run matcher
|
||||||
for _, urlString := range urls {
|
for _, urlString := range urls {
|
||||||
err := handleURL(urlString)
|
err := handleURL(browserRules, urlString)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
54
rules.go
54
rules.go
@ -1,54 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
// Update this file with your rules
|
|
||||||
// You can either define a BrowserRule directly, or using one of the
|
|
||||||
// pre-defined browsers in browsers.go
|
|
||||||
|
|
||||||
func rules() []BrowserRule {
|
|
||||||
return []BrowserRule{
|
|
||||||
// Personal domains
|
|
||||||
Firefox(
|
|
||||||
matchHostRegexp(
|
|
||||||
`.*\.iamthefij\.com$`,
|
|
||||||
`.*\.thefij\.rocks$`,
|
|
||||||
`.*\.thefij$`,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
// Work domains
|
|
||||||
ChromiumFlatpak(
|
|
||||||
matchAny(
|
|
||||||
matchHostname(
|
|
||||||
"app.signalfx.com",
|
|
||||||
"lever.co",
|
|
||||||
"work.grubhub.com",
|
|
||||||
"y",
|
|
||||||
"yelp.rimeto.io",
|
|
||||||
"yelp.slack.com",
|
|
||||||
"yelplove.appspot.com",
|
|
||||||
"yelp-shootie.appspot.com",
|
|
||||||
),
|
|
||||||
matchHostRegexp(
|
|
||||||
`.*\.lifesize\.com$`,
|
|
||||||
`.*\.myworkday\.com$`,
|
|
||||||
`.*\.salesforce\.com$`,
|
|
||||||
`.*\.yelpcorp\.com$`,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
// Googly domains
|
|
||||||
ChromiumFlatpak(
|
|
||||||
matchAny(
|
|
||||||
matchHostname(
|
|
||||||
"google.com",
|
|
||||||
"youtube.com",
|
|
||||||
),
|
|
||||||
matchHostRegexp(
|
|
||||||
`.*\.google\.com$`,
|
|
||||||
`.*\.youtube\.com$`,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
// Default fallback browser
|
|
||||||
Firefox(defaultBrowser),
|
|
||||||
}
|
|
||||||
}
|
|
39
sample-config.hcl
Normal file
39
sample-config.hcl
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
default_browser_cmd = ["firefox"]
|
||||||
|
|
||||||
|
# The rule label doesn't matter
|
||||||
|
rule "Flatpak Chromium" {
|
||||||
|
# Command and args, like with default
|
||||||
|
browser_cmd = ["flatpak", "run", "org.chromium.Chromium"]
|
||||||
|
# match evaluates an expr that should result in a MatchFunc where the signature
|
||||||
|
# is MatchFunc = func(url.URL) bool
|
||||||
|
# There are several functions provided that contain common rules and help construct this.
|
||||||
|
# provided are:
|
||||||
|
# matchHostname(...string) MatchFunc
|
||||||
|
# matchHostRegexp(...string) MatchFunc
|
||||||
|
# matchRegexp(...string) MatchFunc
|
||||||
|
# matchAny(...MatchFunc) MatchFunc
|
||||||
|
#
|
||||||
|
# For writing custom rules using expr, you can use the following two variables:
|
||||||
|
# hostname: the URL hostname
|
||||||
|
# url: the full string of the URL
|
||||||
|
#
|
||||||
|
# When using those, you can turn them into a MatchFunc using
|
||||||
|
# MatchFunc(bool) MatchFunc
|
||||||
|
#
|
||||||
|
# For example:
|
||||||
|
# MatchFunc(hostname endsWith "example.com")
|
||||||
|
# is equivalent to:
|
||||||
|
# matchHostRegexp(".*\\example\\.com$")
|
||||||
|
match = "matchHostname('google.com', 'youtube.com')"
|
||||||
|
}
|
||||||
|
|
||||||
|
rule "Firefox Private" {
|
||||||
|
browser_cmd = ["firefox", "--private-window"]
|
||||||
|
# For more complex rules, rules can span multiple lines using EOF syntax
|
||||||
|
match = <<EOF
|
||||||
|
matchAny(
|
||||||
|
matchHostname('facebook.com'),
|
||||||
|
MatchFunc(hostname == 'instagram.com')
|
||||||
|
)
|
||||||
|
EOF
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user