github.com/dirkolbrich/hugo@v0.47.1/tpl/transform/remarshal.go (about)

     1  package transform
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"strings"
     7  
     8  	"github.com/gohugoio/hugo/parser"
     9  	"github.com/spf13/cast"
    10  )
    11  
    12  // Remarshal is used in the Hugo documentation to convert configuration
    13  // examples from YAML to JSON, TOML (and possibly the other way around).
    14  // The is primarily a helper for the Hugo docs site.
    15  // It is not a general purpose YAML to TOML converter etc., and may
    16  // change without notice if it serves a purpose in the docs.
    17  // Format is one of json, yaml or toml.
    18  func (ns *Namespace) Remarshal(format string, data interface{}) (string, error) {
    19  	from, err := cast.ToStringE(data)
    20  	if err != nil {
    21  		return "", err
    22  	}
    23  
    24  	from = strings.TrimSpace(from)
    25  	format = strings.TrimSpace(strings.ToLower(format))
    26  
    27  	if from == "" {
    28  		return "", nil
    29  	}
    30  
    31  	mark, err := toFormatMark(format)
    32  	if err != nil {
    33  		return "", err
    34  	}
    35  
    36  	fromFormat, err := detectFormat(from)
    37  	if err != nil {
    38  		return "", err
    39  	}
    40  
    41  	var metaHandler func(d []byte) (map[string]interface{}, error)
    42  
    43  	switch fromFormat {
    44  	case "yaml":
    45  		metaHandler = parser.HandleYAMLMetaData
    46  	case "toml":
    47  		metaHandler = parser.HandleTOMLMetaData
    48  	case "json":
    49  		metaHandler = parser.HandleJSONMetaData
    50  	}
    51  
    52  	meta, err := metaHandler([]byte(from))
    53  	if err != nil {
    54  		return "", err
    55  	}
    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  func toFormatMark(format string) (rune, error) {
    66  	// TODO(bep) the parser package needs a cleaning.
    67  	switch format {
    68  	case "yaml":
    69  		return rune(parser.YAMLLead[0]), nil
    70  	case "toml":
    71  		return rune(parser.TOMLLead[0]), nil
    72  	case "json":
    73  		return rune(parser.JSONLead[0]), nil
    74  	}
    75  
    76  	return 0, errors.New("failed to detect target data serialization format")
    77  }
    78  
    79  func detectFormat(data string) (string, error) {
    80  	jsonIdx := strings.Index(data, "{")
    81  	yamlIdx := strings.Index(data, ":")
    82  	tomlIdx := strings.Index(data, "=")
    83  
    84  	if jsonIdx != -1 && (yamlIdx == -1 || jsonIdx < yamlIdx) && (tomlIdx == -1 || jsonIdx < tomlIdx) {
    85  		return "json", nil
    86  	}
    87  
    88  	if yamlIdx != -1 && (tomlIdx == -1 || yamlIdx < tomlIdx) {
    89  		return "yaml", nil
    90  	}
    91  
    92  	if tomlIdx != -1 {
    93  		return "toml", nil
    94  	}
    95  
    96  	return "", errors.New("failed to detect data serialization format")
    97  
    98  }