diff options
Diffstat (limited to 'parser/metadecoders/decoder.go')
-rw-r--r-- | parser/metadecoders/decoder.go | 64 |
1 files changed, 56 insertions, 8 deletions
diff --git a/parser/metadecoders/decoder.go b/parser/metadecoders/decoder.go index 6da791c73..0ca8575fe 100644 --- a/parser/metadecoders/decoder.go +++ b/parser/metadecoders/decoder.go @@ -14,6 +14,8 @@ package metadecoders import ( + "bytes" + "encoding/csv" "encoding/json" "fmt" @@ -27,22 +29,37 @@ import ( yaml "gopkg.in/yaml.v2" ) +// Decoder provides some configuration options for the decoders. +type Decoder struct { + // Comma is the field delimiter used in the CSV decoder. It defaults to ','. + Comma rune + + // Comment, if not 0, is the comment character ued in the CSV decoder. Lines beginning with the + // Comment character without preceding whitespace are ignored. + Comment rune +} + +// Default is a Decoder in its default configuration. +var Default = Decoder{ + Comma: ',', +} + // UnmarshalToMap will unmarshall data in format f into a new map. This is // what's needed for Hugo's front matter decoding. -func UnmarshalToMap(data []byte, f Format) (map[string]interface{}, error) { +func (d Decoder) UnmarshalToMap(data []byte, f Format) (map[string]interface{}, error) { m := make(map[string]interface{}) if data == nil { return m, nil } - err := unmarshal(data, f, &m) + err := d.unmarshal(data, f, &m) return m, err } // UnmarshalFileToMap is the same as UnmarshalToMap, but reads the data from // the given filename. -func UnmarshalFileToMap(fs afero.Fs, filename string) (map[string]interface{}, error) { +func (d Decoder) UnmarshalFileToMap(fs afero.Fs, filename string) (map[string]interface{}, error) { format := FormatFromString(filename) if format == "" { return nil, errors.Errorf("%q is not a valid configuration format", filename) @@ -52,23 +69,29 @@ func UnmarshalFileToMap(fs afero.Fs, filename string) (map[string]interface{}, e if err != nil { return nil, err } - return UnmarshalToMap(data, format) + return d.UnmarshalToMap(data, format) } // Unmarshal will unmarshall data in format f into an interface{}. // This is what's needed for Hugo's /data handling. -func Unmarshal(data []byte, f Format) (interface{}, error) { +func (d Decoder) Unmarshal(data []byte, f Format) (interface{}, error) { if data == nil { - return make(map[string]interface{}), nil + switch f { + case CSV: + return make([][]string, 0), nil + default: + return make(map[string]interface{}), nil + } + } var v interface{} - err := unmarshal(data, f, &v) + err := d.unmarshal(data, f, &v) return v, err } // unmarshal unmarshals data in format f into v. -func unmarshal(data []byte, f Format, v interface{}) error { +func (d Decoder) unmarshal(data []byte, f Format, v interface{}) error { var err error @@ -116,6 +139,9 @@ func unmarshal(data []byte, f Format, v interface{}) error { *v.(*interface{}) = mm } } + case CSV: + return d.unmarshalCSV(data, v) + default: return errors.Errorf("unmarshal of format %q is not supported", f) } @@ -128,6 +154,28 @@ func unmarshal(data []byte, f Format, v interface{}) error { } +func (d Decoder) unmarshalCSV(data []byte, v interface{}) error { + r := csv.NewReader(bytes.NewReader(data)) + r.Comma = d.Comma + r.Comment = d.Comment + + records, err := r.ReadAll() + if err != nil { + return err + } + + switch v.(type) { + case *interface{}: + *v.(*interface{}) = records + default: + return errors.Errorf("CSV cannot be unmarshaled into %T", v) + + } + + return nil + +} + func toFileError(f Format, err error) error { return herrors.ToFileError(string(f), err) } |