package main import ( "flag" "fmt" "net/url" "os/exec" "regexp" ) // 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 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 } func handleURL(browserRules []BrowserRule, urlString string) error { dest, err := url.Parse(urlString) if err != nil { return fmt.Errorf("failed to parse url: %s, %w", urlString, err) } var matched bool for _, rule := range browserRules { matched, err = rule.MaybeLaunch(*dest) if err != nil { return fmt.Errorf("failed launching browser: %w", err) } if matched { break } } return nil } 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() // 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() // For each url, run matcher for _, urlString := range urls { err := handleURL(browserRules, urlString) if err != nil { panic(err) } } }