mirror of
https://github.com/wavetermdev/waveterm.git
synced 2025-11-28 05:00:26 +08:00
Huge PR. 135 commits here to rebuild waveapps into the "Tsunami" framework.
* Simplified API
* Updated system.md prompt
* Basic applications building and running
* /api/config and /api/data support
* tailwind styling
* no need for async updates
* goroutine/timer primitives for async routing handling
* POC for integrating 3rd party react frameworks (recharts)
* POC for server side components (table.go)
* POC for interacting with apps via /api/config (tsunamiconfig)
Checkpoint. Still needs to be tightly integrated with Wave (lifecycle, AI interaction, etc.) but looking very promising 🚀
121 lines
No EOL
2.7 KiB
Go
121 lines
No EOL
2.7 KiB
Go
// Copyright 2025, Command Line Inc.
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package util
|
|
|
|
import (
|
|
"fmt"
|
|
"reflect"
|
|
"strings"
|
|
)
|
|
|
|
func MapToStruct(in map[string]any, out any) error {
|
|
// Check that out is a pointer
|
|
outValue := reflect.ValueOf(out)
|
|
if outValue.Kind() != reflect.Ptr {
|
|
return fmt.Errorf("out parameter must be a pointer, got %v", outValue.Kind())
|
|
}
|
|
|
|
// Get the struct it points to
|
|
elem := outValue.Elem()
|
|
if elem.Kind() != reflect.Struct {
|
|
return fmt.Errorf("out parameter must be a pointer to struct, got pointer to %v", elem.Kind())
|
|
}
|
|
|
|
// Get type information
|
|
typ := elem.Type()
|
|
|
|
// For each field in the struct
|
|
for i := 0; i < typ.NumField(); i++ {
|
|
field := typ.Field(i)
|
|
|
|
// Skip unexported fields
|
|
if !field.IsExported() {
|
|
continue
|
|
}
|
|
|
|
name := getJSONName(field)
|
|
if value, ok := in[name]; ok {
|
|
if err := setValue(elem.Field(i), value); err != nil {
|
|
return fmt.Errorf("error setting field %s: %w", name, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func StructToMap(in any) (map[string]any, error) {
|
|
// Get value and handle pointer
|
|
val := reflect.ValueOf(in)
|
|
if val.Kind() == reflect.Ptr {
|
|
val = val.Elem()
|
|
}
|
|
|
|
// Check that we have a struct
|
|
if val.Kind() != reflect.Struct {
|
|
return nil, fmt.Errorf("input must be a struct or pointer to struct, got %v", val.Kind())
|
|
}
|
|
|
|
// Get type information
|
|
typ := val.Type()
|
|
out := make(map[string]any)
|
|
|
|
// For each field in the struct
|
|
for i := 0; i < typ.NumField(); i++ {
|
|
field := typ.Field(i)
|
|
|
|
// Skip unexported fields
|
|
if !field.IsExported() {
|
|
continue
|
|
}
|
|
|
|
name := getJSONName(field)
|
|
out[name] = val.Field(i).Interface()
|
|
}
|
|
|
|
return out, nil
|
|
}
|
|
|
|
// getJSONName returns the field name to use for JSON mapping
|
|
func getJSONName(field reflect.StructField) string {
|
|
tag := field.Tag.Get("json")
|
|
if tag == "" || tag == "-" {
|
|
return field.Name
|
|
}
|
|
return strings.Split(tag, ",")[0]
|
|
}
|
|
|
|
// setValue attempts to set a reflect.Value with a given interface{} value
|
|
func setValue(field reflect.Value, value any) error {
|
|
if value == nil {
|
|
return nil
|
|
}
|
|
|
|
valueRef := reflect.ValueOf(value)
|
|
|
|
// Direct assignment if types are exactly equal
|
|
if valueRef.Type() == field.Type() {
|
|
field.Set(valueRef)
|
|
return nil
|
|
}
|
|
|
|
// Check if types are assignable
|
|
if valueRef.Type().AssignableTo(field.Type()) {
|
|
field.Set(valueRef)
|
|
return nil
|
|
}
|
|
|
|
// If field is pointer and value isn't already a pointer, try address
|
|
if field.Kind() == reflect.Ptr && valueRef.Kind() != reflect.Ptr {
|
|
return setValue(field, valueRef.Addr().Interface())
|
|
}
|
|
|
|
// Try conversion if types are convertible
|
|
if valueRef.Type().ConvertibleTo(field.Type()) {
|
|
field.Set(valueRef.Convert(field.Type()))
|
|
return nil
|
|
}
|
|
|
|
return fmt.Errorf("cannot set value of type %v to field of type %v", valueRef.Type(), field.Type())
|
|
} |