import (
"bufio"
- "encoding/hex"
"encoding/json"
"flag"
"fmt"
"io"
"log"
"os"
- "slices"
- "strconv"
"strings"
- "time"
"go.cypherpunks.su/keks"
- "go.cypherpunks.su/keks/types"
- "go.cypherpunks.su/tai64n/v4"
)
func usage() {
pth []string
)
-func prindent(depth int) {
- fmt.Print(strings.Repeat(" ", depth))
-}
-
-func hexenc(b []byte) string {
- return strings.ToUpper(hex.EncodeToString(b))
-}
-
-func printbin(s []byte) {
- sLen := len(s)
- var dots string
- if sLen > int(*MaxStrLen) {
- s = s[:int(*MaxStrLen)]
- dots = "..."
- }
- if *onlyV {
- fmt.Println(hexenc(s))
- } else {
- fmt.Printf("%s%d:%s%s%s\n", Magenta, sLen, Reset, hexenc(s), dots)
- }
-}
-
-func isQuiet(where []string) bool {
- if len(pth) == 0 {
- return false
- }
- if len(pth) > len(where) {
- return true
- }
- return !slices.Equal(where[:len(pth)], pth)
-}
-
-func printer(iter *keks.Iterator, where []string, count int, inList, inMap bool) {
- var quiet bool
- for i := range count {
- if !iter.Next() {
- panic("unexpected")
- }
- depth := iter.Depth
- var key string
- if inMap {
- key = iter.Str()
- if !iter.Next() {
- panic("unexpected")
- }
- }
- if inList {
- where[len(where)-1] = strconv.Itoa(i)
- } else if inMap {
- where[len(where)-1] = key
- }
- quiet = isQuiet(where)
- if !quiet && !*onlyV {
- fmt.Print(Blue + strconv.Itoa(depth) + Reset)
- fmt.Printf(" %s%03d%s ", Red, iter.Offset(), Reset)
- prindent(depth)
- if inList {
- fmt.Printf("%s%d:%s ", Yellow, i, Reset)
- } else if inMap {
- fmt.Printf("%s%s:%s ", Green, key, Reset)
- }
- }
- var listOrMap bool
- switch iter.T {
- case types.List:
- listOrMap = true
- if !quiet && !*onlyV {
- fmt.Printf("[ %s%d%s\n", Cyan, iter.Len(), Reset)
- }
- printer(iter, append(where, "0"), iter.Len(), true, false)
- if !quiet && !*onlyV {
- prindent(depth)
- fmt.Println(" " + " ]")
- }
- case types.Map:
- listOrMap = true
- if !quiet && !*onlyV {
- fmt.Printf("{ %s%d%s\n", Cyan, iter.Len(), Reset)
- }
- printer(iter, append(where, key), iter.Len(), false, true)
- if !quiet && !*onlyV {
- prindent(depth)
- fmt.Println(" " + " }")
- }
- }
- if listOrMap || quiet {
- continue
- }
- switch iter.T {
- case types.NIL:
- fmt.Println("NIL")
- case types.Bool:
- if iter.Bool() {
- fmt.Println("TRUE")
- } else {
- fmt.Println("FALSE")
- }
- case types.Hexlet:
- h := iter.Hexlet()
- if *onlyV {
- fmt.Println(h.UUID())
- } else {
- fmt.Println(h.UUID(), h.IP())
- }
- case types.UInt:
- fmt.Println(iter.UInt())
- case types.Int:
- fmt.Println(iter.Int())
- case types.BigInt:
- fmt.Println(iter.BigInt())
- case types.Blob:
- blob := iter.Blob()
- if *onlyV {
- v, err := io.ReadAll(blob.Reader())
- if err != nil {
- log.Fatal(err)
- }
- fmt.Println(hex.EncodeToString(v))
- break
- }
- fmt.Printf("BLOB[ %s%d l=%d%s\n",
- Cyan, len(blob.Chunks), blob.ChunkLen, Reset)
- off := iter.Offset() + 1 + 8
- for i, chunk := range blob.Chunks {
- fmt.Print(Blue + strconv.Itoa(depth+1) + Reset)
- fmt.Printf(" %s%03d%s ", Red, off, Reset)
- prindent(depth + 1)
- fmt.Printf("%s%d:%s ", Yellow, i, Reset)
- printbin([]byte(chunk))
- off += int64(len(chunk)) + 1
- }
- prindent(depth)
- fmt.Println(" " + " ]")
- case types.TAI64:
- t := iter.TAI64().Time()
- utc, isLeap := tai64n.Leapsecs.Sub(t)
- if isLeap {
- fmt.Printf("TAI64(%s TAI, leap)\n", t.Format(time.DateTime))
- } else {
- fmt.Printf("TAI64(%s TAI, %s UTC)\n",
- t.Format(time.DateTime), utc.Format(time.DateTime))
- }
- case types.TAI64N:
- t := iter.TAI64N().Time()
- utc, isLeap := tai64n.Leapsecs.Sub(t)
- if isLeap {
- fmt.Printf("TAI64N(%s TAI, leap)\n",
- t.Format(time.DateTime+".000000000"))
- } else {
- fmt.Printf("TAI64N(%s TAI, %s UTC)\n",
- t.Format(time.DateTime+".000000000"),
- utc.Format(time.DateTime+".000000000"))
- }
- case types.TAI64NA:
- t := iter.TAI64NA()
- fmt.Printf("TAI64NA(%s)\n", hexenc(t[:]))
- case types.Magic:
- var builder strings.Builder
- for _, b := range []byte(iter.Magic()) {
- if strconv.IsPrint(rune(b)) {
- builder.WriteByte(b)
- } else {
- builder.WriteString(fmt.Sprintf("\\x%02x", b))
- }
- }
- fmt.Printf("Magic(%s)\n", builder.String())
- case types.Bin:
- printbin(iter.Bin())
- case types.Str:
- if !*onlyV {
- fmt.Print(`"`)
- }
- s := iter.Str()
- sLen := len(s)
- if sLen > int(*MaxStrLen) {
- fmt.Print(s[:int(*MaxStrLen)])
- fmt.Print("...")
- } else {
- fmt.Print(s)
- }
- if !*onlyV {
- fmt.Print(`"`)
- }
- fmt.Println("")
- case types.Raw:
- fmt.Printf("(l=%d v=%s)\n", len(iter.Raw()), hexenc(iter.Raw()))
- default:
- fmt.Println("???")
- }
- }
-}
-
func main() {
flag.Usage = usage
flag.Parse()
--- /dev/null
+// pp -- KEKS pretty printer
+// Copyright (C) 2024-2025 Sergey Matveev <stargrave@stargrave.org>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as
+// published by the Free Software Foundation, version 3 of the License.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+package main
+
+import (
+ "encoding/hex"
+ "fmt"
+ "io"
+ "log"
+ "slices"
+ "strconv"
+ "strings"
+ "time"
+
+ "go.cypherpunks.su/keks"
+ "go.cypherpunks.su/keks/types"
+ "go.cypherpunks.su/tai64n/v4"
+)
+
+func isQuiet(where []string) bool {
+ if len(pth) == 0 {
+ return false
+ }
+ if len(pth) > len(where) {
+ return true
+ }
+ return !slices.Equal(where[:len(pth)], pth)
+}
+
+func prindent(depth int) {
+ fmt.Print(strings.Repeat(" ", depth))
+}
+
+func hexenc(b []byte) string {
+ return strings.ToUpper(hex.EncodeToString(b))
+}
+
+func printbin(s []byte) {
+ sLen := len(s)
+ var dots string
+ if sLen > int(*MaxStrLen) {
+ s = s[:int(*MaxStrLen)]
+ dots = "..."
+ }
+ if *onlyV {
+ fmt.Println(hexenc(s))
+ } else {
+ fmt.Printf("%s%d:%s%s%s\n", Magenta, sLen, Reset, hexenc(s), dots)
+ }
+}
+
+func printer(iter *keks.Iterator, where []string, count int, inList, inMap bool) {
+ var quiet bool
+ for i := range count {
+ if !iter.Next() {
+ panic("unexpected")
+ }
+ depth := iter.Depth
+ var key string
+ if inMap {
+ key = iter.Str()
+ if !iter.Next() {
+ panic("unexpected")
+ }
+ }
+ if inList {
+ where[len(where)-1] = strconv.Itoa(i)
+ } else if inMap {
+ where[len(where)-1] = key
+ }
+ quiet = isQuiet(where)
+ if !quiet && !*onlyV {
+ fmt.Print(Blue + strconv.Itoa(depth) + Reset)
+ fmt.Printf(" %s%03d%s ", Red, iter.Offset(), Reset)
+ prindent(depth)
+ if inList {
+ fmt.Printf("%s%d:%s ", Yellow, i, Reset)
+ } else if inMap {
+ fmt.Printf("%s%s:%s ", Green, key, Reset)
+ }
+ }
+ var listOrMap bool
+ switch iter.T {
+ case types.List:
+ listOrMap = true
+ if !quiet && !*onlyV {
+ fmt.Printf("[ %s%d%s\n", Cyan, iter.Len(), Reset)
+ }
+ printer(iter, append(where, "0"), iter.Len(), true, false)
+ if !quiet && !*onlyV {
+ prindent(depth)
+ fmt.Println(" " + " ]")
+ }
+ case types.Map:
+ listOrMap = true
+ if !quiet && !*onlyV {
+ fmt.Printf("{ %s%d%s\n", Cyan, iter.Len(), Reset)
+ }
+ printer(iter, append(where, key), iter.Len(), false, true)
+ if !quiet && !*onlyV {
+ prindent(depth)
+ fmt.Println(" " + " }")
+ }
+ }
+ if listOrMap || quiet {
+ continue
+ }
+ switch iter.T {
+ case types.NIL:
+ fmt.Println("NIL")
+ case types.Bool:
+ if iter.Bool() {
+ fmt.Println("TRUE")
+ } else {
+ fmt.Println("FALSE")
+ }
+ case types.Hexlet:
+ h := iter.Hexlet()
+ if *onlyV {
+ fmt.Println(h.UUID())
+ } else {
+ fmt.Println(h.UUID(), h.IP())
+ }
+ case types.UInt:
+ fmt.Println(iter.UInt())
+ case types.Int:
+ fmt.Println(iter.Int())
+ case types.BigInt:
+ fmt.Println(iter.BigInt())
+ case types.Blob:
+ blob := iter.Blob()
+ if *onlyV {
+ v, err := io.ReadAll(blob.Reader())
+ if err != nil {
+ log.Fatal(err)
+ }
+ fmt.Println(hex.EncodeToString(v))
+ break
+ }
+ fmt.Printf("BLOB[ %s%d l=%d%s\n",
+ Cyan, len(blob.Chunks), blob.ChunkLen, Reset)
+ off := iter.Offset() + 1 + 8
+ for i, chunk := range blob.Chunks {
+ fmt.Print(Blue + strconv.Itoa(depth+1) + Reset)
+ fmt.Printf(" %s%03d%s ", Red, off, Reset)
+ prindent(depth + 1)
+ fmt.Printf("%s%d:%s ", Yellow, i, Reset)
+ printbin([]byte(chunk))
+ off += int64(len(chunk)) + 1
+ }
+ prindent(depth)
+ fmt.Println(" " + " ]")
+ case types.TAI64:
+ t := iter.TAI64().Time()
+ utc, isLeap := tai64n.Leapsecs.Sub(t)
+ if isLeap {
+ fmt.Printf("TAI64(%s TAI, leap)\n", t.Format(time.DateTime))
+ } else {
+ fmt.Printf("TAI64(%s TAI, %s UTC)\n",
+ t.Format(time.DateTime), utc.Format(time.DateTime))
+ }
+ case types.TAI64N:
+ t := iter.TAI64N().Time()
+ utc, isLeap := tai64n.Leapsecs.Sub(t)
+ if isLeap {
+ fmt.Printf("TAI64N(%s TAI, leap)\n",
+ t.Format(time.DateTime+".000000000"))
+ } else {
+ fmt.Printf("TAI64N(%s TAI, %s UTC)\n",
+ t.Format(time.DateTime+".000000000"),
+ utc.Format(time.DateTime+".000000000"))
+ }
+ case types.TAI64NA:
+ t := iter.TAI64NA()
+ fmt.Printf("TAI64NA(%s)\n", hexenc(t[:]))
+ case types.Magic:
+ var builder strings.Builder
+ for _, b := range []byte(iter.Magic()) {
+ if strconv.IsPrint(rune(b)) {
+ builder.WriteByte(b)
+ } else {
+ builder.WriteString(fmt.Sprintf("\\x%02x", b))
+ }
+ }
+ fmt.Printf("Magic(%s)\n", builder.String())
+ case types.Bin:
+ printbin(iter.Bin())
+ case types.Str:
+ if !*onlyV {
+ fmt.Print(`"`)
+ }
+ s := iter.Str()
+ sLen := len(s)
+ if sLen > int(*MaxStrLen) {
+ fmt.Print(s[:int(*MaxStrLen)])
+ fmt.Print("...")
+ } else {
+ fmt.Print(s)
+ }
+ if !*onlyV {
+ fmt.Print(`"`)
+ }
+ fmt.Println("")
+ case types.Raw:
+ fmt.Printf("(l=%d v=%s)\n", len(iter.Raw()), hexenc(iter.Raw()))
+ default:
+ fmt.Println("???")
+ }
+ }
+}