First working prototype for a simple table.

Olivier Tremblay 4 years ago
parent
commit
b4aee6ce21
8 changed files with 460 additions and 248 deletions
  1. 1 0
      TODO
  2. 133 0
      dbcodegenerator.go
  3. 5 0
      generator.go
  4. 94 0
      httpcodegenerator.go
  5. 7 248
      main.go
  6. 80 0
      maingenerator.go
  7. 78 0
      tablecolumn.go
  8. 62 0
      tableinfo.go

+ 1 - 0
TODO

@@ -0,0 +1 @@
1
+JSON-LD all the things, based on table names.

+ 133 - 0
dbcodegenerator.go

@@ -0,0 +1,133 @@
1
+package main
2
+
3
+import (
4
+	"bytes"
5
+	"fmt"
6
+	"go/format"
7
+	"io"
8
+	"os"
9
+	"text/template"
10
+)
11
+
12
+type dbCodeGenerator struct {
13
+}
14
+
15
+func (g *dbCodeGenerator) Generate(tables map[string]tableInfo) error {
16
+	err := os.Mkdir("db", 0755)
17
+	if err != nil {
18
+		return err
19
+	}
20
+
21
+	tmpl := template.Must(template.New("class").Parse(`//WARNING.
22
+//THIS HAS BEEN GENERATED AUTOMATICALLY BY AUTOAPI.
23
+//IF THERE WAS A WARRANTY, MODIFYING THIS WOULD VOID IT.
24
+
25
+package {{.TableName}}
26
+
27
+import (
28
+"is-a-dev.com/libautoapi"
29
+//"errors"
30
+)
31
+
32
+var DB libautoapi.DB
33
+
34
+{{if .PrimaryColumns}}
35
+//type {{.NormalizedTableName}}Cache struct{
36
+
37
+//    rowsByKey map{{range .PrimaryColumns}}[{{.MappedColumnType}}]{{end}}*{{.NormalizedTableName}}
38
+
39
+//}
40
+
41
+var cache = map{{range .PrimaryColumns}}[{{.MappedColumnType}}]{{end}}*{{.NormalizedTableName}}{}
42
+
43
+{{end}}
44
+
45
+type {{.NormalizedTableName}} struct {
46
+{{range .ColOrder}}{{.CapitalizedColumnName}} {{.MappedColumnType}}
47
+{{end}}}
48
+
49
+func New() *{{.NormalizedTableName}}{
50
+    return &{{.NormalizedTableName}}{}
51
+}
52
+
53
+func All() ([]*{{.NormalizedTableName}}, error){
54
+    rows, err := DB.Query("SELECT {{.QueryFieldNames}} FROM {{.TableName}}")
55
+    if err != nil {
56
+        return nil,err
57
+    }
58
+    result := make([]*{{.NormalizedTableName}},0)
59
+    for rows.Next() {
60
+        r := &{{.NormalizedTableName}}{}
61
+        rows.Scan(
62
+            {{range .ColOrder}}&r.{{.CapitalizedColumnName}},
63
+            {{end}})
64
+        {{if .PrimaryColumns}}
65
+          cache[r.{{range .PrimaryColumns}}{{.CapitalizedColumnName}}{{end}}] = r
66
+        {{end}}
67
+        result = append(result, r)
68
+    }
69
+    return result, nil
70
+}
71
+
72
+func GetBy{{.PrimaryColumnsJoinedByAnd}}({{.PrimaryColumnsParamList}}) (*{{.NormalizedTableName}}, error) {
73
+    {{if .PrimaryColumns}}
74
+        if r, ok := cache[{{range .PrimaryColumns}}{{.LowercaseColumnName}}{{end}}]; ok { return r, nil}
75
+    {{end}}
76
+    row := &{{.NormalizedTableName}}{}
77
+    err := DB.QueryRow("SELECT {{.QueryFieldNames}} FROM {{.TableName}} WHERE {{.PrimaryWhere}}",
78
+    {{range .PrimaryColumns}}{{.LowercaseColumnName}},
79
+    {{end}}).Scan(
80
+        {{range .ColOrder}}&row.{{.CapitalizedColumnName}},
81
+        {{end}})
82
+    if err != nil {
83
+        return nil, err
84
+    }
85
+    return row, nil
86
+}
87
+
88
+func DeleteBy{{.PrimaryColumnsJoinedByAnd}}({{.PrimaryColumnsParamList}}) (*{{.NormalizedTableName}}, error) {
89
+    //TODO: remove from cache.
90
+    err := DB.Exec("DELETE FROM {{.TableName}} WHERE {{.PrimaryWhere}}",
91
+    {{range .PrimaryColumns}}{{.LowercaseColumnName}},
92
+    {{end}})
93
+    if err != nil {
94
+        return nil, err
95
+    }
96
+    return row, nil
97
+}
98
+
99
+func Save(row *{{.NormalizedTableName}}) error {
100
+    {{range .Constraints}}{{.}}{{end}}
101
+    _, err := DB.Exec("INSERT {{.TableName}} VALUES({{.QueryValuesSection}}) ON DUPLICATE KEY UPDATE {{.UpsertDuplicate}}", 
102
+        {{range .ColOrder}}row.{{.CapitalizedColumnName}},
103
+{{end}})
104
+    if err != nil {return err}
105
+        {{if .PrimaryColumns}}
106
+          cache[row.{{range .PrimaryColumns}}{{.CapitalizedColumnName}}{{end}}] = row
107
+        {{end}}
108
+    return nil
109
+}
110
+`))
111
+	for table, tinfo := range tables {
112
+		os.Mkdir("db/"+table, 0755)
113
+		f, err := os.Create("db/" + table + "/" + table + ".go")
114
+		if err != nil {
115
+			return err
116
+		}
117
+		var b bytes.Buffer
118
+		err = tmpl.Execute(&b, tinfo)
119
+		if err != nil {
120
+			return err
121
+		}
122
+		bf, err := format.Source(b.Bytes())
123
+		if err != nil {
124
+			fmt.Println(b.String())
125
+			return err
126
+		}
127
+		_, err = io.Copy(f, bytes.NewBuffer(bf))
128
+		if err != nil {
129
+			return err
130
+		}
131
+	}
132
+	return nil
133
+}

