OSDN Git Service

Hulk did something
[bytom/vapor.git] / vendor / github.com / magiconair / properties / load.go
1 // Copyright 2017 Frank Schroeder. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 package properties
6
7 import (
8         "fmt"
9         "io/ioutil"
10         "net/http"
11         "os"
12         "strings"
13 )
14
15 // Encoding specifies encoding of the input data.
16 type Encoding uint
17
18 const (
19         // UTF8 interprets the input data as UTF-8.
20         UTF8 Encoding = 1 << iota
21
22         // ISO_8859_1 interprets the input data as ISO-8859-1.
23         ISO_8859_1
24 )
25
26 // Load reads a buffer into a Properties struct.
27 func Load(buf []byte, enc Encoding) (*Properties, error) {
28         return loadBuf(buf, enc)
29 }
30
31 // LoadString reads an UTF8 string into a properties struct.
32 func LoadString(s string) (*Properties, error) {
33         return loadBuf([]byte(s), UTF8)
34 }
35
36 // LoadMap creates a new Properties struct from a string map.
37 func LoadMap(m map[string]string) *Properties {
38         p := NewProperties()
39         for k, v := range m {
40                 p.Set(k, v)
41         }
42         return p
43 }
44
45 // LoadFile reads a file into a Properties struct.
46 func LoadFile(filename string, enc Encoding) (*Properties, error) {
47         return loadAll([]string{filename}, enc, false)
48 }
49
50 // LoadFiles reads multiple files in the given order into
51 // a Properties struct. If 'ignoreMissing' is true then
52 // non-existent files will not be reported as error.
53 func LoadFiles(filenames []string, enc Encoding, ignoreMissing bool) (*Properties, error) {
54         return loadAll(filenames, enc, ignoreMissing)
55 }
56
57 // LoadURL reads the content of the URL into a Properties struct.
58 //
59 // The encoding is determined via the Content-Type header which
60 // should be set to 'text/plain'. If the 'charset' parameter is
61 // missing, 'iso-8859-1' or 'latin1' the encoding is set to
62 // ISO-8859-1. If the 'charset' parameter is set to 'utf-8' the
63 // encoding is set to UTF-8. A missing content type header is
64 // interpreted as 'text/plain; charset=utf-8'.
65 func LoadURL(url string) (*Properties, error) {
66         return loadAll([]string{url}, UTF8, false)
67 }
68
69 // LoadURLs reads the content of multiple URLs in the given order into a
70 // Properties struct. If 'ignoreMissing' is true then a 404 status code will
71 // not be reported as error. See LoadURL for the Content-Type header
72 // and the encoding.
73 func LoadURLs(urls []string, ignoreMissing bool) (*Properties, error) {
74         return loadAll(urls, UTF8, ignoreMissing)
75 }
76
77 // LoadAll reads the content of multiple URLs or files in the given order into a
78 // Properties struct. If 'ignoreMissing' is true then a 404 status code or missing file will
79 // not be reported as error. Encoding sets the encoding for files. For the URLs please see
80 // LoadURL for the Content-Type header and the encoding.
81 func LoadAll(names []string, enc Encoding, ignoreMissing bool) (*Properties, error) {
82         return loadAll(names, enc, ignoreMissing)
83 }
84
85 // MustLoadString reads an UTF8 string into a Properties struct and
86 // panics on error.
87 func MustLoadString(s string) *Properties {
88         return must(LoadString(s))
89 }
90
91 // MustLoadFile reads a file into a Properties struct and
92 // panics on error.
93 func MustLoadFile(filename string, enc Encoding) *Properties {
94         return must(LoadFile(filename, enc))
95 }
96
97 // MustLoadFiles reads multiple files in the given order into
98 // a Properties struct and panics on error. If 'ignoreMissing'
99 // is true then non-existent files will not be reported as error.
100 func MustLoadFiles(filenames []string, enc Encoding, ignoreMissing bool) *Properties {
101         return must(LoadFiles(filenames, enc, ignoreMissing))
102 }
103
104 // MustLoadURL reads the content of a URL into a Properties struct and
105 // panics on error.
106 func MustLoadURL(url string) *Properties {
107         return must(LoadURL(url))
108 }
109
110 // MustLoadURLs reads the content of multiple URLs in the given order into a
111 // Properties struct and panics on error. If 'ignoreMissing' is true then a 404
112 // status code will not be reported as error.
113 func MustLoadURLs(urls []string, ignoreMissing bool) *Properties {
114         return must(LoadURLs(urls, ignoreMissing))
115 }
116
117 // MustLoadAll reads the content of multiple URLs or files in the given order into a
118 // Properties struct. If 'ignoreMissing' is true then a 404 status code or missing file will
119 // not be reported as error. Encoding sets the encoding for files. For the URLs please see
120 // LoadURL for the Content-Type header and the encoding. It panics on error.
121 func MustLoadAll(names []string, enc Encoding, ignoreMissing bool) *Properties {
122         return must(LoadAll(names, enc, ignoreMissing))
123 }
124
125 func loadBuf(buf []byte, enc Encoding) (*Properties, error) {
126         p, err := parse(convert(buf, enc))
127         if err != nil {
128                 return nil, err
129         }
130         return p, p.check()
131 }
132
133 func loadAll(names []string, enc Encoding, ignoreMissing bool) (*Properties, error) {
134         result := NewProperties()
135         for _, name := range names {
136                 n, err := expandName(name)
137                 if err != nil {
138                         return nil, err
139                 }
140                 var p *Properties
141                 if strings.HasPrefix(n, "http://") || strings.HasPrefix(n, "https://") {
142                         p, err = loadURL(n, ignoreMissing)
143                 } else {
144                         p, err = loadFile(n, enc, ignoreMissing)
145                 }
146                 if err != nil {
147                         return nil, err
148                 }
149                 result.Merge(p)
150
151         }
152         return result, result.check()
153 }
154
155 func loadFile(filename string, enc Encoding, ignoreMissing bool) (*Properties, error) {
156         data, err := ioutil.ReadFile(filename)
157         if err != nil {
158                 if ignoreMissing && os.IsNotExist(err) {
159                         LogPrintf("properties: %s not found. skipping", filename)
160                         return NewProperties(), nil
161                 }
162                 return nil, err
163         }
164         p, err := parse(convert(data, enc))
165         if err != nil {
166                 return nil, err
167         }
168         return p, nil
169 }
170
171 func loadURL(url string, ignoreMissing bool) (*Properties, error) {
172         resp, err := http.Get(url)
173         if err != nil {
174                 return nil, fmt.Errorf("properties: error fetching %q. %s", url, err)
175         }
176         if resp.StatusCode == 404 && ignoreMissing {
177                 LogPrintf("properties: %s returned %d. skipping", url, resp.StatusCode)
178                 return NewProperties(), nil
179         }
180         if resp.StatusCode != 200 {
181                 return nil, fmt.Errorf("properties: %s returned %d", url, resp.StatusCode)
182         }
183         body, err := ioutil.ReadAll(resp.Body)
184         if err != nil {
185                 return nil, fmt.Errorf("properties: %s error reading response. %s", url, err)
186         }
187         if err = resp.Body.Close(); err != nil {
188                 return nil, fmt.Errorf("properties: %s error reading response. %s", url, err)
189         }
190
191         ct := resp.Header.Get("Content-Type")
192         var enc Encoding
193         switch strings.ToLower(ct) {
194         case "text/plain", "text/plain; charset=iso-8859-1", "text/plain; charset=latin1":
195                 enc = ISO_8859_1
196         case "", "text/plain; charset=utf-8":
197                 enc = UTF8
198         default:
199                 return nil, fmt.Errorf("properties: invalid content type %s", ct)
200         }
201
202         p, err := parse(convert(body, enc))
203         if err != nil {
204                 return nil, err
205         }
206         return p, nil
207 }
208
209 func must(p *Properties, err error) *Properties {
210         if err != nil {
211                 ErrorHandler(err)
212         }
213         return p
214 }
215
216 // expandName expands ${ENV_VAR} expressions in a name.
217 // If the environment variable does not exist then it will be replaced
218 // with an empty string. Malformed expressions like "${ENV_VAR" will
219 // be reported as error.
220 func expandName(name string) (string, error) {
221         return expand(name, make(map[string]bool), "${", "}", make(map[string]string))
222 }
223
224 // Interprets a byte buffer either as an ISO-8859-1 or UTF-8 encoded string.
225 // For ISO-8859-1 we can convert each byte straight into a rune since the
226 // first 256 unicode code points cover ISO-8859-1.
227 func convert(buf []byte, enc Encoding) string {
228         switch enc {
229         case UTF8:
230                 return string(buf)
231         case ISO_8859_1:
232                 runes := make([]rune, len(buf))
233                 for i, b := range buf {
234                         runes[i] = rune(b)
235                 }
236                 return string(runes)
237         default:
238                 ErrorHandler(fmt.Errorf("unsupported encoding %v", enc))
239         }
240         panic("ErrorHandler should exit")
241 }