autostruct.go 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. package autostruct
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "log"
  6. "os"
  7. "reflect"
  8. "strings"
  9. )
  10. // Generates structures given json
  11. //
  12. // Can also be used to generate structures via go code
  13. type AutoStruct struct {
  14. StructName string // Name to prefix to all structures generated
  15. PackageName string // Package to assign the new go file
  16. FileName string // The name to assign the new go file
  17. File *os.File // Access to the new go file (writer)
  18. }
  19. // Sets up the AutoStruct to build a go file containing structures
  20. //
  21. // Structname is a prefix to add to all structures, if no prefix desired leave an empty string in place
  22. func (a *AutoStruct) Init(fname, packname, structname string) error {
  23. a.StructName = structname
  24. a.FileName = fname
  25. a.PackageName = packname
  26. var err error
  27. if FileExists(a.FileName + ".go") {
  28. log.Printf("AutoStruct.Init('%s', '%s', '%s') => Warning, file exists, this is an experimental feature.", fname, packname, structname)
  29. }
  30. a.File, err = os.OpenFile(a.FileName+".go", os.O_CREATE|os.O_RDWR, 0666)
  31. if err != nil {
  32. return err
  33. }
  34. return nil
  35. }
  36. // Places the package name defined in the init
  37. func (a *AutoStruct) PlacePackageName() {
  38. a.File.Write([]byte("package " + a.PackageName + "\n"))
  39. }
  40. // In cases where you use a specific type from another module
  41. //
  42. // Don't forget to call EndImports() to close this, go will be cranky
  43. func (a *AutoStruct) StartImports() {
  44. a.File.Write([]byte("import (\n"))
  45. }
  46. // Adds a unambiguous import
  47. //
  48. // Use AddAliasImport() for 'alias "import"'
  49. func (a *AutoStruct) AddImport(importname string) {
  50. a.File.Write([]byte("\t\"" + importname + "\"\n"))
  51. }
  52. // Adds an aliased import
  53. //
  54. // Ex door "red-gree.com/door"
  55. //
  56. // Use AddImport() for a unambiguous import
  57. func (a *AutoStruct) AddAliasImport(importalias, importname string) {
  58. a.File.Write([]byte("\t" + importalias + " \"" + importname + "\"\n"))
  59. }
  60. // Closes the import statement
  61. func (a *AutoStruct) EndImports() {
  62. a.File.Write([]byte(")\n"))
  63. }
  64. // Begins a structure
  65. func (a *AutoStruct) StartStruct(structname string) {
  66. a.File.Write([]byte("type " + a.StructName + structname + " struct {\n"))
  67. }
  68. // Adds a field to the structure
  69. //
  70. // Ex Name string
  71. //
  72. // Use AddCommentField() to add a comment with the field
  73. //
  74. // Use AddJsonField() to add a json tag with the field
  75. //
  76. // Use AddJsonCommentField() to add a json tag and a comment
  77. func (a *AutoStruct) AddField(fieldname, fieldtype string) {
  78. a.File.Write([]byte("\t" + fieldname + " " + fieldtype + "\n"))
  79. }
  80. // Adds a field to the structure (includes a comment about the field)
  81. //
  82. // Ex Name string // The BBS user name
  83. //
  84. // Use AddField() to add a field without a comment or json tag
  85. //
  86. // Use AddJsonCommentField() to add a field with a comment and a json tag
  87. //
  88. // Use AddJsonField() to add a field with a json tag but no comment
  89. func (a *AutoStruct) AddCommentField(fieldname, fieldtype, comment string) {
  90. a.File.Write([]byte("\t" + fieldname + " " + fieldtype + " // " + comment + "\n"))
  91. }
  92. // Adds a field to the structure (includes a json tag)
  93. //
  94. // Ex Name string `json:"name"`
  95. //
  96. // Use json tag "-" to mark fields to not be in json's marshal or unmarshal process
  97. //
  98. // Use AddField() to add a field without a comment or json tag
  99. //
  100. // Use AddCommentField() to add a field without a json tag but with a comment
  101. //
  102. // Use AddJsonCommentField() to add a field with a json tag and a comment
  103. func (a *AutoStruct) AddJsonField(fieldname, fieldtype, jsonname string) {
  104. a.File.Write([]byte("\t" + fieldname + " " + fieldtype + " `json:\"" + jsonname + "\"`\n"))
  105. }
  106. // Adds a field to the structure (includes both a json tag and a comment)
  107. //
  108. // Ex Name string `json:"name"` // The BBS user name
  109. //
  110. // Use json tag "-" to mark fields to not be in json's marshal or unmarshal process
  111. //
  112. // Use AddJsonField() to add a field with a json tag but no comment
  113. //
  114. // Use AddField() to add a field without a comment or json tag
  115. //
  116. // Use AddCommentField() to add a field with a comment but no json tag
  117. func (a *AutoStruct) AddJsonCommentField(fieldname, fieldtype, jsonname, comment string) {
  118. a.File.Write([]byte("\t" + fieldname + " " + fieldtype + " `json:\"" + jsonname + "\"` // " + comment + "\n"))
  119. }
  120. // Ends/Closes the structure
  121. func (a *AutoStruct) EndStruct() {
  122. a.File.Write([]byte("}\n"))
  123. }
  124. // Adds some space between things
  125. func (a *AutoStruct) Newline() {
  126. a.File.Write([]byte("\n"))
  127. }
  128. // Adds a single line comment
  129. func (a *AutoStruct) Comment(comment string) {
  130. a.File.Write([]byte("// " + comment + "\n"))
  131. }
  132. // Closes the newly formed go file
  133. func (a *AutoStruct) Close() {
  134. a.File.Close()
  135. }
  136. // Forms needed structures and a master structure
  137. //
  138. // TODO: Change it so it actually forms a map[string]string key=fieldname value=fieldtype
  139. // This will allow us to then form other structures we need as we iterate the data
  140. func (a *AutoStruct) LoadJson(filename, structname string) (int, error) {
  141. var structsMade int
  142. var err error
  143. var file []byte
  144. file, err = os.ReadFile(filename + ".json")
  145. if err != nil {
  146. return structsMade, err
  147. }
  148. var data map[string]interface{}
  149. err = json.Unmarshal(file, &data)
  150. if err != nil {
  151. return structsMade, err
  152. }
  153. fmt.Printf("%#v\n", data)
  154. // Wrong, we actually want to store this then make any substructures then make the base struct
  155. a.StartStruct(structname)
  156. for k, v := range data {
  157. k = strings.ReplaceAll(k, "-", "_") // Replace '-' with '_'
  158. k = strings.ReplaceAll(k, " ", "") // Remove ' '
  159. switch reflect.TypeOf(v).Kind() {
  160. case reflect.Array, reflect.Slice:
  161. // Array or slice []
  162. // Check all types are the same (else need new struct)
  163. // Iterate over nested array/slice and maps
  164. // Assuming all types are same
  165. var array []interface{} = v.([]interface{})
  166. a.AddField(k, "[]"+reflect.TypeOf(array[0]).String())
  167. case reflect.Map:
  168. // Map key, val
  169. // Check all types are the same (else need new struct)
  170. // Iterate over nested array/slice and maps
  171. // Assuming all types are same
  172. var mp map[string]interface{} = v.(map[string]interface{})
  173. var keys []string = make([]string, 0, len(mp))
  174. for k2 := range mp {
  175. keys = append(keys, k2)
  176. }
  177. a.AddField(k, "map[string]"+reflect.TypeOf(mp[keys[0]]).String())
  178. default:
  179. // Fallback
  180. a.AddField(k, reflect.TypeOf(v).String())
  181. }
  182. }
  183. structsMade += 1
  184. a.EndStruct()
  185. return structsMade, nil
  186. }