package autostruct import ( "encoding/json" "fmt" "log" "os" "reflect" "strings" ) // Generates structures given json // // Can also be used to generate structures via go code type AutoStruct struct { StructName string // Name to prefix to all structures generated PackageName string // Package to assign the new go file FileName string // The name to assign the new go file File *os.File // Access to the new go file (writer) } // Sets up the AutoStruct to build a go file containing structures // // Structname is a prefix to add to all structures, if no prefix desired leave an empty string in place func (a *AutoStruct) Init(fname, packname, structname string) error { a.StructName = structname a.FileName = fname a.PackageName = packname var err error if FileExists(a.FileName + ".go") { log.Printf("AutoStruct.Init('%s', '%s', '%s') => Warning, file exists, this is an experimental feature.", fname, packname, structname) } a.File, err = os.OpenFile(a.FileName+".go", os.O_CREATE|os.O_RDWR, 0666) if err != nil { return err } return nil } // Places the package name defined in the init func (a *AutoStruct) PlacePackageName() { a.File.Write([]byte("package " + a.PackageName + "\n")) } // In cases where you use a specific type from another module // // Don't forget to call EndImports() to close this, go will be cranky func (a *AutoStruct) StartImports() { a.File.Write([]byte("import (\n")) } // Adds a unambiguous import // // Use AddAliasImport() for 'alias "import"' func (a *AutoStruct) AddImport(importname string) { a.File.Write([]byte("\t\"" + importname + "\"\n")) } // Adds an aliased import // // Ex door "red-gree.com/door" // // Use AddImport() for a unambiguous import func (a *AutoStruct) AddAliasImport(importalias, importname string) { a.File.Write([]byte("\t" + importalias + " \"" + importname + "\"\n")) } // Closes the import statement func (a *AutoStruct) EndImports() { a.File.Write([]byte(")\n")) } // Begins a structure func (a *AutoStruct) StartStruct(structname string) { a.File.Write([]byte("type " + a.StructName + structname + " struct {\n")) } // Adds a field to the structure // // Ex Name string // // Use AddCommentField() to add a comment with the field // // Use AddJsonField() to add a json tag with the field // // Use AddJsonCommentField() to add a json tag and a comment func (a *AutoStruct) AddField(fieldname, fieldtype string) { a.File.Write([]byte("\t" + fieldname + " " + fieldtype + "\n")) } // Adds a field to the structure (includes a comment about the field) // // Ex Name string // The BBS user name // // Use AddField() to add a field without a comment or json tag // // Use AddJsonCommentField() to add a field with a comment and a json tag // // Use AddJsonField() to add a field with a json tag but no comment func (a *AutoStruct) AddCommentField(fieldname, fieldtype, comment string) { a.File.Write([]byte("\t" + fieldname + " " + fieldtype + " // " + comment + "\n")) } // Adds a field to the structure (includes a json tag) // // Ex Name string `json:"name"` // // Use json tag "-" to mark fields to not be in json's marshal or unmarshal process // // Use AddField() to add a field without a comment or json tag // // Use AddCommentField() to add a field without a json tag but with a comment // // Use AddJsonCommentField() to add a field with a json tag and a comment func (a *AutoStruct) AddJsonField(fieldname, fieldtype, jsonname string) { a.File.Write([]byte("\t" + fieldname + " " + fieldtype + " `json:\"" + jsonname + "\"`\n")) } // Adds a field to the structure (includes both a json tag and a comment) // // Ex Name string `json:"name"` // The BBS user name // // Use json tag "-" to mark fields to not be in json's marshal or unmarshal process // // Use AddJsonField() to add a field with a json tag but no comment // // Use AddField() to add a field without a comment or json tag // // Use AddCommentField() to add a field with a comment but no json tag func (a *AutoStruct) AddJsonCommentField(fieldname, fieldtype, jsonname, comment string) { a.File.Write([]byte("\t" + fieldname + " " + fieldtype + " `json:\"" + jsonname + "\"` // " + comment + "\n")) } // Ends/Closes the structure func (a *AutoStruct) EndStruct() { a.File.Write([]byte("}\n")) } // Adds some space between things func (a *AutoStruct) Newline() { a.File.Write([]byte("\n")) } // Adds a single line comment func (a *AutoStruct) Comment(comment string) { a.File.Write([]byte("// " + comment + "\n")) } // Closes the newly formed go file func (a *AutoStruct) Close() { a.File.Close() } // Forms needed structures and a master structure // // TODO: Change it so it actually forms a map[string]string key=fieldname value=fieldtype // This will allow us to then form other structures we need as we iterate the data func (a *AutoStruct) LoadJson(filename, structname string) (int, error) { var structsMade int var err error var file []byte file, err = os.ReadFile(filename + ".json") if err != nil { return structsMade, err } var data map[string]interface{} err = json.Unmarshal(file, &data) if err != nil { return structsMade, err } fmt.Printf("%#v\n", data) // Wrong, we actually want to store this then make any substructures then make the base struct a.StartStruct(structname) for k, v := range data { k = strings.ReplaceAll(k, "-", "_") // Replace '-' with '_' k = strings.ReplaceAll(k, " ", "") // Remove ' ' switch reflect.TypeOf(v).Kind() { case reflect.Array, reflect.Slice: // Array or slice [] // Check all types are the same (else need new struct) // Iterate over nested array/slice and maps // Assuming all types are same var array []interface{} = v.([]interface{}) a.AddField(k, "[]"+reflect.TypeOf(array[0]).String()) case reflect.Map: // Map key, val // Check all types are the same (else need new struct) // Iterate over nested array/slice and maps // Assuming all types are same var mp map[string]interface{} = v.(map[string]interface{}) var keys []string = make([]string, 0, len(mp)) for k2 := range mp { keys = append(keys, k2) } a.AddField(k, "map[string]"+reflect.TypeOf(mp[keys[0]]).String()) default: // Fallback a.AddField(k, reflect.TypeOf(v).String()) } } structsMade += 1 a.EndStruct() return structsMade, nil }