// SPDX-FileCopyrightText: 2020 Ethel Morgan // // SPDX-License-Identifier: MIT package config import ( "encoding/json" "errors" "io/ioutil" "net/url" ) type ( Rule struct { Name string Triggers []Trigger Actions []Action } Trigger struct { URL *url.URL FormValues url.Values } ActionKind int Action struct { Kind ActionKind Name string URL *url.URL FormValues url.Values MQTT *url.URL Value string Retain bool } Config struct { RulesByName map[string]Rule } config struct { Rules map[string]struct { Triggers []struct { URL uurl `json:"url"` FormValues map[string]string `json:"formValues"` } `json:"triggers"` Actions []struct { URL uurl `json:"url"` FormValues map[string]string `json:"formValues"` MQTT uurl `json:"mqtt"` Value string `json:"value"` Retain bool `json:"retain"` } `json:"actions"` } `json:"rules"` } uurl struct { *url.URL } ) const ( HTTPAction ActionKind = iota MQTTAction ) func ParseFile(path string) (*Config, error) { bytes, err := ioutil.ReadFile(path) if err != nil { return nil, err } raw := config{} if err := json.Unmarshal(bytes, &raw); err != nil { return nil, err } return configFromConfig(raw) } func configFromConfig(raw config) (*Config, error) { c := &Config{ RulesByName: map[string]Rule{}, } for k, v := range raw.Rules { rule := Rule{ Name: k, } for _, rawTrigger := range v.Triggers { rule.Triggers = append(rule.Triggers, Trigger{ URL: rawTrigger.URL.URL, FormValues: urlValuesFromRawValues(rawTrigger.FormValues), }) } for _, action := range v.Actions { var kind ActionKind switch { case action.URL != uurl{} && action.MQTT == uurl{}: kind = HTTPAction case action.URL == uurl{} && action.MQTT != uurl{}: kind = MQTTAction default: return nil, errors.New("actions must be URL xor MQTT") } rule.Actions = append(rule.Actions, Action{ Kind: kind, URL: action.URL.URL, FormValues: urlValuesFromRawValues(action.FormValues), MQTT: action.MQTT.URL, Value: action.Value, Retain: action.Retain, }) } c.RulesByName[k] = rule } return c, nil } func (u uurl) MarshalText() ([]byte, error) { return []byte(u.String()), nil } func (u *uurl) UnmarshalText(raw []byte) error { uu, err := url.Parse(string(raw)) if err != nil { return err } u.URL = uu return nil } func urlValuesFromRawValues(raw map[string]string) url.Values { urlValues := map[string][]string{} for k, v := range raw { urlValues[k] = []string{v} } return urlValues }