package fin /* writer.go Copyright 2025 Sharkk, sharkk.net Authors: Sky Johnson */ import ( "io" "strconv" ) // Write serializes the data to the provided writer func (d *Data) Write(w io.Writer) error { return writeMap(w, d.data, 0) } // Save writes data to a writer (standalone function) func Save(w io.Writer, d *Data) error { return d.Write(w) } // writeMap writes a map at the given indent level func writeMap(w io.Writer, data map[string]any, level int) error { for key, value := range data { // Write indentation for i := 0; i < level; i++ { if _, err := w.Write([]byte{'\t'}); err != nil { return err } } // Write key if _, err := io.WriteString(w, key); err != nil { return err } // Special case: combined value+object if m, ok := value.(map[string]any); ok && len(m) > 1 { if simpleValue, hasValue := m["value"]; hasValue { // Write simple value first if err := writeSimpleValue(w, simpleValue); err != nil { return err } // Write object portion if _, err := io.WriteString(w, " {\n"); err != nil { return err } for k, v := range m { if k != "value" { for i := 0; i < level+1; i++ { if _, err := w.Write([]byte{'\t'}); err != nil { return err } } if _, err := io.WriteString(w, k); err != nil { return err } if err := writeValueWithNewline(w, v, level+1); err != nil { return err } } } for i := 0; i < level; i++ { if _, err := w.Write([]byte{'\t'}); err != nil { return err } } if _, err := io.WriteString(w, "}\n"); err != nil { return err } continue } } // Regular value handling if err := writeValueWithNewline(w, value, level); err != nil { return err } } return nil } // writeValueWithNewline writes a value and adds a newline func writeValueWithNewline(w io.Writer, value any, level int) error { if err := writeValue(w, value, level); err != nil { return err } if _, err := io.WriteString(w, "\n"); err != nil { return err } return nil } // writeValue writes a value with appropriate formatting func writeValue(w io.Writer, value any, level int) error { // Space after key if _, err := w.Write([]byte{' '}); err != nil { return err } switch v := value.(type) { case nil: return nil case string: // Quote strings if _, err := io.WriteString(w, "\""); err != nil { return err } if err := writeEscapedString(w, v); err != nil { return err } if _, err := io.WriteString(w, "\""); err != nil { return err } return nil case int: // Use byte slice pool for better performance buffer := GetByteSlice() *buffer = strconv.AppendInt((*buffer)[:0], int64(v), 10) _, err := w.Write(*buffer) PutByteSlice(buffer) return err case float64: buffer := GetByteSlice() *buffer = strconv.AppendFloat((*buffer)[:0], v, 'g', -1, 64) _, err := w.Write(*buffer) PutByteSlice(buffer) return err case bool: if v { _, err := io.WriteString(w, "true") return err } _, err := io.WriteString(w, "false") 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 } for i := 0; i < level; i++ { if _, err := w.Write([]byte{'\t'}); err != nil { return err } } if _, err := io.WriteString(w, "}"); err != nil { return err } return nil case []any: if _, err := io.WriteString(w, "{\n"); err != nil { return err } if err := writeArray(w, v, level+1); err != nil { return err } for i := 0; i < level; i++ { if _, err := w.Write([]byte{'\t'}); err != nil { return err } } if _, err := io.WriteString(w, "}"); err != nil { return err } return nil default: // Fall back for any other types buffer := GetByteSlice() *buffer = append((*buffer)[:0], []byte(strconv.FormatInt(int64(v.(int)), 10))...) _, err := w.Write(*buffer) PutByteSlice(buffer) return err } } // writeArray writes array elements func writeArray(w io.Writer, array []any, level int) error { for _, item := range array { for i := 0; i < level; i++ { if _, err := w.Write([]byte{'\t'}); err != nil { return err } } if err := writeValue(w, item, level); err != nil { return err } if _, err := io.WriteString(w, "\n"); err != nil { return err } } return nil } // writeEscapedString writes a string with escape sequences func writeEscapedString(w io.Writer, s string) error { for i := 0; i < len(s); i++ { c := s[i] switch c { case '"': if _, err := io.WriteString(w, "\\\""); err != nil { return err } case '\\': if _, err := io.WriteString(w, "\\\\"); err != nil { return err } case '\n': if _, err := io.WriteString(w, "\\n"); err != nil { return err } case '\t': if _, err := io.WriteString(w, "\\t"); err != nil { return err } default: if _, err := w.Write([]byte{c}); err != nil { return err } } } return nil } // writeSimpleValue writes a simple value without newline func writeSimpleValue(w io.Writer, value any) error { if _, err := w.Write([]byte{' '}); err != nil { return err } switch v := value.(type) { case string: if _, err := io.WriteString(w, "\""); err != nil { return err } if err := writeEscapedString(w, v); err != nil { return err } if _, err := io.WriteString(w, "\""); err != nil { return err } case int: buffer := GetByteSlice() *buffer = strconv.AppendInt((*buffer)[:0], int64(v), 10) _, err := w.Write(*buffer) PutByteSlice(buffer) return err case float64: buffer := GetByteSlice() *buffer = strconv.AppendFloat((*buffer)[:0], v, 'g', -1, 64) _, err := w.Write(*buffer) PutByteSlice(buffer) return err case bool: if v { _, err := io.WriteString(w, "true") return err } _, err := io.WriteString(w, "false") return err } return nil }