Author: LakeFox
Email: [email protected]
Date: Sun, 20 Apr 2025 21:14:43 -0600
output/main.go
Delete
Commits
Diff
package output

import (
	"bytes"
	"encoding/json"
	"encoding/xml"
	"fmt"
	"reflect"
	"strings"
	"text/template"
)

// TextEncoder converts a struct to plain text based on struct tags, handling nested structs with indentation.
func Text(v interface{}) string {
	var result strings.Builder
	val := reflect.ValueOf(v)
	typ := val.Type()

	// Handle pointer types
	if val.Kind() == reflect.Ptr {
		val = val.Elem()
		typ = val.Type()
	}

	processValue(val, typ, 0, &result)

	return result.String()
}

func processValue(val reflect.Value, typ reflect.Type, indentLevel int, result *strings.Builder) {
	indent := strings.Repeat("\t", indentLevel)

	switch val.Kind() {
	case reflect.Struct:
		for i := 0; i < val.NumField(); i++ {
			field := val.Field(i)
			fieldType := typ.Field(i)

			// Get the 'text' tag, fallback to 'xml' tag if not present
			tag := fieldType.Tag.Get("text")
			if tag == "" {
				tag = fieldType.Tag.Get("xml")
			}

			// Skip if tag is "-"
			if tag == "-" {
				continue
			}

			// Use field name if no tag
			fieldName := fieldType.Name
			if tag == "" {
				tag = fieldName
			}

			currentTag := tag

			// Handle slices
			if field.Kind() == reflect.Slice {
				for j := 0; j < field.Len(); j++ {
					item := field.Index(j)
					itemType := item.Type()

					if item.Kind() == reflect.Struct || (item.Kind() == reflect.Ptr && item.Elem().Kind() == reflect.Struct) {
						// Nested struct or pointer to struct in slice
						if strings.Contains(tag, "omitempty") && isEmptyValue(item) {
							continue
						} else {
							currentTagWithoutOmit := strings.ReplaceAll(currentTag, ",omitempty", "")
							result.WriteString(fmt.Sprintf("%s%s:\n", indent, currentTagWithoutOmit))
							if item.Kind() == reflect.Ptr {
								processValue(item.Elem(), itemType.Elem(), indentLevel+1, result)
							} else {
								processValue(item, itemType, indentLevel+1, result)
							}
						}
					} else {
						lines := strings.TrimSpace(fmt.Sprintf("%v", item.Interface()))

						if strings.Contains(tag, "omitempty") && lines == "" {
							continue
						} else {
							currentTagWithoutOmit := strings.ReplaceAll(currentTag, ",omitempty", "")
							result.WriteString(fmt.Sprintf("%s%s: %v\n", indent, currentTagWithoutOmit, lines))
						}
					}
				}
			} else if field.Kind() == reflect.Struct || (field.Kind() == reflect.Ptr && field.Elem().Kind() == reflect.Struct) {
				// Nested struct or pointer to struct
				if strings.Contains(tag, "omitempty") && isEmptyValue(field) {
					continue
				} else {
					currentTagWithoutOmit := strings.ReplaceAll(currentTag, ",omitempty", "")
					result.WriteString(fmt.Sprintf("%s%s:\n", indent, currentTagWithoutOmit))
					fieldTypeToPass := fieldType.Type
					fieldValToPass := field
					if field.Kind() == reflect.Ptr {
						fieldTypeToPass = fieldType.Type.Elem()
						fieldValToPass = field.Elem()
					}
					processValue(fieldValToPass, fieldTypeToPass, indentLevel+1, result)
				}
			} else {
				// Basic type
				lines := strings.TrimSpace(fmt.Sprintf("%v", field.Interface()))

				if strings.Contains(tag, "omitempty") && lines == "" {
					continue
				} else {
					currentTagWithoutOmit := strings.ReplaceAll(currentTag, ",omitempty", "")
					result.WriteString(fmt.Sprintf("%s%s: %v\n", indent, currentTagWithoutOmit, lines))
				}
			}
		}
	default:
		result.WriteString(fmt.Sprintf("%v", val.Interface()))
	}
}

func isEmptyValue(v reflect.Value) bool {
	switch v.Kind() {
	case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
		return v.Len() == 0
	case reflect.Bool:
		return !v.Bool()
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return v.Int() == 0
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		return v.Uint() == 0
	case reflect.Float32, reflect.Float64:
		return v.Float() == 0
	case reflect.Interface, reflect.Ptr:
		return v.IsNil()
	case reflect.Struct:
		for i := 0; i < v.NumField(); i++ {
			if !isEmptyValue(v.Field(i)) {
				return false
			}
		}
		return true
	}
	return false
}

func JSON(v interface{}) string {
	b, e := json.Marshal(v)
	fmt.Println(e)
	return string(b)
}

func XML(v interface{}) string {
	b, _ := xml.Marshal(v)
	return string(b)
}

func FormatPath(path string, data interface{}) string {
	var output bytes.Buffer

	templ := template.Must(template.New("fp").Parse(path))
	templ.Execute(&output, data)

	return output.String()
}