Skip to content

Commit 3aabfd3

Browse files
committed
Add pure in-memory operations.
This allows for pure in-memory operation using the `io.Reader` and `io.Writer` interfaces.
1 parent 5cfc02f commit 3aabfd3

File tree

4 files changed

+95
-11
lines changed

4 files changed

+95
-11
lines changed

README.md

+6-1
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,15 @@ Please see [conf.ini](testdata/conf.ini) as an example.
5151
- Finally, `SaveConfigFile` saves your configuration to local file system.
5252
- Use method `Reload` in case someone else modified your file(s).
5353
- Methods contains `Comment` help you manipulate comments.
54+
- `LoadFromReader` allows loading data without an intermediate file.
55+
- `SaveConfigData` added, which writes configuration to an arbitrary writer.
56+
- `ReloadData` allows to reload data from memory.
57+
58+
Note that you cannot mix in-memory configuration with on-disk configuration.
5459

5560
## More Information
5661

57-
- All characters are CASE SENSITIVE, BE CAREFULL!
62+
- All characters are CASE SENSITIVE, BE CAREFUL!
5863

5964
## Credits
6065

goconfig_test.go

+35
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@
1515
package goconfig
1616

1717
import (
18+
"bytes"
1819
"fmt"
20+
"io/ioutil"
1921
"testing"
2022

2123
. "github.com/smartystreets/goconvey/convey"
@@ -204,6 +206,19 @@ func TestSaveConfigFile(t *testing.T) {
204206
})
205207
}
206208

