github.com/ndau/noms@v1.0.5/go/util/json/to_json.go (about)

     1  // Copyright 2019 Attic Labs, Inc. All rights reserved.
     2  // Licensed under the Apache License, version 2.0:
     3  // http://www.apache.org/licenses/LICENSE-2.0
     4  
     5  package json
     6  
     7  import (
     8  	"encoding/json"
     9  	"errors"
    10  	"fmt"
    11  	"io"
    12  
    13  	"github.com/ndau/noms/go/types"
    14  )
    15  
    16  // ToJSON encodes a Noms value as JSON.
    17  func ToJSON(v types.Value, w io.Writer, opts ToOptions) error {
    18  	// TODO: This is a quick hack that is expedient. We should marshal directly to the writer without
    19  	// allocating a bunch of Go values.
    20  	p, err := toPile(v, opts)
    21  	if err != nil {
    22  		return err
    23  	}
    24  
    25  	enc := json.NewEncoder(w)
    26  	enc.SetIndent("", opts.Indent)
    27  	return enc.Encode(p)
    28  }
    29  
    30  // ToOptions controls how ToJSON works.
    31  type ToOptions struct {
    32  	// Enable support for encoding Noms Lists. Lists are encoded as JSON arrays.
    33  	Lists bool
    34  	// Enable support for encoding Noms Maps. Maps are encoded as JSON objects.
    35  	Maps bool
    36  	// Enable support for encoding Noms Sets. Sets are encoded as JSON arrays.
    37  	Sets bool
    38  	// Enable support for encoding Noms Structs. Structs are encoded as JSON objects.
    39  	Structs bool
    40  	// String to use for indent when pretty-printing
    41  	Indent string
    42  }
    43  
    44  func toPile(v types.Value, opts ToOptions) (ret interface{}, err error) {
    45  	switch v := v.(type) {
    46  	case types.Bool:
    47  		return bool(v), nil
    48  	case types.Number:
    49  		return float64(v), nil
    50  	case types.String:
    51  		return string(v), nil
    52  	case types.Struct:
    53  		if !opts.Structs {
    54  			return nil, errors.New("Struct marshaling not enabled")
    55  		}
    56  		r := map[string]interface{}{}
    57  		if v.Name() != "" {
    58  			return nil, errors.New("Named struct marshaling not supported")
    59  		}
    60  		v.IterFields(func(k string, cv types.Value) (stop bool) {
    61  			var cp interface{}
    62  			cp, err = toPile(cv, opts)
    63  			if err != nil {
    64  				return true
    65  			}
    66  			r[k] = cp
    67  			return false
    68  		})
    69  		return r, err
    70  	case types.Map:
    71  		if !opts.Maps {
    72  			return nil, errors.New("Map marshaling not enabled")
    73  		}
    74  		r := make(map[string]interface{}, v.Len())
    75  		v.Iter(func(k, cv types.Value) (stop bool) {
    76  			sk, ok := k.(types.String)
    77  			if !ok {
    78  				err = fmt.Errorf("Map key kind %s not supported", types.KindToString[k.Kind()])
    79  				return true
    80  			}
    81  			var cp interface{}
    82  			cp, err = toPile(cv, opts)
    83  			if err != nil {
    84  				return true
    85  			}
    86  			r[string(sk)] = cp
    87  			return false
    88  		})
    89  		return r, err
    90  	case types.List:
    91  		if !opts.Lists {
    92  			return nil, errors.New("List marshaling not enabled")
    93  		}
    94  		r := make([]interface{}, v.Len())
    95  		v.Iter(func(cv types.Value, i uint64) (stop bool) {
    96  			var cp interface{}
    97  			cp, err = toPile(cv, opts)
    98  			if err != nil {
    99  				return true
   100  			}
   101  			r[i] = cp
   102  			return false
   103  		})
   104  		return r, err
   105  	case types.Set:
   106  		if !opts.Sets {
   107  			return nil, errors.New("Set marshaling not enabled")
   108  		}
   109  		r := make([]interface{}, 0, v.Len())
   110  		v.Iter(func(cv types.Value) (stop bool) {
   111  			var cp interface{}
   112  			cp, err = toPile(cv, opts)
   113  			if err != nil {
   114  				return true
   115  			}
   116  			r = append(r, cp)
   117  			return false
   118  		})
   119  		return r, err
   120  	}
   121  	return nil, fmt.Errorf("Unsupported kind: %s", types.KindToString[v.Kind()])
   122  }