package main import ( "encoding" "encoding/base64" "encoding/json" "log" "net/url" "os" "regexp" ) type userToken struct { Hash []byte `json:"hash"` Salt []byte `json:"salt"` } type jsonURL struct{ v url.URL } func (u *jsonURL) UnmarshalText(v []byte) error { parsed, err := url.Parse(string(v)) if err != nil { return err } u.v = *parsed return nil } var _ encoding.TextUnmarshaler = new(jsonURL) type config struct { CalendarURL jsonURL `json:"calendar_url"` Ignore ignoreRules `json:"ignore"` Port string `json:"port"` UserTokens map[string]userToken `json:"user_tokens"` } type ignoreRules struct { LocationRegexes []regexp.Regexp `json:"location_regexes"` SummaryRegexes []regexp.Regexp `json:"summary_regexes"` } func printConfig(cfg *config) { b64 := base64.StdEncoding log.Print("Loaded configuration: ") log.Print(" Calendar URL: ", cfg.CalendarURL.v.String()) log.Print(" HTTP Port: ", cfg.Port) log.Print(" Ignoring:") for _, entry := range cfg.Ignore.LocationRegexes { log.Printf(" Events with locations matching %s", entry.String()) } for _, entry := range cfg.Ignore.SummaryRegexes { log.Printf(" Events with summaries matching %s", entry.String()) } if len(cfg.Ignore.LocationRegexes)+len(cfg.Ignore.SummaryRegexes) == 0 { log.Printf(" ") } log.Print(" User Tokens:") for user, token := range cfg.UserTokens { log.Print(" User ", user, ":") log.Print(" Hash: ", b64.EncodeToString(token.Hash)) log.Print(" Salt: ", b64.EncodeToString(token.Salt)) } if len(cfg.UserTokens) == 0 { log.Print(" ") } } func loadConfigFrom(filename string) config { bytes, err := os.ReadFile(filename) if err != nil { log.Fatal("Could not read config file (at ", filename, ")") } var cfg config if err = json.Unmarshal(bytes, &cfg); err != nil { log.Fatalln("Could not parse config file:", err) } return cfg } func loadConfig() config { configFile := os.Getenv("CONFIG_FILE") if configFile == "" { log.Fatal("Environment variable CONFIG_FILE not specified") } log.Print("Loading configuration from ", configFile) return loadConfigFrom(configFile) }