summaryrefslogtreecommitdiffstats
path: root/tpl/transform/unmarshal.go
diff options
context:
space:
mode:
Diffstat (limited to 'tpl/transform/unmarshal.go')
-rw-r--r--tpl/transform/unmarshal.go93
1 files changed, 88 insertions, 5 deletions
diff --git a/tpl/transform/unmarshal.go b/tpl/transform/unmarshal.go
index bf7db8920..d83cafd3a 100644
--- a/tpl/transform/unmarshal.go
+++ b/tpl/transform/unmarshal.go
@@ -15,8 +15,10 @@ package transform
import (
"io/ioutil"
+ "strings"
"github.com/gohugoio/hugo/common/hugio"
+ "github.com/mitchellh/mapstructure"
"github.com/gohugoio/hugo/helpers"
"github.com/gohugoio/hugo/parser/metadecoders"
@@ -27,8 +29,33 @@ import (
)
// Unmarshal unmarshals the data given, which can be either a string
-// or a Resource. Supported formats are JSON, TOML and YAML.
-func (ns *Namespace) Unmarshal(data interface{}) (interface{}, error) {
+// or a Resource. Supported formats are JSON, TOML, YAML, and CSV.
+// You can optional provide an Options object as the first argument.
+func (ns *Namespace) Unmarshal(args ...interface{}) (interface{}, error) {
+ if len(args) < 1 || len(args) > 2 {
+ return nil, errors.New("unmarshal takes 1 or 2 arguments")
+ }
+
+ var data interface{}
+ var decoder = metadecoders.Default
+
+ if len(args) == 1 {
+ data = args[0]
+ } else {
+ m, ok := args[0].(map[string]interface{})
+ if !ok {
+ return nil, errors.New("first argument must be a map")
+ }
+
+ var err error
+
+ data = args[1]
+ decoder, err = decodeDecoder(m)
+ if err != nil {
+ return nil, errors.WithMessage(err, "failed to decode options")
+ }
+
+ }
// All the relevant Resource types implements ReadSeekCloserResource,
// which should be the most effective way to get the content.
@@ -75,7 +102,7 @@ func (ns *Namespace) Unmarshal(data interface{}) (interface{}, error) {
return nil, err
}
- return metadecoders.Unmarshal(b, f)
+ return decoder.Unmarshal(b, f)
})
}
@@ -88,11 +115,67 @@ func (ns *Namespace) Unmarshal(data interface{}) (interface{}, error) {
key := helpers.MD5String(dataStr)
return ns.cache.GetOrCreate(key, func() (interface{}, error) {
- f := metadecoders.FormatFromContentString(dataStr)
+ f := decoder.FormatFromContentString(dataStr)
if f == "" {
return nil, errors.New("unknown format")
}
- return metadecoders.Unmarshal([]byte(dataStr), f)
+ return decoder.Unmarshal([]byte(dataStr), f)
})
}
+
+func decodeDecoder(m map[string]interface{}) (metadecoders.Decoder, error) {
+ opts := metadecoders.Default
+
+ if m == nil {
+ return opts, nil
+ }
+
+ // mapstructure does not support string to rune conversion, so do that manually.
+ // See https://github.com/mitchellh/mapstructure/issues/151
+ for k, v := range m {
+ if strings.EqualFold(k, "Comma") {
+ r, err := stringToRune(v)
+ if err != nil {
+ return opts, err
+ }
+ opts.Comma = r
+ delete(m, k)
+
+ } else if strings.EqualFold(k, "Comment") {
+ r, err := stringToRune(v)
+ if err != nil {
+ return opts, err
+ }
+ opts.Comment = r
+ delete(m, k)
+ }
+ }
+
+ err := mapstructure.WeakDecode(m, &opts)
+
+ return opts, err
+}
+
+func stringToRune(v interface{}) (rune, error) {
+ s, err := cast.ToStringE(v)
+ if err != nil {
+ return 0, err
+ }
+
+ if len(s) == 0 {
+ return 0, nil
+ }
+
+ var r rune
+
+ for i, rr := range s {
+ if i == 0 {
+ r = rr
+ } else {
+ return 0, errors.Errorf("invalid character: %q", v)
+ }
+ }
+
+ return r, nil
+}