package main import ( "fmt" "net/url" "os/exec" "regexp" "github.com/antonmedv/expr" ) // MatchFunc is a signature for a function to match a URL type MatchFunc = func(url.URL) bool // A BrowserRule is a rule that will launch a browser, if matched type BrowserRule struct { matcher MatchFunc command string args []string } // IsMatched will check to see the rule matches the provied URL func (r BrowserRule) IsMatched(dest url.URL) bool { return r.matcher(dest) } // Launch will launch the browser with the provided URL func (r BrowserRule) Launch(dest url.URL) error { args := append(r.args, "--", dest.String()) cmd := exec.Command(r.command, args...) return cmd.Start() } // MaybeLaunch will lauch the browser with the provided URL if it matches the rule func (r BrowserRule) MaybeLaunch(dest url.URL) (bool, error) { if r.IsMatched(dest) { return true, r.Launch(dest) } return false, nil } func NewBrowserRule(command string, args []string, matcher MatchFunc) BrowserRule { return BrowserRule{ command: command, args: args, matcher: matcher, } } func NewBrowserRuleFromExpr(command string, args []string, matcherExpr string) BrowserRule { return NewBrowserRule( command, args, MakeMatchFunc(matcherExpr), ) } func MakeMatchFunc(rule string) MatchFunc { return func(dest url.URL) bool { env := map[string]interface{}{ // Prebuilt match functions "matchAny": matchAny, "matchHostRegexp": matchHostRegexp, "matchHostname": matchHostname, "matchNever": matchNever, "matchRegexp": matchRegexp, // Helpers for building custom matchers "MatchFunc": func(foundMatch bool) MatchFunc { return func(_ url.URL) bool { return foundMatch } }, "fullUrl": dest.String(), "hostname": dest.Hostname(), "url": dest, } matchFuncExpr, err := expr.Eval(rule, env) if err != nil { fmt.Printf("Error evaluating rule %s: %v", rule, err) return false } matchFunc, ok := matchFuncExpr.(MatchFunc) if !ok { fmt.Printf("Error evaluating rule %s. Did not evaluate to a MatchFunc.", rule) return false } return matchFunc(dest) } } func matchHostname(hostnames ...string) MatchFunc { return func(dest url.URL) bool { for _, host := range hostnames { if host == dest.Hostname() { return true } } return false } } func matchHostRegexp(hostRegexp ...string) MatchFunc { matchers := []*regexp.Regexp{} for _, s := range hostRegexp { matchers = append(matchers, regexp.MustCompile(s)) } return func(dest url.URL) bool { for _, matcher := range matchers { if matcher.MatchString(dest.Hostname()) { return true } } return false } } func matchRegexp(urlRegexp ...string) MatchFunc { matchers := []*regexp.Regexp{} for _, s := range urlRegexp { matchers = append(matchers, regexp.MustCompile(s)) } return func(dest url.URL) bool { for _, matcher := range matchers { if matcher.MatchString(dest.String()) { return true } } return false } } func matchAny(matchFuncs ...MatchFunc) MatchFunc { return func(dest url.URL) bool { for _, f := range matchFuncs { if f(dest) { return true } } return false } } // Always returns true func defaultBrowser(dest url.URL) bool { return true } // Never returns true func matchNever(dest url.URL) bool { return false }