123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334 |
- // Copyright 2017 The Xorm Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style
- // license that can be found in the LICENSE file.
- package main
- import (
- "bytes"
- "fmt"
- "io/ioutil"
- "os"
- "path"
- "path/filepath"
- "regexp"
- "strconv"
- "strings"
- "text/template"
- "github.com/go-xorm/core"
- "github.com/go-xorm/xorm"
- "github.com/lunny/log"
- _ "github.com/denisenkom/go-mssqldb"
- _ "github.com/go-sql-driver/mysql"
- _ "github.com/lib/pq"
- _ "github.com/ziutek/mymysql/godrv"
- )
- var CmdReverse = &Command{
- UsageLine: "reverse [-s] driverName datasourceName tmplPath [generatedPath] [tableFilterReg]",
- Short: "reverse a db to codes",
- Long: `
- according database's tables and columns to generate codes for Go, C++ and etc.
- -s Generated one go file for every table
- driverName Database driver name, now supported four: mysql mymysql sqlite3 postgres
- datasourceName Database connection uri, for detail infomation please visit driver's project page
- tmplPath Template dir for generated. the default templates dir has provide 1 template
- generatedPath This parameter is optional, if blank, the default value is models, then will
- generated all codes in models dir
- tableFilterReg Table name filter regexp
- `,
- }
- func init() {
- CmdReverse.Run = runReverse
- CmdReverse.Flags = map[string]bool{
- "-s": false,
- "-l": false,
- }
- }
- var (
- genJson bool = false
- ignoreColumnsJSON, created, updated, deleted []string = []string{}, []string{"created_at"}, []string{"updated_at"}, []string{"deleted_at"}
- )
- func printReversePrompt(flag string) {
- }
- type Tmpl struct {
- Tables []*core.Table
- Imports map[string]string
- Models string
- }
- func dirExists(dir string) bool {
- d, e := os.Stat(dir)
- switch {
- case e != nil:
- return false
- case !d.IsDir():
- return false
- }
- return true
- }
- func runReverse(cmd *Command, args []string) {
- num := checkFlags(cmd.Flags, args, printReversePrompt)
- if num == -1 {
- return
- }
- args = args[num:]
- if len(args) < 3 {
- fmt.Println("params error, please see xorm help reverse")
- return
- }
- var isMultiFile bool = true
- if use, ok := cmd.Flags["-s"]; ok {
- isMultiFile = !use
- }
- curPath, err := os.Getwd()
- if err != nil {
- fmt.Println(err)
- return
- }
- var genDir string
- var model string
- var filterPat *regexp.Regexp
- if len(args) >= 4 {
- genDir, err = filepath.Abs(args[3])
- if err != nil {
- fmt.Println(err)
- return
- }
- //[SWH|+] 经测试,path.Base不能解析windows下的“\”,需要替换为“/”
- genDir = strings.Replace(genDir, "\\", "/", -1)
- model = path.Base(genDir)
- if len(args) >= 5 {
- filterPat, err = regexp.Compile(args[4])
- if err != nil {
- fmt.Println(err)
- return
- }
- }
- } else {
- model = "models"
- genDir = path.Join(curPath, model)
- }
- dir, err := filepath.Abs(args[2])
- if err != nil {
- log.Errorf("%v", err)
- return
- }
- if !dirExists(dir) {
- log.Errorf("Template %v path is not exist", dir)
- return
- }
- var langTmpl LangTmpl
- var ok bool
- var lang string = "go"
- var prefix string = "" //[SWH|+]
- cfgPath := path.Join(dir, "config")
- info, err := os.Stat(cfgPath)
- var configs map[string]string
- if err == nil && !info.IsDir() {
- configs = loadConfig(cfgPath)
- if l, ok := configs["lang"]; ok {
- lang = l
- }
- if j, ok := configs["genJson"]; ok {
- genJson, err = strconv.ParseBool(j)
- }
- //[SWH|+]
- if j, ok := configs["prefix"]; ok {
- prefix = j
- }
- if j, ok := configs["ignoreColumnsJSON"]; ok {
- ignoreColumnsJSON = strings.Split(j, ",")
- }
- if j, ok := configs["created"]; ok {
- created = strings.Split(j, ",")
- }
- if j, ok := configs["updated"]; ok {
- updated = strings.Split(j, ",")
- }
- if j, ok := configs["deleted"]; ok {
- deleted = strings.Split(j, ",")
- }
- }
- if langTmpl, ok = langTmpls[lang]; !ok {
- fmt.Println("Unsupported programing language", lang)
- return
- }
- os.MkdirAll(genDir, os.ModePerm)
- supportComment = (args[0] == "mysql" || args[0] == "mymysql")
- Orm, err := xorm.NewEngine(args[0], args[1])
- if err != nil {
- log.Errorf("%v", err)
- return
- }
- tables, err := Orm.DBMetas()
- if err != nil {
- log.Errorf("%v", err)
- return
- }
- if filterPat != nil && len(tables) > 0 {
- size := 0
- for _, t := range tables {
- if filterPat.MatchString(t.Name) {
- tables[size] = t
- size++
- }
- }
- tables = tables[:size]
- }
- filepath.Walk(dir, func(f string, info os.FileInfo, err error) error {
- if info.IsDir() {
- return nil
- }
- if info.Name() == "config" {
- return nil
- }
- bs, err := ioutil.ReadFile(f)
- if err != nil {
- log.Errorf("%v", err)
- return err
- }
- t := template.New(f)
- t.Funcs(langTmpl.Funcs)
- tmpl, err := t.Parse(string(bs))
- if err != nil {
- log.Errorf("%v", err)
- return err
- }
- var w *os.File
- fileName := info.Name()
- newFileName := fileName[:len(fileName)-4]
- ext := path.Ext(newFileName)
- if !isMultiFile {
- w, err = os.Create(path.Join(genDir, newFileName))
- if err != nil {
- log.Errorf("%v", err)
- return err
- }
- imports := langTmpl.GenImports(tables)
- tbls := make([]*core.Table, 0)
- for _, table := range tables {
- //[SWH|+]
- if prefix != "" {
- table.Name = strings.TrimPrefix(table.Name, prefix)
- }
- tbls = append(tbls, table)
- }
- newbytes := bytes.NewBufferString("")
- t := &Tmpl{Tables: tbls, Imports: imports, Models: model}
- err = tmpl.Execute(newbytes, t)
- if err != nil {
- log.Errorf("%v", err)
- return err
- }
- tplcontent, err := ioutil.ReadAll(newbytes)
- if err != nil {
- log.Errorf("%v", err)
- return err
- }
- var source string
- if langTmpl.Formater != nil {
- source, err = langTmpl.Formater(string(tplcontent))
- if err != nil {
- log.Errorf("%v", err)
- return err
- }
- } else {
- source = string(tplcontent)
- }
- w.WriteString(source)
- w.Close()
- } else {
- for _, table := range tables {
- //[SWH|+]
- if prefix != "" {
- table.Name = strings.TrimPrefix(table.Name, prefix)
- }
- // imports
- tbs := []*core.Table{table}
- imports := langTmpl.GenImports(tbs)
- w, err := os.Create(path.Join(genDir, table.Name+ext))
- if err != nil {
- log.Errorf("%v", err)
- return err
- }
- defer w.Close()
- newbytes := bytes.NewBufferString("")
- t := &Tmpl{Tables: tbs, Imports: imports, Models: model}
- err = tmpl.Execute(newbytes, t)
- if err != nil {
- log.Errorf("%v", err)
- return err
- }
- tplcontent, err := ioutil.ReadAll(newbytes)
- if err != nil {
- log.Errorf("%v", err)
- return err
- }
- var source string
- if langTmpl.Formater != nil {
- source, err = langTmpl.Formater(string(tplcontent))
- if err != nil {
- log.Errorf("%v-%v", err, string(tplcontent))
- return err
- }
- } else {
- source = string(tplcontent)
- }
- w.WriteString(source)
- w.Close()
- }
- }
- return nil
- })
- }
|