213 lines
4.2 KiB
Go
213 lines
4.2 KiB
Go
package fin
|
|
|
|
/*
|
|
writer.go
|
|
Copyright 2025 Sharkk, sharkk.net
|
|
Authors: Sky Johnson
|
|
*/
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"strconv"
|
|
)
|
|
|
|
// Write serializes the data to the provided writer
|
|
func (d *Data) Write(w io.Writer) error {
|
|
return writeMap(w, d.data, 0)
|
|
}
|
|
|
|
func Save(w io.Writer, d *Data) error {
|
|
return d.Write(w)
|
|
}
|
|
|
|
// Helper for writing indentation
|
|
func writeIndent(w io.Writer, level int) error {
|
|
for range level {
|
|
if _, err := w.Write([]byte{'\t'}); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Helper for writing numbers using pooled buffers
|
|
func writeNumber(w io.Writer, val any) error {
|
|
buffer := GetByteSlice()
|
|
defer PutByteSlice(buffer)
|
|
|
|
switch v := val.(type) {
|
|
case int:
|
|
*buffer = strconv.AppendInt((*buffer)[:0], int64(v), 10)
|
|
case float64:
|
|
*buffer = strconv.AppendFloat((*buffer)[:0], v, 'g', -1, 64)
|
|
default:
|
|
return fmt.Errorf("not a number type")
|
|
}
|
|
|
|
_, err := w.Write(*buffer)
|
|
return err
|
|
}
|
|
|
|
// Consolidated string writing with escaping
|
|
func writeString(w io.Writer, s string, quote bool) error {
|
|
if quote {
|
|
if _, err := io.WriteString(w, "\""); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
for i := range len(s) {
|
|
c := s[i]
|
|
var escaped string
|
|
switch c {
|
|
case '"':
|
|
escaped = "\\\""
|
|
case '\\':
|
|
escaped = "\\\\"
|
|
case '\n':
|
|
escaped = "\\n"
|
|
case '\t':
|
|
escaped = "\\t"
|
|
default:
|
|
if _, err := w.Write([]byte{c}); err != nil {
|
|
return err
|
|
}
|
|
continue
|
|
}
|
|
if _, err := io.WriteString(w, escaped); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if quote {
|
|
if _, err := io.WriteString(w, "\""); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Unified value writing logic
|
|
func writeValue(w io.Writer, value any, level int, addSpace, addNewline bool) error {
|
|
if addSpace {
|
|
if _, err := w.Write([]byte{' '}); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
switch v := value.(type) {
|
|
case nil:
|
|
// Do nothing for nil
|
|
case string:
|
|
if err := writeString(w, v, true); err != nil {
|
|
return err
|
|
}
|
|
case int, float64:
|
|
if err := writeNumber(w, v); err != nil {
|
|
return err
|
|
}
|
|
case bool:
|
|
val := "false"
|
|
if v {
|
|
val = "true"
|
|
}
|
|
if _, err := io.WriteString(w, val); err != nil {
|
|
return err
|
|
}
|
|
case map[string]any:
|
|
if _, err := io.WriteString(w, "{\n"); err != nil {
|
|
return err
|
|
}
|
|
if err := writeMap(w, v, level+1); err != nil {
|
|
return err
|
|
}
|
|
if err := writeIndent(w, level); err != nil {
|
|
return err
|
|
}
|
|
if _, err := io.WriteString(w, "}"); err != nil {
|
|
return err
|
|
}
|
|
case []any:
|
|
if _, err := io.WriteString(w, "{\n"); err != nil {
|
|
return err
|
|
}
|
|
if err := writeArray(w, v, level+1); err != nil {
|
|
return err
|
|
}
|
|
if err := writeIndent(w, level); err != nil {
|
|
return err
|
|
}
|
|
if _, err := io.WriteString(w, "}"); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if addNewline {
|
|
if _, err := io.WriteString(w, "\n"); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func writeMap(w io.Writer, data map[string]any, level int) error {
|
|
for key, value := range data {
|
|
if err := writeIndent(w, level); err != nil {
|
|
return err
|
|
}
|
|
if _, err := io.WriteString(w, key); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Handle combined value+object case
|
|
if m, ok := value.(map[string]any); ok && len(m) > 1 {
|
|
if simpleValue, hasValue := m["value"]; hasValue {
|
|
if err := writeValue(w, simpleValue, level, true, false); err != nil {
|
|
return err
|
|
}
|
|
if _, err := io.WriteString(w, " {\n"); err != nil {
|
|
return err
|
|
}
|
|
for k, v := range m {
|
|
if k != "value" {
|
|
if err := writeIndent(w, level+1); err != nil {
|
|
return err
|
|
}
|
|
if _, err := io.WriteString(w, k); err != nil {
|
|
return err
|
|
}
|
|
if err := writeValue(w, v, level+1, true, true); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
if err := writeIndent(w, level); err != nil {
|
|
return err
|
|
}
|
|
if _, err := io.WriteString(w, "}\n"); err != nil {
|
|
return err
|
|
}
|
|
continue
|
|
}
|
|
}
|
|
|
|
if err := writeValue(w, value, level, true, true); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func writeArray(w io.Writer, array []any, level int) error {
|
|
for _, item := range array {
|
|
if err := writeIndent(w, level); err != nil {
|
|
return err
|
|
}
|
|
if err := writeValue(w, item, level, true, true); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|