@@ -2,6 +2,7 @@ package configparser
2
2
3
3
import (
4
4
"bufio"
5
+ "bytes"
5
6
"fmt"
6
7
"io"
7
8
"os"
23
24
interpolater = regexp .MustCompile (`%\(([^)]*)\)s` )
24
25
)
25
26
26
- // AllowNoValue allows parser to save options without values as empty strings.
27
- var AllowNoValue bool
28
-
29
27
var boolMapping = map [string ]bool {
30
28
"1" : true ,
31
29
"true" : true ,
@@ -48,6 +46,12 @@ type Config map[string]*Section
48
46
type ConfigParser struct {
49
47
config Config
50
48
defaults * Section
49
+ opt * Options
50
+ }
51
+
52
+ // Options allows to control parser behavior.
53
+ type Options struct {
54
+ AllowNoValue bool
51
55
}
52
56
53
57
// Keys returns a sorted slice of keys
@@ -75,22 +79,29 @@ func New() *ConfigParser {
75
79
return & ConfigParser {
76
80
config : make (Config ),
77
81
defaults : newSection (defaultSectionName ),
82
+ opt : & Options {},
78
83
}
79
84
}
80
85
81
- // NewWithDefaults allows creation of a new ConfigParser with a pre-existing
82
- // Dict.
83
- func NewWithDefaults (defaults Dict ) (* ConfigParser , error ) {
84
- p := ConfigParser {
86
+ // NewWithOptions creates a new ConfigParser with options.
87
+ func NewWithOptions (opt * Options ) * ConfigParser {
88
+ return & ConfigParser {
85
89
config : make (Config ),
86
90
defaults : newSection (defaultSectionName ),
91
+ opt : opt ,
87
92
}
93
+ }
94
+
95
+ // NewWithDefaults allows creation of a new ConfigParser with a pre-existing
96
+ // Dict.
97
+ func NewWithDefaults (defaults Dict ) (* ConfigParser , error ) {
98
+ p := New ()
88
99
for key , value := range defaults {
89
100
if err := p .defaults .Add (key , value ); err != nil {
90
101
return nil , fmt .Errorf ("failed to add %q to %q: %w" , key , value , err )
91
102
}
92
103
}
93
- return & p , nil
104
+ return p , nil
94
105
}
95
106
96
107
// NewConfigParserFromFile creates a new ConfigParser struct populated from the
@@ -106,53 +117,17 @@ func NewConfigParserFromFile(filename string) (*ConfigParser, error) {
106
117
// ParseReader parses a ConfigParser from the provided input.
107
118
func ParseReader (in io.Reader ) (* ConfigParser , error ) {
108
119
p := New ()
109
- reader := bufio .NewReader (in )
110
- var lineNo int
111
- var err error
112
- var curSect * Section
120
+ err := p .ParseReader (in )
113
121
114
- for err == nil {
115
- l , _ , err := reader .ReadLine ()
116
- if err != nil {
117
- break
118
- }
119
- lineNo ++
120
- if len (l ) == 0 {
121
- continue
122
- }
123
- line := strings .TrimFunc (string (l ), unicode .IsSpace ) // ensures sectionHeader regex will match
122
+ return p , err
123
+ }
124
124
125
- // Skip comment lines and empty lines
126
- if strings . HasPrefix ( line , "#" ) || line == "" {
127
- continue
128
- }
125
+ // ParseReaderWithOptions parses a ConfigParser from the provided input with given options.
126
+ func ParseReaderWithOptions ( in io. Reader , opt * Options ) ( * ConfigParser , error ) {
127
+ p := NewWithOptions ( opt )
128
+ err := p . ParseReader ( in )
129
129
130
- if match := sectionHeader .FindStringSubmatch (line ); len (match ) > 0 {
131
- section := match [1 ]
132
- if section == defaultSectionName {
133
- curSect = p .defaults
134
- } else if _ , present := p .config [section ]; ! present {
135
- curSect = newSection (section )
136
- p .config [section ] = curSect
137
- }
138
- } else if match = keyValue .FindStringSubmatch (line ); len (match ) > 0 {
139
- if curSect == nil {
140
- return nil , fmt .Errorf ("missing section header: %d %s" , lineNo , line )
141
- }
142
- key := strings .TrimSpace (match [1 ])
143
- value := match [3 ]
144
- if err := curSect .Add (key , value ); err != nil {
145
- return nil , fmt .Errorf ("failed to add %q = %q: %w" , key , value , err )
146
- }
147
- } else if match = keyWNoValue .FindStringSubmatch (line ); len (match ) > 0 && AllowNoValue && curSect != nil {
148
- key := strings .TrimSpace (match [1 ])
149
- value := match [4 ]
150
- if err := curSect .Add (key , value ); err != nil {
151
- return nil , fmt .Errorf ("failed to add %q = %q: %w" , key , value , err )
152
- }
153
- }
154
- }
155
- return p , nil
130
+ return p , err
156
131
}
157
132
158
133
// Parse takes a filename and parses it into a ConfigParser value.
@@ -169,6 +144,17 @@ func Parse(filename string) (*ConfigParser, error) {
169
144
return p , nil
170
145
}
171
146
147
+ // ParseWithOptions takes a filename and parses it into a ConfigParser value with given options.
148
+ func ParseWithOptions (filename string , opt * Options ) (* ConfigParser , error ) {
149
+ p := NewWithOptions (opt )
150
+ data , err := os .ReadFile (filename )
151
+ if err != nil {
152
+ return nil , err
153
+ }
154
+ err = p .ParseReader (bytes .NewReader (data ))
155
+ return p , err
156
+ }
157
+
172
158
func writeSection (file * os.File , delimiter string , section * Section ) error {
173
159
_ , err := file .WriteString (fmt .Sprintf ("[%s]\n " , section .Name ))
174
160
if err != nil {
@@ -210,3 +196,55 @@ func (p *ConfigParser) SaveWithDelimiter(filename, delimiter string) error {
210
196
211
197
return nil
212
198
}
199
+
200
+ // ParseReader parses data into ConfigParser from provided reader.
201
+ func (p * ConfigParser ) ParseReader (in io.Reader ) error {
202
+ reader := bufio .NewReader (in )
203
+ var lineNo int
204
+ var err error
205
+ var curSect * Section
206
+
207
+ for err == nil {
208
+ l , _ , err := reader .ReadLine ()
209
+ if err != nil {
210
+ break
211
+ }
212
+ lineNo ++
213
+ if len (l ) == 0 {
214
+ continue
215
+ }
216
+ line := strings .TrimFunc (string (l ), unicode .IsSpace ) // ensures sectionHeader regex will match
217
+
218
+ // Skip comment lines and empty lines
219
+ if strings .HasPrefix (line , "#" ) || line == "" {
220
+ continue
221
+ }
222
+
223
+ if match := sectionHeader .FindStringSubmatch (line ); len (match ) > 0 {
224
+ section := match [1 ]
225
+ if section == defaultSectionName {
226
+ curSect = p .defaults
227
+ } else if _ , present := p .config [section ]; ! present {
228
+ curSect = newSection (section )
229
+ p .config [section ] = curSect
230
+ }
231
+ } else if match = keyValue .FindStringSubmatch (line ); len (match ) > 0 {
232
+ if curSect == nil {
233
+ return fmt .Errorf ("missing section header: %d %s" , lineNo , line )
234
+ }
235
+ key := strings .TrimSpace (match [1 ])
236
+ value := match [3 ]
237
+ if err := curSect .Add (key , value ); err != nil {
238
+ return fmt .Errorf ("failed to add %q = %q: %w" , key , value , err )
239
+ }
240
+ } else if match = keyWNoValue .FindStringSubmatch (line ); len (match ) > 0 && p .opt .AllowNoValue && curSect != nil {
241
+ key := strings .TrimSpace (match [1 ])
242
+ value := match [4 ]
243
+ if err := curSect .Add (key , value ); err != nil {
244
+ return fmt .Errorf ("failed to add %q = %q: %w" , key , value , err )
245
+ }
246
+ }
247
+ }
248
+
249
+ return nil
250
+ }
0 commit comments