github.com/kovansky/hugo@v0.92.3-0.20220224232819-63076e4ff19f/tpl/transform/remarshal.go (about)

     1  package transform
     2  
     3  import (
     4  	"bytes"
     5  	"strings"
     6  
     7  	"github.com/pkg/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 interface{}) (string, error) {
    21  	var meta map[string]interface{}
    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]interface{}); 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]interface{}) {
    69  	for k, v := range m {
    70  		switch t := v.(type) {
    71  		case map[string]interface{}:
    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  }