+ 5 - 0
generator.go

@@ -0,0 +1,5 @@
1
+package main
2
+
3
+type Generator interface {
4
+	Generate(tables map[string]tableInfo) error
5
+}

+ 94 - 0
httpcodegenerator.go

@@ -0,0 +1,94 @@
1
+package main
2
+
3
+import (
4
+	"bytes"
5
+	"fmt"
6
+	"go/format"
7
+	"io"
8
+	"os"
9
+	"text/template"
10
+)
11
+
12
+type httpCodeGenerator struct {
13
+	DbRootPackageName string
14
+}
15
+
16
+func (g *httpCodeGenerator) Generate(tables map[string]tableInfo) error {
17
+	err := os.Mkdir("http", 0755)
18
+	if err != nil {
19
+		return err
20
+	}
21
+	tmpl := template.Must(template.New("httphandlers").Parse(`//WARNING.
22
+//THIS HAS BEEN GENERATED AUTOMATICALLY BY AUTOAPI.
23
+//IF THERE WAS A WARRANTY, MODIFYING THIS WOULD VOID IT.
24
+
25
+package {{.Table.TableName}}
26
+
27
+import (
28
+"{{.DbRootPackageName}}/{{.Table.TableName}}"
29
+
30
+"net/http"
31
+"fmt"
32
+"encoding/json"
33
+"github.com/gorilla/mux"
34
+)
35
+
36
+
37
+
38
+func List(res http.ResponseWriter, req *http.Request){
39
+    enc := json.NewEncoder(res)
40
+    rows, _ := {{.Table.TableName}}.All()
41
+    enc.Encode(rows)
42
+}
43
+
44
+func Get(res http.ResponseWriter, req *http.Request){
45
+    vars := mux.Vars(req)
46
+    enc := json.NewEncoder(res)
47
+    row, _ := {{.Table.TableName}}.GetBy{{.Table.PrimaryColumnsJoinedByAnd}}(vars["id"])
48
+    enc.Encode(row)
49
+}
50
+
51
+func Post(res http.ResponseWriter, req *http.Request){
52
+    dec := json.NewDecoder(req.Body)
53
+    row := &{{.Table.TableName}}.{{.Table.NormalizedTableName}}{}
54
+    dec.Decode(&row)
55
+    {{.Table.TableName}}.Save(row)
56
+}
57
+
58
+func Put(res http.ResponseWriter, req *http.Request){
59
+    dec := json.NewDecoder(req.Body)
60
+    row := &{{.Table.TableName}}.{{.Table.NormalizedTableName}}{}
61
+    dec.Decode(&row)
62
+    {{.Table.TableName}}.Save(row)
63
+}
64
+
65
+func Delete(res http.ResponseWriter, req *http.Request){
66
+    fmt.Fprintf(res, "Delete stub!")
67
+}
68
+
69
+`))
70
+	for table, tinfo := range tables {
71
+		os.Mkdir("http/"+table, 0755)
72
+		f, err := os.Create("http/" + table + "/" + table + ".go")
73
+		if err != nil {
74
+			return err
75
+		}
76
+		var b bytes.Buffer
77
+		err = tmpl.Execute(&b, map[string]interface{}{"Table": tinfo, "DbRootPackageName": "is-a-dev.com/autoapi/db"})
78
+		if err != nil {
79
+			return err
80
+		}
81
+		bf, err := format.Source(b.Bytes())
82
+		if err != nil {
83
+			fmt.Println(b.String())
84
+			return err
85
+		}
86
+		_, err = io.Copy(f, bytes.NewBuffer(bf))
87
+		if err != nil {
88
+			return err
89
+		}
90
+	}
91
+
92
+	tmpl = tmpl
93
+	return nil
94
+}

