remarshal.go (2090B)
1 package transform
2
3 import (
4 "bytes"
5 "strings"
6
7 "errors"
8
9 "github.com/gohugoio/hugo/parser"
10 "github.com/gohugoio/hugo/parser/metadecoders"
11 "github.com/spf13/cast"
12 )
13
14 // Remarshal is used in the Hugo documentation to convert configuration
15 // examples from YAML to JSON, TOML (and possibly the other way around).
16 // The is primarily a helper for the Hugo docs site.
17 // It is not a general purpose YAML to TOML converter etc., and may
18 // change without notice if it serves a purpose in the docs.
19 // Format is one of json, yaml or toml.
20 func (ns *Namespace) Remarshal(format string, data any) (string, error) {
21 var meta map[string]any
22
23 format = strings.TrimSpace(strings.ToLower(format))
24
25 mark, err := toFormatMark(format)
26 if err != nil {
27 return "", err
28 }
29
30 if m, ok := data.(map[string]any); ok {
31 meta = m
32 } else {
33 from, err := cast.ToStringE(data)
34 if err != nil {
35 return "", err
36 }
37
38 from = strings.TrimSpace(from)
39 if from == "" {
40 return "", nil
41 }
42
43 fromFormat := metadecoders.Default.FormatFromContentString(from)
44 if fromFormat == "" {
45 return "", errors.New("failed to detect format from content")
46 }
47
48 meta, err = metadecoders.Default.UnmarshalToMap([]byte(from), fromFormat)
49 if err != nil {
50 return "", err
51 }
52 }
53
54 // Make it so 1.0 float64 prints as 1 etc.
55 applyMarshalTypes(meta)
56
57 var result bytes.Buffer
58 if err := parser.InterfaceToConfig(meta, mark, &result); err != nil {
59 return "", err
60 }
61
62 return result.String(), nil
63 }
64
65 // The unmarshal/marshal dance is extremely type lossy, and we need
66 // to make sure that integer types prints as "43" and not "43.0" in
67 // all formats, hence this hack.
68 func applyMarshalTypes(m map[string]any) {
69 for k, v := range m {
70 switch t := v.(type) {
71 case map[string]any:
72 applyMarshalTypes(t)
73 case float64:
74 i := int64(t)
75 if t == float64(i) {
76 m[k] = i
77 }
78 }
79 }
80 }
81
82 func toFormatMark(format string) (metadecoders.Format, error) {
83 if f := metadecoders.FormatFromString(format); f != "" {
84 return f, nil
85 }
86
87 return "", errors.New("failed to detect target data serialization format")
88 }