Skip to content

Commit 4875441

Browse files
committed
initial commit
0 parents  commit 4875441

File tree

6 files changed

+1160
-0
lines changed

6 files changed

+1160
-0
lines changed

properties.go

Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
/*
2+
* This file is part of PropertiesMap library.
3+
*
4+
* Copyright 2017 Arduino LLC (http://www.arduino.cc/)
5+
*
6+
* Arduino Builder is free software; you can redistribute it and/or modify
7+
* it under the terms of the GNU General Public License as published by
8+
* the Free Software Foundation; either version 2 of the License, or
9+
* (at your option) any later version.
10+
*
11+
* This program is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
* GNU General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU General Public License
17+
* along with this program; if not, write to the Free Software
18+
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19+
*
20+
* As a special exception, you may use this file as part of a free software
21+
* library without restriction. Specifically, if other files instantiate
22+
* templates or use macros or inline functions from this file, or you compile
23+
* this file and link it with other files to produce an executable, this
24+
* file does not by itself cause the resulting executable to be covered by
25+
* the GNU General Public License. This exception does not however
26+
* invalidate any other reasons why the executable file might be covered by
27+
* the GNU General Public License.
28+
*/
29+
30+
package properties
31+
32+
import (
33+
"fmt"
34+
"io/ioutil"
35+
"os"
36+
"reflect"
37+
"regexp"
38+
"runtime"
39+
"strings"
40+
)
41+
42+
// Map is a container of properties
43+
type Map map[string]string
44+
45+
var osSuffix string
46+
47+
func init() {
48+
switch value := runtime.GOOS; value {
49+
case "linux", "freebsd", "windows":
50+
osSuffix = runtime.GOOS
51+
case "darwin":
52+
osSuffix = "macosx"
53+
default:
54+
panic("Unsupported OS")
55+
}
56+
}
57+
58+
func Load(filepath string) (Map, error) {
59+
bytes, err := ioutil.ReadFile(filepath)
60+
if err != nil {
61+
return nil, fmt.Errorf("Error reading file: %s", err)
62+
}
63+
64+
text := string(bytes)
65+
text = strings.Replace(text, "\r\n", "\n", -1)
66+
text = strings.Replace(text, "\r", "\n", -1)
67+
68+
properties := make(Map)
69+
70+
for lineNum, line := range strings.Split(text, "\n") {
71+
if err := properties.loadSingleLine(line); err != nil {
72+
return nil, fmt.Errorf("Error reading file (%s:%d): %s", filepath, lineNum, err)
73+
}
74+
}
75+
76+
return properties, nil
77+
}
78+
79+
func LoadFromSlice(lines []string) (Map, error) {
80+
properties := make(Map)
81+
82+
for lineNum, line := range lines {
83+
if err := properties.loadSingleLine(line); err != nil {
84+
return nil, fmt.Errorf("Error reading from slice (index:%d): %s", lineNum, err)
85+
}
86+
}
87+
88+
return properties, nil
89+
}
90+
91+
func (properties Map) loadSingleLine(line string) error {
92+
line = strings.TrimSpace(line)
93+
94+
if len(line) == 0 || line[0] == '#' {
95+
return nil
96+
}
97+
98+
lineParts := strings.SplitN(line, "=", 2)
99+
if len(lineParts) != 2 {
100+
return fmt.Errorf("Invalid line format, should be 'key=value'")
101+
}
102+
key := strings.TrimSpace(lineParts[0])
103+
value := strings.TrimSpace(lineParts[1])
104+
105+
key = strings.Replace(key, "."+osSuffix, "", 1)
106+
properties[key] = value
107+
108+
return nil
109+
}
110+
111+
func SafeLoad(filepath string) (Map, error) {
112+
_, err := os.Stat(filepath)
113+
if os.IsNotExist(err) {
114+
return make(Map), nil
115+
}
116+
117+
properties, err := Load(filepath)
118+
if err != nil {
119+
return nil, err
120+
}
121+
return properties, nil
122+
}
123+
124+
func (m Map) FirstLevelOf() map[string]Map {
125+
newMap := make(map[string]Map)
126+
for key, value := range m {
127+
if strings.Index(key, ".") == -1 {
128+
continue
129+
}
130+
keyParts := strings.SplitN(key, ".", 2)
131+
if newMap[keyParts[0]] == nil {
132+
newMap[keyParts[0]] = make(Map)
133+
}
134+
newMap[keyParts[0]][keyParts[1]] = value
135+
}
136+
return newMap
137+
}
138+
139+
func (m Map) SubTree(key string) Map {
140+
return m.FirstLevelOf()[key]
141+
}
142+
143+
func (m Map) ExpandPropsInString(str string) string {
144+
replaced := true
145+
for i := 0; i < 10 && replaced; i++ {
146+
replaced = false
147+
for key, value := range m {
148+
newStr := strings.Replace(str, "{"+key+"}", value, -1)
149+
replaced = replaced || str != newStr
150+
str = newStr
151+
}
152+
}
153+
return str
154+
}
155+
156+
func (m Map) Merge(sources ...Map) Map {
157+
for _, source := range sources {
158+
for key, value := range source {
159+
m[key] = value
160+
}
161+
}
162+
return m
163+
}
164+
165+
func (m Map) Clone() Map {
166+
clone := make(Map)
167+
clone.Merge(m)
168+
return clone
169+
}
170+
171+
func (m Map) Equals(other Map) bool {
172+
return reflect.DeepEqual(m, other)
173+
}
174+
175+
func MergeMapsOfProperties(target map[string]Map, sources ...map[string]Map) map[string]Map {
176+
for _, source := range sources {
177+
for key, value := range source {
178+
target[key] = value
179+
}
180+
}
181+
return target
182+
}
183+
184+
func DeleteUnexpandedPropsFromString(str string) (string, error) {
185+
rxp := regexp.MustCompile("\\{.+?\\}")
186+
return rxp.ReplaceAllString(str, ""), nil
187+
}