+ 7 - 248
main.go

@@ -1,16 +1,9 @@
1 1
 package main
2 2
 
3 3
 import (
4
-	"bytes"
5 4
 	"database/sql"
6 5
 	"fmt"
7
-	"io"
8 6
 	"os"
9
-	"strings"
10
-
11
-	"text/template"
12
-
13
-	"go/format"
14 7
 
15 8
 	_ "github.com/ziutek/mymysql/godrv"
16 9
 )
@@ -54,258 +47,24 @@ func main() {
54 47
 		col := tableColumn{ColumnName: cn, ColumnType: ct}
55 48
 
56 49
 		col.Primary = ck == "PRI"
57
-		fmt.Println(col, ck)
58 50
 		if nullable == "NO" && extra != "auto_increment" {
59
-			table.Constraints = append(table.Constraints, fmt.Sprintf(`if row.%s == %s {return errors.New("Preconditions failed, %s must be set.")}`, col.CapitalizedColumnName(), col.ColumnNullValue(), col.CapitalizedColumnName()))
51
+			table.Constraints = append(table.Constraints, fmt.Sprintf(`if row.%s == %s {return libautoapi.Error("Preconditions failed, %s must be set.")}`, col.CapitalizedColumnName(), col.ColumnNullValue(), col.CapitalizedColumnName()))
60 52
 		}
61 53
 		table.TableColumns[cn] = col
62 54
 		table.ColOrder = append(table.ColOrder, col)
63 55
 		tables[tn] = table
64 56
 	}
65 57
 
66
-	err = (&generator{}).Generate(tables)
58
+	err = (&dbCodeGenerator{}).Generate(tables)
67 59
 	if err != nil {
68 60
 		panic(err)
69 61
 	}
