123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206 |
- 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
- }
|