github.com/lmorg/murex@v0.0.0-20240217211045-e081c89cd4ef/utils/json/unmarshal.go (about)

     1  package json
     2  
     3  import (
     4  	"encoding/json"
     5  	"errors"
     6  	"fmt"
     7  	"strconv"
     8  	"strings"
     9  
    10  	"github.com/lmorg/murex/utils/mxjson"
    11  )
    12  
    13  // Unmarshal is a wrapper around the standard json.Unmarshal function. This is
    14  // done this way so that murex can swap out the JSON unmarshaller from the
    15  // standard libraries with a 3rd party decoder that might run more efficiently.
    16  func Unmarshal(data []byte, v interface{}) (err error) {
    17  	//err = gojay.Unmarshal(data, v)
    18  	//if err == nil {
    19  	//	return
    20  	//}
    21  
    22  	return json.Unmarshal(data, v)
    23  }
    24  
    25  // UnmarshalMurex is a wrapper around Go's JSON unmarshaller to support nested
    26  // brace quotes (which allows for a cleaner syntax when embedding Murex code as
    27  // JSON strings) and line comments via the hash, `#`, prefix.
    28  func UnmarshalMurex(data []byte, v interface{}) error {
    29  	err := unmarshalMurex(data, v)
    30  	if err == nil {
    31  		return nil
    32  	}
    33  
    34  	_, mxerr := mxjson.Parse(data)
    35  	return fmt.Errorf("mxjson parse error: %s\n%s", err, mxerr)
    36  }
    37  
    38  func unmarshalMurex(data []byte, v interface{}) error {
    39  	var (
    40  		escape   bool
    41  		comment  bool
    42  		comments = make([]string, 1)
    43  		single   bool
    44  		double   bool
    45  		brace    int
    46  		replace  []string
    47  		pop      []byte
    48  	)
    49  
    50  	for i := range data {
    51  		if brace > 0 {
    52  			pop = append(pop, data[i])
    53  		}
    54  
    55  		switch data[i] {
    56  		case '\\':
    57  			switch {
    58  			case comment:
    59  				comments[len(comments)-1] += string(data[i])
    60  			default:
    61  				escape = !escape
    62  			}
    63  
    64  		case '#':
    65  			switch {
    66  			case escape:
    67  				escape = false
    68  			case single, double, brace > 0:
    69  				// do nothing
    70  			default:
    71  				comment = true
    72  				comments[len(comments)-1] += string(data[i])
    73  			}
    74  
    75  		case '\'':
    76  			switch {
    77  			case comment:
    78  				comments[len(comments)-1] += string(data[i])
    79  			case escape:
    80  				escape = false
    81  			case double, brace > 0:
    82  				// do nothing
    83  			case single:
    84  				single = false
    85  			default:
    86  				single = true
    87  			}
    88  
    89  		case '"':
    90  			switch {
    91  			case comment:
    92  				comments[len(comments)-1] += string(data[i])
    93  			case escape:
    94  				escape = false
    95  			case single, brace > 0:
    96  				// do nothing
    97  			case double:
    98  				double = false
    99  			default:
   100  				double = true
   101  			}
   102  
   103  		case '(':
   104  			switch {
   105  			case comment:
   106  				comments[len(comments)-1] += string(data[i])
   107  			case escape:
   108  				escape = false
   109  			case single, double:
   110  				// do nothing
   111  			case brace == 0:
   112  				//pop = append(pop, data[i])
   113  				brace++
   114  			default:
   115  				brace++
   116  			}
   117  
   118  		case ')':
   119  			switch {
   120  			case comment:
   121  				comments[len(comments)-1] += string(data[i])
   122  			case escape:
   123  				escape = false
   124  			case single, double:
   125  				// do nothing
   126  			case brace == 1:
   127  				replace = append(replace, string(pop[:len(pop)-1]))
   128  				pop = []byte{}
   129  				brace--
   130  			default:
   131  				brace--
   132  			}
   133  
   134  		case '\n':
   135  			switch {
   136  			case comment:
   137  				comment = false
   138  				comments = append(comments, "")
   139  			case escape:
   140  				escape = false
   141  			}
   142  
   143  		default:
   144  			switch {
   145  			case comment:
   146  				comments[len(comments)-1] += string(data[i])
   147  			default:
   148  				escape = false
   149  			}
   150  		}
   151  
   152  	}
   153  
   154  	switch {
   155  	case comment:
   156  		comments = append(comments, "")
   157  	case single:
   158  		return errors.New("unterminated single quotes")
   159  	case double:
   160  		return errors.New("unterminated double quotes")
   161  	case brace > 0:
   162  		return fmt.Errorf("%d more open brace(s) than closed", brace)
   163  	case brace < 0:
   164  		return fmt.Errorf("%d more closed brace(s) than opened", brace)
   165  	}
   166  
   167  	// nothing to do so might as well just forward the params on without editing
   168  	if len(replace) == 0 && len(comments) == 1 {
   169  		return json.Unmarshal(data, v)
   170  	}
   171  
   172  	s := string(data)
   173  
   174  	for i := range comments {
   175  		s = strings.Replace(s, comments[i], "", 1)
   176  	}
   177  
   178  	for i := range replace {
   179  		if len(replace[i]) > 0 {
   180  			s = strings.Replace(s, "("+replace[i]+")", strconv.Quote(replace[i]), 1)
   181  		}
   182  	}
   183  
   184  	return Unmarshal([]byte(s), v)
   185  }