<- Back to shtanton's homepage
aboutsummaryrefslogtreecommitdiff
path: root/main/json.go
diff options
context:
space:
mode:
authorCharlie Stanton <charlie@shtanton.xyz>2023-02-19 09:27:55 +0000
committerCharlie Stanton <charlie@shtanton.xyz>2023-02-19 09:27:55 +0000
commit3bd45dc49a35b82dcc4ae93796c3e152d799bc0b (patch)
tree3a681ac5dbd777d2b6b116429cfbd934815661ce /main/json.go
parenta5a4db8283fda88c5bd42272de0258e5d134c5bd (diff)
downloadstred-go-3bd45dc49a35b82dcc4ae93796c3e152d799bc0b.tar
Move JSON serialising, deserialising and walking code into a separate package
Diffstat (limited to 'main/json.go')
-rw-r--r--main/json.go293
1 files changed, 0 insertions, 293 deletions
diff --git a/main/json.go b/main/json.go
index 77c3733..06ab7d0 100644
--- a/main/json.go
+++ b/main/json.go
@@ -1,294 +1 @@
package main
-
-import (
- "io"
- "encoding/json"
- "fmt"
-)
-
-type WalkItemStream struct {
- channel chan WalkItem
- rewinds []WalkItem
-}
-
-func (stream *WalkItemStream) next() (WalkItem, bool) {
- if len(stream.rewinds) == 0 {
- item, hasItem := <- stream.channel
- return item, hasItem
- }
- item := stream.rewinds[len(stream.rewinds)-1]
- stream.rewinds = stream.rewinds[0:len(stream.rewinds)-1]
- return item, true
-}
-
-func (stream *WalkItemStream) rewind(item WalkItem) {
- stream.rewinds = append(stream.rewinds, item)
-}
-
-func (stream *WalkItemStream) peek() (WalkItem, bool) {
- item, hasItem := stream.next()
- if !hasItem {
- return item, false
- }
- stream.rewind(item)
- return item, true
-}
-
-func tokenToValue(token json.Token) WalkValue {
- switch token.(type) {
- case nil:
- return ValueNull {}
- case bool:
- return ValueBool(token.(bool))
- case float64:
- return ValueNumber(token.(float64))
- case string:
- return ValueString(token.(string))
- default:
- panic("Can't convert JSON token to value")
- }
-}
-
-func readValue(dec *json.Decoder, path Path, out chan WalkItem) bool {
- if !dec.More() {
- return true
- }
- t, err := dec.Token()
- if err == io.EOF {
- return true
- } else if err != nil {
- panic("Invalid JSON")
- }
- switch t.(type) {
- case nil, string, float64, bool:
- v := tokenToValue(t)
- out <- WalkItem {v, path}
- return false
- case json.Delim:
- switch rune(t.(json.Delim)) {
- case '[':
- out <- WalkItem {ArrayBegin, path}
- index := 0
- for dec.More() {
- empty := readValue(dec, append(path, index), out)
- if empty {
- break
- }
- index += 1
- }
- t, err := dec.Token()
- if err != nil {
- panic("Invalid JSON")
- }
- delim, isDelim := t.(json.Delim)
- if !isDelim || delim != ']' {
- panic("Expected ] in JSON")
- }
- out <- WalkItem{ArrayEnd, path}
- return false
- case '{':
- out <- WalkItem {MapBegin, path}
- for dec.More() {
- t, _ := dec.Token()
- key, keyIsString := t.(string)
- if !keyIsString {
- panic("Invalid JSON")
- }
- empty := readValue(dec, append(path, key), out)
- if empty {
- panic("Invalid JSON")
- }
- }
- t, err := dec.Token()
- if err != nil {
- panic("Invalid JSON")
- }
- delim, isDelim := t.(json.Delim)
- if !isDelim || delim != '}' {
- panic("Expected } in JSON")
- }
- out <- WalkItem {MapEnd, path}
- return false
- default:
- panic("Error parsing JSON")
- }
- default:
- panic("Invalid JSON token")
- }
-}
-
-func startWalk(dec *json.Decoder, out chan WalkItem) {
- isEmpty := readValue(dec, nil, out)
- if isEmpty {
- panic("Missing JSON input")
- }
- close(out)
-}
-
-func Json(r io.Reader) chan WalkItem {
- dec := json.NewDecoder(r)
- out := make(chan WalkItem)
- go startWalk(dec, out)
- return out
-}
-
-func printIndent(indent int) {
- for i := 0; i < indent; i += 1 {
- fmt.Print("\t")
- }
-}
-
-func jsonOutArray(in *WalkItemStream, indent int) {
- fmt.Println("[")
- token, hasToken := in.next()
- if !hasToken {
- panic("Missing ] in output JSON")
- }
- terminal, isTerminal := token.value.(TerminalValue)
- if isTerminal && terminal == ArrayEnd {
- fmt.Print("\n")
- printIndent(indent)
- fmt.Print("]")
- return
- }
- in.rewind(token)
- for {
- valueToken := jsonOutValue(in, indent + 1, true)
- if valueToken != nil {
- panic("Missing value in output JSON array")
- }
- token, hasToken := in.next()
- if !hasToken {
- panic("Missing ] in output JSON")
- }
- terminal, isTerminal := token.value.(TerminalValue)
- if isTerminal && terminal == ArrayEnd {
- fmt.Print("\n")
- printIndent(indent)
- fmt.Print("]")
- return
- }
- in.rewind(token)
- fmt.Println(",")
- }
-}
-
-func jsonOutMap(in *WalkItemStream, indent int) {
- fmt.Println("{")
- token, hasToken := in.next()
- if !hasToken {
- panic("Missing } in output JSON")
- }
- terminal, isTerminal := token.value.(TerminalValue)
- if isTerminal && terminal == MapEnd {
- fmt.Print("\n")
- printIndent(indent)
- fmt.Print("}")
- return
- }
- in.rewind(token)
- for {
- keyToken, hasKeyToken := in.peek()
- if !hasKeyToken {
- panic("Missing map element")
- }
- printIndent(indent + 1)
- if len(keyToken.path) == 0 {
- panic("Map element missing key")
- }
- key := keyToken.path[len(keyToken.path)-1]
- switch key.(type) {
- case int:
- fmt.Print(key.(int))
- case string:
- fmt.Printf("%q", key.(string))
- default:
- panic("Invalid path segment")
- }
- fmt.Print(": ")
- valueToken := jsonOutValue(in, indent + 1, false)
- if valueToken != nil {
- panic("Missing value int output JSON map")
- }
- token, hasToken := in.next()
- if !hasToken {
- panic("Missing } in output JSON")
- }
- terminal, isTerminal := token.value.(TerminalValue)
- if isTerminal && terminal == MapEnd {
- fmt.Print("\n")
- printIndent(indent)
- fmt.Print("}")
- return
- }
- in.rewind(token)
- fmt.Println(",")
- }
-}
-
-func jsonOutValue(in *WalkItemStream, indent int, doIndent bool) WalkValue {
- token, hasToken := in.next()
- if !hasToken {
- panic("Missing JSON token in output")
- }
- switch token.value.(type) {
- case ValueNull:
- if doIndent {
- printIndent(indent)
- }
- fmt.Printf("null")
- return nil
- case ValueBool:
- if doIndent {
- printIndent(indent)
- }
- if token.value.(ValueBool) {
- fmt.Print("true")
- } else {
- fmt.Print("false")
- }
- return nil
- case ValueNumber:
- if doIndent {
- printIndent(indent)
- }
- fmt.Printf("%v", token.value)
- return nil
- case ValueString:
- if doIndent {
- printIndent(indent)
- }
- fmt.Printf("%q", token.value)
- return nil
- case TerminalValue:
- switch token.value.(TerminalValue) {
- case ArrayBegin:
- if doIndent {
- printIndent(indent)
- }
- jsonOutArray(in, indent)
- return nil
- case MapBegin:
- if doIndent {
- printIndent(indent)
- }
- jsonOutMap(in, indent)
- return nil
- default:
- return token
- }
- default:
- panic("Invalid WalkValue")
- }
-}
-
-func JsonOut(in chan WalkItem) {
- stream := WalkItemStream {
- channel: in,
- rewinds: nil,
- }
- if jsonOutValue(&stream, 0, true) != nil {
- panic("Invalid output JSON")
- }
- fmt.Print("\n")
-}