github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/examples/gno.land/p/demo/json/encode.gno (about)

     1  package json
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"math"
     7  	"strconv"
     8  
     9  	"gno.land/p/demo/json/ryu"
    10  	"gno.land/p/demo/ufmt"
    11  )
    12  
    13  // Marshal returns the JSON encoding of a Node.
    14  func Marshal(node *Node) ([]byte, error) {
    15  	var (
    16  		buf  bytes.Buffer
    17  		sVal string
    18  		bVal bool
    19  		nVal float64
    20  		oVal []byte
    21  		err  error
    22  	)
    23  
    24  	if node == nil {
    25  		return nil, errors.New("node is nil")
    26  	}
    27  
    28  	if !node.modified && !node.ready() {
    29  		return nil, errors.New("node is not ready")
    30  	}
    31  
    32  	if !node.modified && node.ready() {
    33  		buf.Write(node.source())
    34  	}
    35  
    36  	if node.modified {
    37  		switch node.nodeType {
    38  		case Null:
    39  			buf.Write(nullLiteral)
    40  
    41  		case Number:
    42  			nVal, err = node.GetNumeric()
    43  			if err != nil {
    44  				return nil, err
    45  			}
    46  
    47  			// ufmt does not support %g. by doing so, we need to check if the number is an integer
    48  			// after then, apply the correct format for each float and integer numbers.
    49  			if math.Mod(nVal, 1.0) == 0 {
    50  				// must convert float to integer. otherwise it will be overflowed.
    51  				num := ufmt.Sprintf("%d", int(nVal))
    52  				buf.WriteString(num)
    53  			} else {
    54  				// use ryu algorithm to convert float to string
    55  				num := ryu.FormatFloat64(nVal)
    56  				buf.WriteString(num)
    57  			}
    58  
    59  		case String:
    60  			sVal, err = node.GetString()
    61  			if err != nil {
    62  				return nil, err
    63  			}
    64  
    65  			quoted := ufmt.Sprintf("%s", strconv.Quote(sVal))
    66  			buf.WriteString(quoted)
    67  
    68  		case Boolean:
    69  			bVal, err = node.GetBool()
    70  			if err != nil {
    71  				return nil, err
    72  			}
    73  
    74  			bStr := ufmt.Sprintf("%t", bVal)
    75  			buf.WriteString(bStr)
    76  
    77  		case Array:
    78  			buf.WriteByte(bracketOpen)
    79  
    80  			for i := 0; i < len(node.next); i++ {
    81  				if i != 0 {
    82  					buf.WriteByte(comma)
    83  				}
    84  
    85  				elem, ok := node.next[strconv.Itoa(i)]
    86  				if !ok {
    87  					return nil, ufmt.Errorf("array element %d is not found", i)
    88  				}
    89  
    90  				oVal, err = Marshal(elem)
    91  				if err != nil {
    92  					return nil, err
    93  				}
    94  
    95  				buf.Write(oVal)
    96  			}
    97  
    98  			buf.WriteByte(bracketClose)
    99  
   100  		case Object:
   101  			buf.WriteByte(curlyOpen)
   102  
   103  			bVal = false
   104  			for k, v := range node.next {
   105  				if bVal {
   106  					buf.WriteByte(comma)
   107  				} else {
   108  					bVal = true
   109  				}
   110  
   111  				key := ufmt.Sprintf("%s", strconv.Quote(k))
   112  				buf.WriteString(key)
   113  				buf.WriteByte(colon)
   114  
   115  				oVal, err = Marshal(v)
   116  				if err != nil {
   117  					return nil, err
   118  				}
   119  
   120  				buf.Write(oVal)
   121  			}
   122  
   123  			buf.WriteByte(curlyClose)
   124  		}
   125  	}
   126  
   127  	return buf.Bytes(), nil
   128  }