123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366 |
- package astruct
- import (
- "encoding/json"
- "fmt"
- "log"
- "os"
- "reflect"
- "strings"
- "golang.org/x/exp/maps"
- "golang.org/x/exp/slices"
- )
- type Map map[string]reflect.Kind
- type Astruct struct {
-
- Use32Bit bool
- UseAny bool
- UseInterface bool
- VerboseLogging bool
- AssumeStruct bool
- PrefixStructName string
-
-
-
-
- MapLikes map[string]Map
- ArrayLikes map[string][]reflect.Kind
- Parent any
- Depth uint
- }
- func (A *Astruct) Defaults() {
- A.Use32Bit = false
- A.UseAny = true
- A.UseInterface = true
- A.VerboseLogging = false
- A.AssumeStruct = false
- A.PrefixStructName = ""
- A.Init()
- }
- func (A *Astruct) Init() {
- A.MapLikes = make(map[string]Map)
- A.ArrayLikes = make(map[string][]reflect.Kind)
- A.Parent = nil
- A.Depth = 0
- }
- func NewAstruct(PrefixStructName ...string) *Astruct {
- A := &Astruct{}
- A.Defaults()
- if len(PrefixStructName) != 0 {
- A.PrefixStructName = CamelCase(PrefixStructName[0])
- }
- return A
- }
- func (A *Astruct) ReadFile(filename string) error {
- data, err := os.ReadFile(filename)
- if err != nil {
- return fmt.Errorf("astruct.Astruct.ReadFile(filename='%s') On os.ReadFile > %w", filename, err)
- }
- var root map[string]any
- err = json.Unmarshal(data, &root)
- if err != nil {
- return fmt.Errorf("astruct.Astruct.ReadFile(filename='%s') On json.Unmarshal > %w", filename, err)
- }
-
- A.parse(root, "")
- return nil
- }
- func (A *Astruct) parse(at any, name string) error {
- switch reflect.TypeOf(at).Kind() {
- case reflect.Map:
- m := at.(map[string]any)
- var last_type reflect.Kind = 0
- var same_type bool = true
- for k, v := range m {
- fmt.Printf(strings.Repeat(" ", int(A.Depth))+"'%s' = %s\n", k, reflect.TypeOf(v).Kind().String())
- if same_type {
- if last_type == 0 {
- last_type = reflect.TypeOf(v).Kind()
- } else if reflect.TypeOf(v).Kind() != last_type {
- same_type = false
- }
- }
- switch reflect.TypeOf(v).Kind() {
- case reflect.Map:
- A.Depth += 1
- err := A.parse(v, k)
- if err != nil {
- return err
- }
- A.Depth -= 1
- case reflect.Array, reflect.Slice:
- A.Depth += 1
- err := A.parse(v, k)
- if err != nil {
- return err
- }
- A.Depth -= 1
- }
- }
- A.MapLikes[name] = make(Map)
- if !same_type {
- for k, v := range m {
- if reflect.TypeOf(v).Kind() == reflect.Float32 || reflect.TypeOf(v).Kind() == reflect.Float64 {
- f := v.(float64)
- if IsInt(f) {
- A.MapLikes[name][k] = reflect.Int64
- } else {
- A.MapLikes[name][k] = reflect.Float64
- }
- } else {
- A.MapLikes[name][k] = reflect.TypeOf(v).Kind()
- }
- }
- } else {
- A.MapLikes[name]["all"] = reflect.TypeOf(m[maps.Keys(m)[0]]).Kind()
- }
- case reflect.Array, reflect.Slice:
- a := at.([]any)
- var last_type reflect.Kind = 0
- var same_type bool = true
- for i, v := range a {
- fmt.Printf(strings.Repeat(" ", int(A.Depth))+"%d = %s\n", i, reflect.TypeOf(v).Kind().String())
- if same_type {
- if last_type == 0 {
- last_type = reflect.TypeOf(v).Kind()
- } else if reflect.TypeOf(v).Kind() != last_type {
- same_type = false
- }
- }
- switch reflect.TypeOf(v).Kind() {
- case reflect.Map:
- A.Depth += 1
- err := A.parse(v, fmt.Sprint(i))
- if err != nil {
- return err
- }
- A.Depth -= 1
- case reflect.Array, reflect.Slice:
- A.Depth += 1
- err := A.parse(v, fmt.Sprint(i))
- if err != nil {
- return err
- }
- A.Depth -= 1
- }
- }
- A.ArrayLikes[name] = []reflect.Kind{}
- if !same_type {
- for _, v := range a {
- if reflect.TypeOf(v).Kind() == reflect.Float32 || reflect.TypeOf(v).Kind() == reflect.Float64 {
- f := v.(float64)
- if IsInt(f) {
- A.ArrayLikes[name] = append(A.ArrayLikes[name], reflect.Int64)
- } else {
- A.ArrayLikes[name] = append(A.ArrayLikes[name], reflect.Float64)
- }
- } else {
- A.ArrayLikes[name] = append(A.ArrayLikes[name], reflect.TypeOf(v).Kind())
- }
- }
- } else {
- v := a[0]
- if reflect.TypeOf(v).Kind() == reflect.Float32 || reflect.TypeOf(v).Kind() == reflect.Float64 {
- f := v.(float64)
- if IsInt(f) {
- A.ArrayLikes[name] = append(A.ArrayLikes[name], reflect.Int64)
- } else {
- A.ArrayLikes[name] = append(A.ArrayLikes[name], reflect.Float64)
- }
- } else {
- A.ArrayLikes[name] = append(A.ArrayLikes[name], reflect.TypeOf(v).Kind())
- }
- }
- default:
- fmt.Printf(strings.Repeat(" ", int(A.Depth))+"%s\n", reflect.TypeOf(at).Kind().String())
- }
- return nil
- }
- func IsInt(v float64) bool {
- str := fmt.Sprintf("%f", v)
- fmt.Printf("'%s' = ", str)
- var hit_dot bool = false
- for _, r := range str {
- if r == '.' {
- hit_dot = true
- continue
- }
- if hit_dot {
- if r != '0' {
- fmt.Println("false")
- return false
- }
- }
- }
- fmt.Println("true")
- return true
- }
- func SameType(data any) bool {
- if reflect.TypeOf(data) == reflect.TypeOf(Map{}) {
- m := data.(Map)
- var last_type reflect.Kind = 0
- for _, v := range m {
- if last_type == 0 {
- last_type = v
- } else if v != last_type {
- return false
- }
- }
- return true
- }
- switch reflect.TypeOf(data).Kind() {
- case reflect.Map:
- m := data.(map[string]any)
- var last_type reflect.Kind = 0
- for _, v := range m {
- if last_type == 0 {
- last_type = reflect.TypeOf(v).Kind()
- } else if reflect.TypeOf(v).Kind() != last_type {
- return false
- }
- }
- return true
- case reflect.Array, reflect.Slice:
- a := data.([]any)
- var last_type reflect.Kind = 0
- for _, v := range a {
- if last_type == 0 {
- last_type = reflect.TypeOf(v).Kind()
- } else if reflect.TypeOf(v).Kind() != last_type {
- return false
- }
- }
- return true
- }
- return false
- }
- func CamelCase(field string) string {
- if len(field) == 0 {
- return ""
- }
- var result string
- if !strings.Contains(field, " ") || !strings.Contains(field, "-") {
- result += strings.ToUpper(string(field[0]))
- result += field[1:]
- return result
- }
- result = strings.ToTitle(field)
- result = strings.ReplaceAll(result, " ", "")
- result = strings.ReplaceAll(result, "-", "")
- return result
- }
- func (A *Astruct) WriteFile(filename, packageName string, permissions os.FileMode) error {
- if packageName == "" {
- return fmt.Errorf("astruct.Astruct.WriteFile(filename='%s', packageName='%s', permissions=%d) > %s", filename, packageName, permissions, "Missing 'packageName', this must not be empty!")
- }
- var successful bool = true
- fh, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, permissions)
- if err != nil {
- return fmt.Errorf("astruct.Astruct.WriteFile(filename='%s', packageName='%s', permissions=%d) On os.OpenFile > %w", filename, packageName, permissions, err)
- }
- defer func() {
- fh.Close()
- if !successful {
- err = os.Remove(filename)
- if err != nil {
- log.Panicf("astruct.Astruct.WriteFile(filename='%s', packageName='%s', permissions=%d) On os.Remove > %v", filename, packageName, permissions, err)
- }
- }
- }()
- fh.WriteString(fmt.Sprintf("package %s\n\n", packageName))
-
- var seen []string = []string{}
- for name, m := range A.MapLikes {
- if name == "all" {
- continue
- }
- if slices.Contains(seen, name) {
- continue
- }
- if !A.AssumeStruct || !SameType(m) {
- fh.WriteString(fmt.Sprintf("type %s%s struct {\n", A.PrefixStructName, CamelCase(name)))
- for k, v := range m {
- if slices.Contains(maps.Keys(A.MapLikes), k) {
- if SameType(A.MapLikes[k]) {
- fh.WriteString(fmt.Sprintf("\t%s map[string]%s\n", CamelCase(k), A.MapLikes[k]["all"].String()))
- seen = append(seen, k)
- } else {
- fh.WriteString(fmt.Sprintf("\t%s %s%s\n", CamelCase(k), A.PrefixStructName, CamelCase(k)))
- }
- } else {
- if slices.Contains(maps.Keys(A.ArrayLikes), k) {
- a := A.ArrayLikes[k]
- if len(a) == 1 {
- if a[0] == reflect.Float64 && A.Use32Bit {
- fh.WriteString(fmt.Sprintf("\t%s []float32\n", CamelCase(k)))
- continue
- } else if a[0] == reflect.Int64 && A.Use32Bit {
- fh.WriteString(fmt.Sprintf("\t%s []int32\n", CamelCase(k)))
- continue
- } else if a[0] == reflect.Map && A.AssumeStruct {
- fh.WriteString(fmt.Sprintf("\t%s []%s\n", CamelCase(k), CamelCase(k)))
- continue
- } else if a[0] == reflect.Map && !A.AssumeStruct {
- fh.WriteString(fmt.Sprintf("\t%s []%s%d\n", CamelCase(k), CamelCase(k), 0))
- continue
- }
- fh.WriteString(fmt.Sprintf("\t%s []%s\n", CamelCase(k), a[0]))
- } else {
-
-
- if A.UseAny && A.UseInterface {
- fh.WriteString(fmt.Sprintf("\t%s []any\n", CamelCase(k)))
- } else if !A.UseAny && A.UseInterface {
- fh.WriteString(fmt.Sprintf("\t%s []interface{}\n", CamelCase(k)))
- } else {
- return fmt.Errorf("astruct.Astruct.WriteFile(filename='%s', packageName='%s', permissions=%d) > Field '%s' is a slice, but it contains varying types (!UseAny, !UseInterface)", filename, packageName, permissions, k)
- }
- }
- } else {
- if v == reflect.Float64 && A.Use32Bit {
- fh.WriteString(fmt.Sprintf("\t%s float32\n", CamelCase(k)))
- continue
- } else if v == reflect.Int64 && A.Use32Bit {
- fh.WriteString(fmt.Sprintf("\t%s int32\n", CamelCase(k)))
- continue
- }
- fh.WriteString(fmt.Sprintf("\t%s %s\n", CamelCase(k), v.String()))
- }
- }
- }
- fh.WriteString("}\n\n")
- }
- }
-
- return nil
- }
|