209+
func TestSaveConfigData(t *testing.T) {
210+
Convey("Save a ConfigFile to file system", t, func() {
211+
c, err := LoadConfigFile("testdata/conf.ini", "testdata/conf2.ini")
212+
So(err, ShouldBeNil)
213+
So(c, ShouldNotBeNil)
214+
215+
c.SetValue("", "", "empty")
216+
var dst bytes.Buffer
217+
So(SaveConfigData(c, &dst), ShouldBeNil)
218+
So(dst.Len(), ShouldNotEqual, 0)
219+
})
220+
}
221+
207222
func TestReload(t *testing.T) {
208223
Convey("Reload a configuration file", t, func() {
209224
c, err := LoadConfigFile("testdata/conf.ini", "testdata/conf2.ini")
@@ -214,6 +229,18 @@ func TestReload(t *testing.T) {
214229
})
215230
}
216231

232+
func TestReloadData(t *testing.T) {
233+
Convey("Reload a configuration file", t, func() {
234+
data, err := ioutil.ReadFile("testdata/conf.ini")
235+
So(err, ShouldBeNil)
236+
c, err := LoadFromReader(bytes.NewBuffer(data))
237+
So(err, ShouldBeNil)
238+
So(c, ShouldNotBeNil)
239+
240+
So(c.ReloadData(bytes.NewBuffer(data)), ShouldBeNil)
241+
})
242+
}
243+
217244
func TestAppendFiles(t *testing.T) {
218245
Convey("Reload a configuration file", t, func() {
219246
c, err := LoadConfigFile("testdata/conf.ini")
@@ -347,6 +374,14 @@ func TestLoadFromData(t *testing.T) {
347374
})
348375
}
349376

377+
func TestLoadFromReader(t *testing.T) {
378+
Convey("Load config file from reader", t, func() {
379+
c, err := LoadFromReader(bytes.NewBuffer([]byte("")))
380+
So(err, ShouldBeNil)
381+
So(c, ShouldNotBeNil)
382+
})
383+
}
384+
350385
func Benchmark_GetValue(b *testing.B) {
351386
c, _ := LoadConfigFile("testdata/conf.ini")
352387
c.BlockMode = false

read.go

+36-1
Original file line numberDiff line numberDiff line change
@@ -176,10 +176,15 @@ func (c *ConfigFile) read(reader io.Reader) (err error) {
176176

177177
// LoadFromData accepts raw data directly from memory
178178
// and returns a new configuration representation.
179+
// Note that the configuration is written to the system
180+
// temporary folder, so your file should not contain
181+
// sensitive information.
179182
func LoadFromData(data []byte) (c *ConfigFile, err error) {
180183
// Save memory data to temporary file to support further operations.
181184
tmpName := path.Join(os.TempDir(), "goconfig", fmt.Sprintf("%d", time.Now().Nanosecond()))
182-
os.MkdirAll(path.Dir(tmpName), os.ModePerm)
185+
if err = os.MkdirAll(path.Dir(tmpName), os.ModePerm); err != nil {
186+
return nil, err
187+
}
183188
if err = ioutil.WriteFile(tmpName, data, 0655); err != nil {
184189
return nil, err
185190
}
@@ -189,6 +194,16 @@ func LoadFromData(data []byte) (c *ConfigFile, err error) {
189194
return c, err
190195
}
191196

197+
// LoadFromReader accepts raw data directly from a reader
198+
// and returns a new configuration representation.
199+
// You must use ReloadData to reload.
200+
// You cannot append files a configfile read this way.
201+
func LoadFromReader(in io.Reader) (c *ConfigFile, err error) {
202+
c = newConfigFile([]string{""})
203+
err = c.read(in)
204+
return c, err
205+
}
206+
192207
func (c *ConfigFile) loadFile(fileName string) (err error) {
193208
f, err := os.Open(fileName)
194209
if err != nil {
@@ -224,6 +239,9 @@ func LoadConfigFile(fileName string, moreFiles ...string) (c *ConfigFile, err er
224239
func (c *ConfigFile) Reload() (err error) {
225240
var cfg *ConfigFile
226241
if len(c.fileNames) == 1 {
242+
if c.fileNames[0] == "" {
243+
return fmt.Errorf("file opened from in-memory data, use ReloadData to reload")
244+
}
227245
cfg, err = LoadConfigFile(c.fileNames[0])
228246
} else {
229247
cfg, err = LoadConfigFile(c.fileNames[0], c.fileNames[1:]...)
@@ -235,8 +253,25 @@ func (c *ConfigFile) Reload() (err error) {
235253
return err
236254
}
237255

256+
// ReloadData reloads configuration file from memory
257+
func (c *ConfigFile) ReloadData(in io.Reader) (err error) {
258+
var cfg *ConfigFile
259+
if len(c.fileNames) != 1 {
260+
return fmt.Errorf("Multiple files loaded, unable to mix in-memory and file data")
261+
}
262+
263+
cfg, err = LoadFromReader(in)
264+
if err == nil {
265+
*c = *cfg
266+
}
267+
return err
268+
}
269+
238270
// AppendFiles appends more files to ConfigFile and reload automatically.
239271
func (c *ConfigFile) AppendFiles(files ...string) error {
272+
if len(c.fileNames) == 1 && c.fileNames[0] == "" {
273+
return fmt.Errorf("Cannot append file data to in-memory data")
274+
}
240275
c.fileNames = append(c.fileNames, files...)
241276
return c.Reload()
242277
}

write.go

+18-9
Original file line numberDiff line numberDiff line change
@@ -16,21 +16,16 @@ package goconfig
1616

1717
import (
1818
"bytes"
19+
"io"
1920
"os"
2021
"strings"
2122
)
2223

2324
// Write spaces around "=" to look better.
2425
var PrettyFormat = true
2526

26-
// SaveConfigFile writes configuration file to local file system
27-
func SaveConfigFile(c *ConfigFile, filename string) (err error) {
28-
// Write configuration file by filename.
29-
var f *os.File
30-
if f, err = os.Create(filename); err != nil {
31-
return err
32-
}
33-
27+
// SaveConfigData writes configuration to a writer
28+
func SaveConfigData(c *ConfigFile, out io.Writer) (err error) {
3429
equalSign := "="
3530
if PrettyFormat {
3631
equalSign = " = "
@@ -101,7 +96,21 @@ func SaveConfigFile(c *ConfigFile, filename string) (err error) {
10196
}
10297
}
10398

104-
if _, err = buf.WriteTo(f); err != nil {
99+
if _, err := buf.WriteTo(out); err != nil {
100+
return err
101+
}
102+
return nil
103+
}
104+
105+
// SaveConfigFile writes configuration file to local file system
106+
func SaveConfigFile(c *ConfigFile, filename string) (err error) {
107+
// Write configuration file by filename.
108+
var f *os.File
109+
if f, err = os.Create(filename); err != nil {
110+
return err
111+
}
112+
113+
if err := SaveConfigData(c, f); err != nil {
105114
return err
106115
}
107116
return f.Close()

0 commit comments

Comments
 (0)