forked from miracl/conflate
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request miracl#27 from miracl/file-extension
use file extensions to unmarshal data appropriately
- Loading branch information
Showing
12 changed files
with
384 additions
and
278 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
package conflate | ||
|
||
import ( | ||
pkgurl "net/url" | ||
"path/filepath" | ||
"strings" | ||
) | ||
|
||
type filedata struct { | ||
url pkgurl.URL | ||
bytes []byte | ||
obj map[string]interface{} | ||
includes []string | ||
} | ||
|
||
type filedatas []filedata | ||
|
||
// UnmarshallerFunc defines the type of function used for unmarshalling data | ||
type UnmarshallerFunc func([]byte, interface{}) error | ||
|
||
// UnmarshallerFuncs defines the type for a slice of UnmarshallerFunc | ||
type UnmarshallerFuncs []UnmarshallerFunc | ||
|
||
// UnmarshallerMap defines the type of a map of string to UnmarshallerFuncs | ||
type UnmarshallerMap map[string]UnmarshallerFuncs | ||
|
||
// Unmarshallers is a list of unmarshalling functions to be used for given file extensions. The unmarshaller slice for the blank file extension is used when no match is found. | ||
var Unmarshallers = UnmarshallerMap{ | ||
".json": {JSONUnmarshal}, | ||
".jsn": {JSONUnmarshal}, | ||
".yaml": {YAMLUnmarshal}, | ||
".yml": {YAMLUnmarshal}, | ||
".toml": {TOMLUnmarshal}, | ||
".tml": {TOMLUnmarshal}, | ||
"": {JSONUnmarshal, YAMLUnmarshal, TOMLUnmarshal}, | ||
} | ||
|
||
func newFiledata(bytes []byte, url pkgurl.URL) (filedata, error) { | ||
fd := filedata{bytes: bytes, url: url} | ||
err := fd.unmarshal() | ||
if err != nil { | ||
return fd, err | ||
} | ||
err = fd.extractIncludes() | ||
return fd, err | ||
} | ||
|
||
func wrapFiledata(bytes []byte) (filedata, error) { | ||
return newFiledata(bytes, emptyURL) | ||
} | ||
|
||
func wrapFiledatas(bytes ...[]byte) (filedatas, error) { | ||
var fds []filedata | ||
for _, b := range bytes { | ||
fd, err := wrapFiledata(b) | ||
if err != nil { | ||
return nil, err | ||
} | ||
fds = append(fds, fd) | ||
} | ||
return fds, nil | ||
} | ||
|
||
func (fd *filedata) unmarshal() error { | ||
ext := strings.ToLower(filepath.Ext(fd.url.Path)) | ||
unmarshallers, ok := Unmarshallers[ext] | ||
if !ok { | ||
unmarshallers = Unmarshallers[""] | ||
} | ||
err := makeError("Could not unmarshal data") | ||
for _, unmarshal := range unmarshallers { | ||
uerr := unmarshal(fd.bytes, &fd.obj) | ||
if uerr == nil { | ||
return nil | ||
} | ||
err = wrapError(uerr, err.Error()) | ||
} | ||
return err | ||
} | ||
|
||
func (fd *filedata) extractIncludes() error { | ||
err := jsonMarshalUnmarshal(fd.obj["includes"], &fd.includes) | ||
if err != nil { | ||
return wrapError(err, "Could not extract includes") | ||
} | ||
delete(fd.obj, "includes") | ||
return nil | ||
} | ||
|
||
func (fds filedatas) objs() []interface{} { | ||
var objs []interface{} | ||
for _, fd := range fds { | ||
objs = append(objs, fd.obj) | ||
} | ||
return objs | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,153 @@ | ||
package conflate | ||
|
||
import ( | ||
"github.com/stretchr/testify/assert" | ||
pkgurl "net/url" | ||
"testing" | ||
) | ||
|
||
func testFiledataNew(t *testing.T, data []byte, path string) (filedata, error) { | ||
url, err := pkgurl.Parse(path) | ||
assert.Nil(t, err) | ||
return newFiledata(data, *url) | ||
} | ||
|
||
func testFiledataNewAssert(t *testing.T, data []byte, path string) filedata { | ||
fd, err := testFiledataNew(t, data, path) | ||
assert.Nil(t, err) | ||
return fd | ||
} | ||
|
||
func TestFiledata_JSONAsAny(t *testing.T) { | ||
fd, err := testFiledataNew(t, testMarshalJSON, "file") | ||
assert.Nil(t, err) | ||
assert.Equal(t, fd.obj, testMarshalData) | ||
} | ||
|
||
func TestFiledata_JSONAsUnknown(t *testing.T) { | ||
fd, err := testFiledataNew(t, testMarshalJSON, "file.unknown") | ||
assert.Nil(t, err) | ||
assert.Equal(t, fd.obj, testMarshalData) | ||
} | ||
|
||
func TestFiledata_JSONAsJSON(t *testing.T) { | ||
fd, err := testFiledataNew(t, testMarshalJSON, "file.json") | ||
assert.Nil(t, err) | ||
assert.Equal(t, fd.obj, testMarshalData) | ||
} | ||
|
||
func TestFiledata_JSONAsJSN(t *testing.T) { | ||
fd, err := testFiledataNew(t, testMarshalJSON, "file.jsn") | ||
assert.Nil(t, err) | ||
assert.Equal(t, fd.obj, testMarshalData) | ||
} | ||
|
||
func TestFiledata_JSONAsTOML(t *testing.T) { | ||
fd, err := testFiledataNew(t, testMarshalJSON, "file.toml") | ||
assert.NotNil(t, err) | ||
assert.Nil(t, fd.obj) | ||
} | ||
|
||
func TestFiledata_YAMLAsAny(t *testing.T) { | ||
fd, err := testFiledataNew(t, testMarshalYAML, "file") | ||
assert.Nil(t, err) | ||
assert.Equal(t, fd.obj, testMarshalData) | ||
} | ||
|
||
func TestFiledata_YAMLAsUnknown(t *testing.T) { | ||
fd, err := testFiledataNew(t, testMarshalYAML, "file.unknown") | ||
assert.Nil(t, err) | ||
assert.Equal(t, fd.obj, testMarshalData) | ||
} | ||
|
||
func TestFiledata_YAMLAsYAML(t *testing.T) { | ||
fd, err := testFiledataNew(t, testMarshalYAML, "file.yaml") | ||
assert.Nil(t, err) | ||
assert.Equal(t, fd.obj, testMarshalData) | ||
} | ||
|
||
func TestFiledata_YAMLAsYML(t *testing.T) { | ||
fd, err := testFiledataNew(t, testMarshalYAML, "file.yml") | ||
assert.Nil(t, err) | ||
assert.Equal(t, fd.obj, testMarshalData) | ||
} | ||
|
||
func TestFiledata_YAMLAsTOML(t *testing.T) { | ||
fd, err := testFiledataNew(t, testMarshalYAML, "file.toml") | ||
assert.NotNil(t, err) | ||
assert.Nil(t, fd.obj) | ||
} | ||
|
||
func TestFiledata_TOMLAsAny(t *testing.T) { | ||
fd, err := testFiledataNew(t, testMarshalTOML, "file") | ||
assert.Nil(t, err) | ||
assert.Equal(t, fd.obj, testMarshalData) | ||
} | ||
|
||
func TestFiledata_TOMLAsUnknown(t *testing.T) { | ||
fd, err := testFiledataNew(t, testMarshalTOML, "file.unknown") | ||
assert.Nil(t, err) | ||
assert.Equal(t, fd.obj, testMarshalData) | ||
} | ||
|
||
func TestFiledata_TOMLAsTOML(t *testing.T) { | ||
fd, err := testFiledataNew(t, testMarshalTOML, "file.toml") | ||
assert.Nil(t, err) | ||
assert.Equal(t, fd.obj, testMarshalData) | ||
} | ||
|
||
func TestFiledata_TOMLAsTML(t *testing.T) { | ||
fd, err := testFiledataNew(t, testMarshalTOML, "file.tml") | ||
assert.Nil(t, err) | ||
assert.Equal(t, fd.obj, testMarshalData) | ||
} | ||
|
||
func TestFiledata_TOMLAsJSON(t *testing.T) { | ||
fd, err := testFiledataNew(t, testMarshalTOML, "file.json") | ||
assert.NotNil(t, err) | ||
assert.Nil(t, fd.obj) | ||
} | ||
|
||
func TestFiledata_NoIncludes(t *testing.T) { | ||
fd, err := wrapFiledata([]byte(`{"x": 1}`)) | ||
assert.Nil(t, err) | ||
assert.Nil(t, fd.obj["includes"]) | ||
assert.Equal(t, fd.obj, map[string]interface{}{"x": 1.0}) | ||
} | ||
|
||
func TestFiledata_BlankIncludes(t *testing.T) { | ||
fd, err := wrapFiledata([]byte(`{"includes":[], "x": 1}`)) | ||
assert.Nil(t, err) | ||
assert.Nil(t, fd.obj["includes"]) | ||
assert.Equal(t, fd.obj, map[string]interface{}{"x": 1.0}) | ||
} | ||
|
||
func TestFiledata_NullIncludes(t *testing.T) { | ||
fd, err := wrapFiledata([]byte(`{"includes":null, "x": 1}`)) | ||
assert.Nil(t, err) | ||
assert.Nil(t, fd.obj["includes"]) | ||
assert.Equal(t, fd.obj, map[string]interface{}{"x": 1.0}) | ||
} | ||
|
||
func TestFiledata_Includes(t *testing.T) { | ||
fd, err := wrapFiledata([]byte(`{"includes":["test1", "test2"], "x": 1}`)) | ||
assert.Nil(t, err) | ||
assert.Equal(t, fd.includes, []string{"test1", "test2"}) | ||
assert.Nil(t, fd.obj["includes"]) | ||
assert.Equal(t, fd.obj, map[string]interface{}{"x": 1.0}) | ||
} | ||
|
||
func TestFiledata_IncludesError(t *testing.T) { | ||
_, err := wrapFiledata([]byte(`{"includes": "not array"}`)) | ||
assert.NotNil(t, err) | ||
assert.Contains(t, err.Error(), "Could not extract includes") | ||
} | ||
|
||
func TestFiledatas_Unmarshal(t *testing.T) { | ||
fds := filedatas{ | ||
testFiledataNewAssert(t, testMarshalJSON, "file.json"), | ||
testFiledataNewAssert(t, testMarshalYAML, "file.yaml"), | ||
testFiledataNewAssert(t, testMarshalTOML, "file.toml"), | ||
} | ||
assert.Equal(t, fds.objs(), []interface{}{testMarshalData, testMarshalData, testMarshalData}) | ||
} |
Oops, something went wrong.