diff --git a/README.md b/README.md index 978dd29..6e6fe9a 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,29 @@ # persist Persist loads and saves Go objects to files + +## Usage + +First get it: + +``` +go get github.com/matryer/persist +``` + +Then save objects like this: + +``` +var conf Config +if err := persist.Save("./project.conf", &conf); err != nil { + log.Fatalln("failed to save config:", err) +} +``` + +And load them like this: + + +``` +var conf Config +if err := persist.Load("./project.conf", &conf); err != nil { + log.Fatalln("failed to load config:", err) +} +``` \ No newline at end of file diff --git a/doc.go b/doc.go new file mode 100644 index 0000000..6190498 --- /dev/null +++ b/doc.go @@ -0,0 +1,2 @@ +// Package persist loads and saves Go objects to files. +package persist diff --git a/persist.go b/persist.go new file mode 100644 index 0000000..5e2493d --- /dev/null +++ b/persist.go @@ -0,0 +1,60 @@ +package persist + +import ( + "bytes" + "encoding/json" + "io" + "os" + "sync" +) + +var lock sync.Mutex + +// Marshal is a function that marshals the object into an +// io.Reader. +// By default, it uses the JSON marshaller. +var Marshal = func(v interface{}) (io.Reader, error) { + b, err := json.MarshalIndent(v, "", "\t") + if err != nil { + return nil, err + } + return bytes.NewReader(b), nil +} + +// Unmarshal is a function that unmarshals the data from the +// reader into the specified value. +// By default, it uses the JSON unmarshaller. +var Unmarshal = func(r io.Reader, v interface{}) error { + return json.NewDecoder(r).Decode(v) +} + +// Save saves a representation of v to the file at path. +func Save(path string, v interface{}) error { + lock.Lock() + defer lock.Unlock() + f, err := os.Create(path) + if err != nil { + return err + } + defer f.Close() + r, err := Marshal(v) + if err != nil { + return err + } + _, err = io.Copy(f, r) + return err +} + +// Load loads the file at path into v. +// Use os.IsNotExist() to see if the returned error is due +// to the file being missing. +func Load(path string, v interface{}) error { + lock.Lock() + defer lock.Unlock() + f, err := os.Open(path) + if err != nil { + return err + } + defer f.Close() + return Unmarshal(f, v) +} diff --git a/persist_test.go b/persist_test.go new file mode 100644 index 0000000..fce661d --- /dev/null +++ b/persist_test.go @@ -0,0 +1,50 @@ +package persist_test + +import ( + "os" + "testing" + "time" + + "github.com/cheekybits/is" + "github.com/matryer/persist" +) + +type obj struct { + Name string + Number int + When time.Time +} + +func TestNoFile(t *testing.T) { + is := is.New(t) + + var v interface{} + err := persist.Load("no-such-file.conf", &v) + is.Equal(true, os.IsNotExist(err)) + +} + +func TestPersist(t *testing.T) { + is := is.New(t) + defer os.Remove("./file.tmp") + + o := &obj{ + Name: "Mat", + Number: 47, + When: time.Now(), + } + + // save it + err := persist.Save("./file.tmp", o) + is.NoErr(err) + + // load it + var o2 obj + err = persist.Load("./file.tmp", &o2) + is.NoErr(err) + + is.Equal(o.Name, o2.Name) + is.Equal(o.Number, o2.Number) + is.Equal(o.When, o2.When) + +}