Fin/writer.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
}