70
-}
71
-
72
-type tableInfo struct {
73
-	TableName    string
74
-	TableColumns map[string]tableColumn
75
-	ColOrder     []tableColumn
76
-	Constraints  []string
77
-}
78
-
79
-func capitalizedColumnNames(cols []tableColumn) []string {
80
-	result := []string{}
81
-	for _, c := range cols {
82
-		result = append(result, c.CapitalizedColumnName())
83
-	}
84
-	return result
85
-}
86
-
87
-func columnNames(cols []tableColumn) []string {
88
-	result := []string{}
89
-	for _, c := range cols {
90
-		result = append(result, c.ColumnName)
91
-	}
92
-	return result
93
-}
94
-
95
-func (t tableInfo) QueryFieldNames() string {
96
-	return strings.Join(columnNames(t.ColOrder), ",")
97
-}
98
-
99
-func (t tableInfo) QueryValuesSection() string {
100
-	return strings.Join(strings.Split(strings.Repeat("?", len(t.TableColumns)), ""), ",")
101
-}
102
-
103
-func (t tableInfo) NormalizedTableName() string {
104
-	var result string = ""
105
-	for _, tp := range strings.Split(t.TableName, "_") {
106
-		tp = strings.ToUpper(tp[0:1]) + strings.ToLower(tp[1:])
107
-		tp = strings.TrimSuffix(tp, "s")
108
-		result = result + tp
109
-	}
110
-	return result
111
-}
112
-
113
-func (t tableInfo) PrimaryColumns() []tableColumn {
114
-	result := make([]tableColumn, 0, len(t.ColOrder))
115
-	for _, col := range t.ColOrder {
116
-		if col.Primary {
117
-
118
-			result = append(result, col)
119
-		}
120
-	}
121
-	return result
122
-}
123
-
124
-func (t tableInfo) PrimaryWhere() string {
125
-	cols := columnNames(t.PrimaryColumns())
126
-	for i, col := range cols {
127
-		cols[i] = fmt.Sprintf("%s = ?", col)
128
-	}
129
-	return strings.Join(cols, " and ")
130
-}
131
-
132
-func (t tableInfo) PrimaryColumnsJoinedByAnd() string {
133
-	return strings.Join(capitalizedColumnNames(t.PrimaryColumns()), "And")
134
-}
135
-
136
-func colformat(cols []tableColumn, format string, joinstring string, str1, str2 func(tableColumn) string) string {
137
-	result := []string{}
138
-	for _, col := range cols {
139
-		result = append(result, fmt.Sprintf(format, str1(col), str2(col)))
140
-	}
141
-	return strings.Join(result, joinstring)
142
-}
143
-
144
-func lcn(c tableColumn) string { return c.LowercaseColumnName() }
145
-func mct(c tableColumn) string { return c.MappedColumnType() }
146
-
147
-func (t tableInfo) PrimaryColumnsParamList() string {
148
-	return colformat(t.PrimaryColumns(), "%s %s", ",", lcn, mct)
149
-}
150
-
151
-func (t tableInfo) UpsertDuplicate() string {
152
-	return colformat(t.ColOrder, "%s = VALUES(%s)", ",", lcn, lcn)
153
-}
154
-
155
-type tableColumn struct {
156
-	ColumnName string
157
-	ColumnType string
158
-	Primary    bool
159
-}
160
-
161
-func (t tableColumn) CapitalizedColumnName() string {
162
-	var result string = ""
163
-	for _, tp := range strings.Split(t.ColumnName, "_") {
164
-		tp = strings.ToUpper(tp[0:1]) + strings.ToLower(tp[1:])
165
-		result = result + tp
166
-	}
167
-	return result
168
-}
169
-
170
-func (t tableColumn) LowercaseColumnName() string {
171
-	return strings.ToLower(t.ColumnName)
172
-}
173
-
174
-func (tc tableColumn) MappedColumnType() string {
175
-	switch tc.ColumnType {
176
-	case "text", "tinytext", "mediumtext", "longtex",
177
-		"blob", "tinyblob", "mediumblob", "longblob",
178
-		"binary", "varbinary":
179
-		return "[]byte"
180
-	case "char", "varchar":
181
-		return "string"
182
-
183
-	}
184
-	return "interface{}"
185
-}
186
-
187
-func (tc tableColumn) ColumnNullValue() string {
188
-	switch tc.ColumnType {
189
-	case "text", "tinytext", "mediumtext", "longtex",
190
-		"blob", "tinyblob", "mediumblob", "longblob",
191
-		"binary", "varbinary":
192
-		return `nil`
193
-	case "char", "varchar":
194
-		return `""`
195
-
196
-	}
197
-	return "nil"
198
-}
199
-
200
-type generator struct {
201
-}
202
-
203
-func (g *generator) Generate(tables map[string]tableInfo) error {
204
-	err := os.Mkdir("db", 0755)
62
+	err = (&httpCodeGenerator{}).Generate(tables)
205 63
 	if err != nil {
206
-		return err
64
+		panic(err)
207 65
 	}
208
-
209
-	tmpl := template.Must(template.New("class").Parse(`//WARNING.
210
-//THIS HAS BEEN GENERATED AUTOMATICALLY BY AUTOAPI.
211
-//IF THERE WAS A WARRANTY, MODIFYING THIS WOULD VOID IT.
212
-
213
-package {{.TableName}}
214
-
215
-import (
216
-"is-a-dev.com/libautoapi"
217
-"errors"
218
-)
219
-
220
-var DB libautoapi.DB
221
-
222
-{{if .PrimaryColumns}}
223
-//type {{.NormalizedTableName}}Cache struct{
224
-
225
-//    rowsByKey map{{range .PrimaryColumns}}[{{.MappedColumnType}}]{{end}}*{{.NormalizedTableName}}
226
-
227
-//}
228
-
229
-var cache = map{{range .PrimaryColumns}}[{{.MappedColumnType}}]{{end}}*{{.NormalizedTableName}}{}
230
-
231
-{{end}}
232
-
233
-type {{.NormalizedTableName}} struct {
234
-{{range .ColOrder}}{{.CapitalizedColumnName}} {{.MappedColumnType}}
235
-{{end}}}
236
-
237
-func New() *{{.NormalizedTableName}}{
238
-    return &{{.NormalizedTableName}}{}
239
-}
240
-
241
-func All() ([]*{{.NormalizedTableName}}, error){
242
-    rows, err := DB.Query("SELECT {{.QueryFieldNames}} FROM {{.TableName}}")
243
-    if err != nil {
244
-        return nil,err
245
-    }
246
-    result := make([]*{{.NormalizedTableName}},0)
247
-    for rows.Next() {
248
-        r := &{{.NormalizedTableName}}{}
249
-        rows.Scan(
250
-            {{range .ColOrder}}&r.{{.CapitalizedColumnName}},
251
-            {{end}})
252
-        {{if .PrimaryColumns}}
253
-          cache[r.{{range .PrimaryColumns}}{{.CapitalizedColumnName}}{{end}}] = r
254
-        {{end}}
255
-        result = append(result, r)
256
-    }
257
-    return result, nil
258
-}
259
-
260
-func GetBy{{.PrimaryColumnsJoinedByAnd}}({{.PrimaryColumnsParamList}}) (*{{.NormalizedTableName}}, error) {
261
-    {{if .PrimaryColumns}}
262
-        if r, ok := cache[{{range .PrimaryColumns}}{{.LowercaseColumnName}}{{end}}]; ok { return r, nil}
263
-    {{end}}
264
-    row := &{{.NormalizedTableName}}{}
265
-    err := DB.QueryRow("SELECT {{.QueryFieldNames}} FROM {{.TableName}} WHERE {{.PrimaryWhere}}",
266
-    {{range .PrimaryColumns}}{{.LowercaseColumnName}},
267
-    {{end}}).Scan(
268
-        {{range .ColOrder}}&row.{{.CapitalizedColumnName}},
269
-        {{end}})
270
-    if err != nil {
271
-        return nil, err
272
-    }
273
-    return row, nil
274
-}
275
-
276
-func Save(row *{{.NormalizedTableName}}) error {
277
-    {{range .Constraints}}{{.}}{{end}}
278
-    _, err := DB.Exec("INSERT {{.TableName}} VALUES({{.QueryValuesSection}}) ON DUPLICATE KEY UPDATE {{.UpsertDuplicate}}", 
279
-        {{range .ColOrder}}row.{{.CapitalizedColumnName}},
280
-{{end}})
281
-    if err != nil {return err}
282
-        {{if .PrimaryColumns}}
283
-          cache[row.{{range .PrimaryColumns}}{{.CapitalizedColumnName}}{{end}}] = row
284
-        {{end}}
285
-    return nil
286
-}
287
-`))
288
-	for table, tinfo := range tables {
289
-		fmt.Println(tinfo.PrimaryColumns())
290
-		os.Mkdir("db/"+table, 0755)
291
-		f, err := os.Create("db/" + table + "/" + table + ".go")
292
-		if err != nil {
293
-			return err
294
-		}
295
-		var b bytes.Buffer
296
-		err = tmpl.Execute(&b, tinfo)
297
-		if err != nil {
298
-			return err
299
-		}
300
-		bf, err := format.Source(b.Bytes())
301
-		if err != nil {
302
-			fmt.Println(b.String())
303
-			return err
304
-		}
305
-		_, err = io.Copy(f, bytes.NewBuffer(bf))
306
-		if err != nil {
307
-			return err
308
-		}
66
+	err = (&mainGenerator{}).Generate(tables)
67
+	if err != nil {
68
+		panic(err)
309 69
 	}
310
-	return nil
311 70
 }

+ 80 - 0
maingenerator.go

@@ -0,0 +1,80 @@
1
+package main
2
+
3
+import (
4
+	"bytes"
5
+	"fmt"
6
+	"go/format"
7
+	"html/template"
8
+	"io"
9
+	"os"
10
+)
11
+
12
+type mainGenerator struct {
13
+	rootDbPackageName       string
14
+	rootHandlersPackageName string
15
+}
16
+
17
+func (g *mainGenerator) Generate(tables map[string]tableInfo) error {
18
+	os.Mkdir("bin", 0755)
19
+	importstmpl := template.Must(template.New("mainImports").Parse(`
20
+package main
21
+import({{$rootHandlersPackageName := .rootHandlersPackageName}}{{$rootdbpackagename := .rootdbpackagename}}
22
+{{range .Tables}}"{{$rootHandlersPackageName}}/{{.TableName}}"
23
+{{.TableName}}db "{{$rootdbpackagename}}/{{.TableName}}"
24
+{{end}}
25
+"net/http"
26
+	"github.com/gorilla/mux"
27
+"os"
28
+"database/sql"
29
+	_ "github.com/ziutek/mymysql/godrv"
30
+)
31
+`))
32
+	importstmpl = importstmpl
33
+	routestmpl := template.Must(template.New("mainRoutes").Parse(`
34
+func main(){
35
+	dbUrl := os.Args[1]
36
+    	db, err := sql.Open("mymysql", dbUrl)
37
+	if err != nil {
38
+		panic(err)
39
+	}
40
+    {{range .Tables}}
41
+    {{.TableName}}db.DB = db
42
+    {{end}}
43
+    r := mux.NewRouter()
44
+    g := r.Methods("GET").Subrouter()
45
+    po := r.Methods("POST").Subrouter()
46
+    pu := r.Methods("PUT").Subrouter()
47
+    d := r.Methods("DELETE").Subrouter()
48
+{{range .Tables}}
49
+g.HandleFunc("/{{.TableName}}/", {{.TableName}}.List)
50
+g.HandleFunc("/{{.TableName}}/{id}/", {{.TableName}}.Get)
51
+po.HandleFunc("/{{.TableName}}/", {{.TableName}}.Post)
52
+pu.HandleFunc("/{{.TableName}}/{id}/", {{.TableName}}.Put)
53
+d.HandleFunc("/{{.TableName}}/{id}/", {{.TableName}}.Delete)
54
+{{end}}
55
+
56
+http.ListenAndServe(":8080",r)
57
+}
58
+`))
59
+	routestmpl = routestmpl
60
+	var b bytes.Buffer
61
+	err := importstmpl.Execute(&b, map[string]interface{}{"rootHandlersPackageName": "is-a-dev.com/autoapi/http", "Tables": tables, "rootdbpackagename": "is-a-dev.com/autoapi/db"})
62
+	if err != nil {
63
+		fmt.Println(err)
64
+	}
65
+	var final bytes.Buffer
66
+	io.Copy(&final, &b)
67
+	b = bytes.Buffer{}
68
+	routestmpl.Execute(&b, map[string]interface{}{"Verbs": []string{"List", "Get", "Post", "Put", "Delete"}, "Tables": tables})
69
+	io.Copy(&final, &b)
70
+	f, err := os.Create("bin/main.go")
71
+	if err != nil {
72
+		return err
73
+	}
74
+	formatted, err := format.Source(final.Bytes())
75
+	if err != nil {
76
+		return err
77
+	}
78
+	io.Copy(f, bytes.NewBuffer(formatted))
79
+	return nil
80
+}

+ 78 - 0
tablecolumn.go

@@ -0,0 +1,78 @@
1
+package main
2
+
3
+import (
4
+	"fmt"
5
+	"strings"
6
+)
7
+
8
+type tableColumn struct {
9
+	ColumnName string
10
+	ColumnType string
11
+	Primary    bool
12
+}
13
+
14
+func (t tableColumn) CapitalizedColumnName() string {
15
+	var result string = ""
16
+	for _, tp := range strings.Split(t.ColumnName, "_") {
17
+		tp = strings.ToUpper(tp[0:1]) + strings.ToLower(tp[1:])
18
+		result = result + tp
19
+	}
20
+	return result
21
+}
22
+
23
+func (t tableColumn) LowercaseColumnName() string {
24
+	return strings.ToLower(t.ColumnName)
25
+}
26
+
27
+func (tc tableColumn) MappedColumnType() string {
28
+	switch tc.ColumnType {
29
+	case "text", "tinytext", "mediumtext", "longtex",
30
+		"blob", "tinyblob", "mediumblob", "longblob",
31
+		"binary", "varbinary":
32
+		return "[]byte"
33
+	case "char", "varchar":
34
+		return "string"
35
+
36
+	}
37
+	return "interface{}"
38
+}
39
+
40
+func (tc tableColumn) ColumnNullValue() string {
41
+	switch tc.ColumnType {
42
+	case "text", "tinytext", "mediumtext", "longtex",
43
+		"blob", "tinyblob", "mediumblob", "longblob",
44
+		"binary", "varbinary":
45
+		return `nil`
46
+	case "char", "varchar":
47
+		return `""`
48
+
49
+	}
50
+	return "nil"
51
+}
52
+
53
+func colformat(cols []tableColumn, format string, joinstring string, str1, str2 func(tableColumn) string) string {
54
+	result := []string{}
55
+	for _, col := range cols {
56
+		result = append(result, fmt.Sprintf(format, str1(col), str2(col)))
57
+	}
58
+	return strings.Join(result, joinstring)
59
+}
60
+
61
+func lcn(c tableColumn) string { return c.LowercaseColumnName() }
62
+func mct(c tableColumn) string { return c.MappedColumnType() }
63
+
64
+func capitalizedColumnNames(cols []tableColumn) []string {
65
+	result := []string{}
66
+	for _, c := range cols {
67
+		result = append(result, c.CapitalizedColumnName())
68
+	}
69
+	return result
70
+}
71
+
72
+func columnNames(cols []tableColumn) []string {
73
+	result := []string{}
74
+	for _, c := range cols {
75
+		result = append(result, c.ColumnName)
76
+	}
77
+	return result
78
+}

+ 62 - 0
tableinfo.go

@@ -0,0 +1,62 @@
1
+package main
2
+
3
+import (
4
+	"fmt"
5
+	"strings"
6
+)
7
+
8
+type tableInfo struct {
9
+	TableName    string
10
+	TableColumns map[string]tableColumn
11
+	ColOrder     []tableColumn
12
+	Constraints  []string
13
+}
14
+
15
+func (t tableInfo) QueryFieldNames() string {
16
+	return strings.Join(columnNames(t.ColOrder), ",")
17
+}
18
+
19
+func (t tableInfo) QueryValuesSection() string {
20
+	return strings.Join(strings.Split(strings.Repeat("?", len(t.TableColumns)), ""), ",")
21
+}
22
+
23
+func (t tableInfo) NormalizedTableName() string {
24
+	var result string = ""
25
+	for _, tp := range strings.Split(t.TableName, "_") {
26
+		tp = strings.ToUpper(tp[0:1]) + strings.ToLower(tp[1:])
27
+		tp = strings.TrimSuffix(tp, "s")
28
+		result = result + tp
29
+	}
30
+	return result
31
+}
32
+
33
+func (t tableInfo) PrimaryColumns() []tableColumn {
34
+	result := make([]tableColumn, 0, len(t.ColOrder))
35
+	for _, col := range t.ColOrder {
36
+		if col.Primary {
37
+
38
+			result = append(result, col)
39
+		}
40
+	}
41
+	return result
42
+}
43
+
44
+func (t tableInfo) PrimaryWhere() string {
45
+	cols := columnNames(t.PrimaryColumns())
46
+	for i, col := range cols {
47
+		cols[i] = fmt.Sprintf("%s = ?", col)
48
+	}
49
+	return strings.Join(cols, " and ")
50
+}
51
+
52
+func (t tableInfo) PrimaryColumnsJoinedByAnd() string {
53
+	return strings.Join(capitalizedColumnNames(t.PrimaryColumns()), "And")
54
+}
55
+
56
+func (t tableInfo) PrimaryColumnsParamList() string {
57
+	return colformat(t.PrimaryColumns(), "%s %s", ",", lcn, mct)
58
+}
59
+
60
+func (t tableInfo) UpsertDuplicate() string {
61
+	return colformat(t.ColOrder, "%s = VALUES(%s)", ",", lcn, lcn)
62
+}