aboutsummaryrefslogtreecommitdiff
path: root/config/config.go
blob: 347fdac8b38c0ceeb7db06f4974f25cc643ad46a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
// 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
}