github.com/philpearl/plenc@v0.0.15/plenccodec/output.go (about)

     1  package plenccodec
     2  
     3  import (
     4  	"strconv"
     5  	"time"
     6  )
     7  
     8  // Outputter is an interface that a Descriptor uses to turn plenc data to some
     9  // other output
    10  type Outputter interface {
    11  	StartObject()
    12  	EndObject()
    13  	StartArray()
    14  	EndArray()
    15  	NameField(name string)
    16  
    17  	Int64(v int64)
    18  	Uint64(v uint64)
    19  	Float64(v float64)
    20  	Float32(v float32)
    21  	String(v string)
    22  	Bool(v bool)
    23  	Time(t time.Time)
    24  	Raw(v string)
    25  }
    26  
    27  // JSONOutput converts Descriptor output to JSON.
    28  type JSONOutput struct {
    29  	data    []byte
    30  	depth   int
    31  	inField bool
    32  
    33  	stack []stackEntry
    34  }
    35  
    36  type state int
    37  
    38  const (
    39  	stateValue state = iota
    40  	stateKey
    41  	stateObjValue
    42  )
    43  
    44  type stackEntry struct {
    45  	state state
    46  }
    47  
    48  func (j *JSONOutput) Done() []byte {
    49  	j.end()
    50  	return j.data
    51  }
    52  
    53  func (j *JSONOutput) Reset() {
    54  	j.data = j.data[:0]
    55  	j.depth = 0
    56  	j.inField = false
    57  	j.stack = j.stack[:0]
    58  }
    59  
    60  func (j *JSONOutput) prefix() {
    61  	if j.inField {
    62  		j.inField = false
    63  		return
    64  	}
    65  	for i := 0; i < j.depth; i++ {
    66  		j.data = append(j.data, "  "...)
    67  	}
    68  }
    69  
    70  func (j *JSONOutput) end() {
    71  	if j.depth == 0 {
    72  		j.data = append(j.data, '\n')
    73  		return
    74  	}
    75  	j.depth--
    76  	j.stack = j.stack[:len(j.stack)-1]
    77  	l := len(j.data)
    78  	if l < 2 {
    79  		return
    80  	}
    81  	if j.data[l-2] == ',' && j.data[l-1] == '\n' {
    82  		j.data[l-2] = '\n'
    83  		j.data = j.data[:l-1]
    84  	}
    85  }
    86  
    87  func (j *JSONOutput) punctuate() {
    88  	if len(j.stack) == 0 {
    89  		return
    90  	}
    91  	s := &j.stack[len(j.stack)-1]
    92  	switch s.state {
    93  	case stateKey:
    94  		j.data = append(j.data, ": "...)
    95  		s.state = stateObjValue
    96  	case stateObjValue:
    97  		j.data = append(j.data, ",\n"...)
    98  		s.state = stateKey
    99  	case stateValue:
   100  		j.data = append(j.data, ",\n"...)
   101  	}
   102  }
   103  
   104  func (j *JSONOutput) StartObject() {
   105  	j.prefix()
   106  	j.data = append(j.data, "{\n"...)
   107  	j.depth++
   108  	j.stack = append(j.stack, stackEntry{state: stateKey})
   109  }
   110  
   111  func (j *JSONOutput) EndObject() {
   112  	j.end()
   113  	j.prefix()
   114  	j.data = append(j.data, '}')
   115  	j.punctuate()
   116  }
   117  
   118  func (j *JSONOutput) StartArray() {
   119  	j.prefix()
   120  	j.data = append(j.data, "[\n"...)
   121  	j.stack = append(j.stack, stackEntry{state: stateValue})
   122  	j.depth++
   123  }
   124  
   125  func (j *JSONOutput) EndArray() {
   126  	j.end()
   127  	j.prefix()
   128  	j.data = append(j.data, ']')
   129  	j.punctuate()
   130  }
   131  
   132  func (j *JSONOutput) NameField(name string) {
   133  	j.prefix()
   134  	j.inField = true
   135  	j.data = j.appendString(j.data, name)
   136  	j.punctuate()
   137  }
   138  
   139  func (j *JSONOutput) Int64(v int64) {
   140  	j.prefix()
   141  	j.data = strconv.AppendInt(j.data, v, 10)
   142  	j.punctuate()
   143  }
   144  
   145  func (j *JSONOutput) Uint64(v uint64) {
   146  	j.prefix()
   147  	j.data = strconv.AppendUint(j.data, v, 10)
   148  	j.punctuate()
   149  }
   150  
   151  func (j *JSONOutput) Float64(v float64) {
   152  	j.prefix()
   153  	j.data = strconv.AppendFloat(j.data, v, 'g', -1, 64)
   154  	j.punctuate()
   155  }
   156  
   157  func (j *JSONOutput) Float32(v float32) {
   158  	j.prefix()
   159  	j.data = strconv.AppendFloat(j.data, float64(v), 'g', -1, 64)
   160  	j.punctuate()
   161  }
   162  
   163  func (j *JSONOutput) String(v string) {
   164  	j.prefix()
   165  	j.data = j.appendString(j.data, v)
   166  	j.punctuate()
   167  }
   168  
   169  func (j *JSONOutput) Raw(v string) {
   170  	j.prefix()
   171  	j.data = append(j.data, v...)
   172  	j.punctuate()
   173  }
   174  
   175  func (j *JSONOutput) Bool(v bool) {
   176  	j.prefix()
   177  	j.data = strconv.AppendBool(j.data, v)
   178  	j.punctuate()
   179  }
   180  
   181  func (j *JSONOutput) Time(t time.Time) {
   182  	j.prefix()
   183  	j.data = t.AppendFormat(j.data, `"`+time.RFC3339Nano+`"`)
   184  	j.punctuate()
   185  }
   186  
   187  const hex = "0123456789abcdef"
   188  
   189  func (j *JSONOutput) appendString(data []byte, v string) []byte {
   190  	data = append(data, '"')
   191  	for i := 0; i < len(v); i++ {
   192  		c := v[i]
   193  		switch c {
   194  		case '\\', '"':
   195  			data = append(data, '\\', c)
   196  		case '\n':
   197  			data = append(data, '\\', 'n')
   198  		case '\r':
   199  			data = append(data, '\\', 'r')
   200  		case '\t':
   201  			data = append(data, '\\', 't')
   202  		default:
   203  			if c < 32 {
   204  				data = append(data, '\\', 'u', '0', '0', hex[c>>4], hex[c&0xF])
   205  			} else {
   206  				// append in its natural form
   207  				data = append(data, c)
   208  			}
   209  		}
   210  	}
   211  	data = append(data, '"')
   212  	return data
   213  }