properties_test.go

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
/*
2+
* This file is part of PropertiesMap library.
3+
*
4+
* Copyright 2017 Arduino LLC (http://www.arduino.cc/)
5+
*
6+
* Arduino Builder is free software; you can redistribute it and/or modify
7+
* it under the terms of the GNU General Public License as published by
8+
* the Free Software Foundation; either version 2 of the License, or
9+
* (at your option) any later version.
10+
*
11+
* This program is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
* GNU General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU General Public License
17+
* along with this program; if not, write to the Free Software
18+
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19+
*
20+
* As a special exception, you may use this file as part of a free software
21+
* library without restriction. Specifically, if other files instantiate
22+
* templates or use macros or inline functions from this file, or you compile
23+
* this file and link it with other files to produce an executable, this
24+
* file does not by itself cause the resulting executable to be covered by
25+
* the GNU General Public License. This exception does not however
26+
* invalidate any other reasons why the executable file might be covered by
27+
* the GNU General Public License.
28+
*/
29+
30+
package properties
31+
32+
import (
33+
"path/filepath"
34+
"runtime"
35+
"testing"
36+
37+
"github.com/stretchr/testify/require"
38+
)
39+
40+
func TestPropertiesBoardsTxt(t *testing.T) {
41+
p, err := Load(filepath.Join("testdata", "boards.txt"))
42+
43+
require.NoError(t, err)
44+
45+
require.Equal(t, "Processor", p["menu.cpu"])
46+
require.Equal(t, "32256", p["ethernet.upload.maximum_size"])
47+
require.Equal(t, "{build.usb_flags}", p["robotMotor.build.extra_flags"])
48+
49+
ethernet := p.SubTree("ethernet")
50+
require.Equal(t, "Arduino Ethernet", ethernet["name"])
51+
}
52+
53+
func TestPropertiesTestTxt(t *testing.T) {
54+
p, err := Load(filepath.Join("testdata", "test.txt"))
55+
56+
require.NoError(t, err)
57+
58+
require.Equal(t, 4, len(p))
59+
require.Equal(t, "value = 1", p["key"])
60+
61+
switch value := runtime.GOOS; value {
62+
case "linux":
63+
require.Equal(t, "is linux", p["which.os"])
64+
case "windows":
65+
require.Equal(t, "is windows", p["which.os"])
66+
case "darwin":
67+
require.Equal(t, "is macosx", p["which.os"])
68+
default:
69+
require.FailNow(t, "unsupported OS")
70+
}
71+
}
72+
73+
func TestExpandPropsInString(t *testing.T) {
74+
aMap := make(Map)
75+
aMap["key1"] = "42"
76+
aMap["key2"] = "{key1}"
77+
78+
str := "{key1} == {key2} == true"
79+
80+
str = aMap.ExpandPropsInString(str)
81+
require.Equal(t, "42 == 42 == true", str)
82+
}
83+
84+
func TestExpandPropsInString2(t *testing.T) {
85+
p := make(Map)
86+
p["key2"] = "{key2}"
87+
p["key1"] = "42"
88+
89+
str := "{key1} == {key2} == true"
90+
91+
str = p.ExpandPropsInString(str)
92+
require.Equal(t, "42 == {key2} == true", str)
93+
}
94+
95+
func TestDeleteUnexpandedPropsFromString(t *testing.T) {
96+
p := make(Map)
97+
p["key1"] = "42"
98+
p["key2"] = "{key1}"
99+
100+
str := "{key1} == {key2} == {key3} == true"
101+
102+
str = p.ExpandPropsInString(str)
103+
str, err := DeleteUnexpandedPropsFromString(str)
104+
require.NoError(t, err)
105+
require.Equal(t, "42 == 42 == == true", str)
106+
}
107+
108+
func TestDeleteUnexpandedPropsFromString2(t *testing.T) {
109+
p := make(Map)
110+
p["key2"] = "42"
111+
112+
str := "{key1} == {key2} == {key3} == true"
113+
114+
str = p.ExpandPropsInString(str)
115+
str, err := DeleteUnexpandedPropsFromString(str)
116+
require.NoError(t, err)
117+
require.Equal(t, " == 42 == == true", str)
118+
}
119+
120+
func TestPropertiesRedBeearLabBoardsTxt(t *testing.T) {
121+
p, err := Load(filepath.Join("testdata", "redbearlab_boards.txt"))
122+
123+
require.NoError(t, err)
124+
125+
require.Equal(t, 83, len(p))
126+
require.Equal(t, "Blend", p["blend.name"])
127+
require.Equal(t, "arduino:arduino", p["blend.build.core"])
128+
require.Equal(t, "0x2404", p["blendmicro16.pid.0"])
129+
130+
ethernet := p.SubTree("blend")
131+
require.Equal(t, "arduino:arduino", ethernet["build.core"])
132+
}
133+
134+
func TestPropertiesBroken(t *testing.T) {
135+
_, err := Load(filepath.Join("testdata", "broken.txt"))
136+
137+
require.Error(t, err)
138+
}

0 commit comments

Comments
 (0)