diff options
Diffstat (limited to 'tpl/transform/unmarshal.go')
-rw-r--r-- | tpl/transform/unmarshal.go | 93 |
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 +} |