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 }