You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
118 lines
2.7 KiB
118 lines
2.7 KiB
package main |
|
|
|
import ( |
|
"fmt" |
|
"net/url" |
|
"os" |
|
"path/filepath" |
|
|
|
"github.com/antonmedv/expr" |
|
"github.com/hashicorp/hcl/v2/hclsimple" |
|
) |
|
|
|
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 |
|
type Config struct { |
|
DefaultBrowserCommand []string `hcl:"default_browser_cmd"` |
|
Rules []BrowserRuleConfig `hcl:"rule,block"` |
|
} |
|
|
|
// BrowserRuleConfig represents a BrowserRule |
|
type BrowserRuleConfig struct { |
|
Name string `hcl:"name,label"` |
|
BrowserCommand []string `hcl:"browser_cmd"` |
|
MatchExpr string `hcl:"match"` |
|
} |
|
|
|
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 LoadConfig(path string) ([]BrowserRule, error) { |
|
rules := []BrowserRule{} |
|
var config Config |
|
err := hclsimple.DecodeFile(path, nil, &config) |
|
if err != nil { |
|
return nil, err |
|
} |
|
|
|
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, |
|
}) |
|
|
|
return rules, nil |
|
} |
|
|
|
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 |
|
} |
|
} |
|
|
|
dir, err = os.UserHomeDir() |
|
if err != nil { |
|
return "", fmt.Errorf("Could not find a config dir or home dir to look for config file. Specify one with -config: %w", err) |
|
} |
|
|
|
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") |
|
}
|
|
|