remove xorm dir as it's relocated to github.com/go-xorm/cmd
This commit is contained in:
parent
e7277992ad
commit
a0919b5371
|
@ -1,2 +0,0 @@
|
||||||
[deps]
|
|
||||||
github.com/go-xorm/xorm=../
|
|
|
@ -1,62 +0,0 @@
|
||||||
# xorm tools
|
|
||||||
|
|
||||||
|
|
||||||
xorm tools is a set of tools for database operation.
|
|
||||||
|
|
||||||
## Install
|
|
||||||
|
|
||||||
`go get github.com/go-xorm/xorm/xorm`
|
|
||||||
|
|
||||||
and you should install the depends below:
|
|
||||||
|
|
||||||
* github.com/go-xorm/xorm
|
|
||||||
|
|
||||||
* Mysql: [github.com/go-sql-driver/mysql](https://github.com/go-sql-driver/mysql)
|
|
||||||
|
|
||||||
* MyMysql: [github.com/ziutek/mymysql/godrv](https://github.com/ziutek/mymysql/godrv)
|
|
||||||
|
|
||||||
* SQLite: [github.com/mattn/go-sqlite3](https://github.com/mattn/go-sqlite3)
|
|
||||||
|
|
||||||
* Postgres: [github.com/bylevel/pq](https://github.com/bylevel/pq)
|
|
||||||
|
|
||||||
|
|
||||||
## Reverse
|
|
||||||
|
|
||||||
After you installed the tool, you can type
|
|
||||||
|
|
||||||
`xorm help reverse`
|
|
||||||
|
|
||||||
to get help
|
|
||||||
|
|
||||||
example:
|
|
||||||
|
|
||||||
sqlite:
|
|
||||||
`xorm reverse sqite3 test.db templates/goxorm`
|
|
||||||
|
|
||||||
mysql:
|
|
||||||
`xorm reverse mysql root:@/xorm_test?charset=utf8 templates/goxorm`
|
|
||||||
|
|
||||||
mymysql:
|
|
||||||
`xorm reverse mymysql xorm_test2/root/ templates/goxorm`
|
|
||||||
|
|
||||||
postgres:
|
|
||||||
`xorm reverse postgres "dbname=xorm_test sslmode=disable" templates/goxorm`
|
|
||||||
|
|
||||||
will generated go files in `./model` directory
|
|
||||||
|
|
||||||
## Template and Config
|
|
||||||
|
|
||||||
Now, xorm tool supports go and c++ two languages and have go, goxorm, c++ three of default templates. In template directory, we can put a config file to control how to generating.
|
|
||||||
|
|
||||||
````
|
|
||||||
lang=go
|
|
||||||
genJson=1
|
|
||||||
```
|
|
||||||
|
|
||||||
lang must be go or c++ now.
|
|
||||||
genJson can be 1 or 0, if 1 then the struct will have json tag.
|
|
||||||
|
|
||||||
## LICENSE
|
|
||||||
|
|
||||||
BSD License
|
|
||||||
[http://creativecommons.org/licenses/BSD/](http://creativecommons.org/licenses/BSD/)
|
|
66
xorm/c++.go
66
xorm/c++.go
|
@ -1,66 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
//"fmt"
|
|
||||||
"strings"
|
|
||||||
"text/template"
|
|
||||||
|
|
||||||
"github.com/go-xorm/core"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
CPlusTmpl LangTmpl = LangTmpl{
|
|
||||||
template.FuncMap{"Mapper": mapper.Table2Obj,
|
|
||||||
"Type": cPlusTypeStr,
|
|
||||||
"UnTitle": unTitle,
|
|
||||||
},
|
|
||||||
nil,
|
|
||||||
genCPlusImports,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
func cPlusTypeStr(col *core.Column) string {
|
|
||||||
tp := col.SQLType
|
|
||||||
name := strings.ToUpper(tp.Name)
|
|
||||||
switch name {
|
|
||||||
case core.Bit, core.TinyInt, core.SmallInt, core.MediumInt, core.Int, core.Integer, core.Serial:
|
|
||||||
return "int"
|
|
||||||
case core.BigInt, core.BigSerial:
|
|
||||||
return "__int64"
|
|
||||||
case core.Char, core.Varchar, core.TinyText, core.Text, core.MediumText, core.LongText:
|
|
||||||
return "tstring"
|
|
||||||
case core.Date, core.DateTime, core.Time, core.TimeStamp:
|
|
||||||
return "time_t"
|
|
||||||
case core.Decimal, core.Numeric:
|
|
||||||
return "tstring"
|
|
||||||
case core.Real, core.Float:
|
|
||||||
return "float"
|
|
||||||
case core.Double:
|
|
||||||
return "double"
|
|
||||||
case core.TinyBlob, core.Blob, core.MediumBlob, core.LongBlob, core.Bytea:
|
|
||||||
return "tstring"
|
|
||||||
case core.Bool:
|
|
||||||
return "bool"
|
|
||||||
default:
|
|
||||||
return "tstring"
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func genCPlusImports(tables []*core.Table) map[string]string {
|
|
||||||
imports := make(map[string]string)
|
|
||||||
|
|
||||||
for _, table := range tables {
|
|
||||||
for _, col := range table.Columns() {
|
|
||||||
switch cPlusTypeStr(col) {
|
|
||||||
case "time_t":
|
|
||||||
imports[`<time.h>`] = `<time.h>`
|
|
||||||
case "tstring":
|
|
||||||
imports["<string>"] = "<string>"
|
|
||||||
//case "__int64":
|
|
||||||
// imports[""] = ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return imports
|
|
||||||
}
|
|
78
xorm/cmd.go
78
xorm/cmd.go
|
@ -1,78 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// A Command is an implementation of a go command
|
|
||||||
// like go build or go fix.
|
|
||||||
type Command struct {
|
|
||||||
// Run runs the command.
|
|
||||||
// The args are the arguments after the command name.
|
|
||||||
Run func(cmd *Command, args []string)
|
|
||||||
|
|
||||||
// UsageLine is the one-line usage message.
|
|
||||||
// The first word in the line is taken to be the command name.
|
|
||||||
UsageLine string
|
|
||||||
|
|
||||||
// Short is the short description shown in the 'go help' output.
|
|
||||||
Short string
|
|
||||||
|
|
||||||
// Long is the long message shown in the 'go help <this-command>' output.
|
|
||||||
Long string
|
|
||||||
|
|
||||||
// Flag is a set of flags specific to this command.
|
|
||||||
Flags map[string]bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// Name returns the command's name: the first word in the usage line.
|
|
||||||
func (c *Command) Name() string {
|
|
||||||
name := c.UsageLine
|
|
||||||
i := strings.Index(name, " ")
|
|
||||||
if i >= 0 {
|
|
||||||
name = name[:i]
|
|
||||||
}
|
|
||||||
return name
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Command) Usage() {
|
|
||||||
fmt.Fprintf(os.Stderr, "usage: %s\n\n", c.UsageLine)
|
|
||||||
fmt.Fprintf(os.Stderr, "%s\n", strings.TrimSpace(c.Long))
|
|
||||||
os.Exit(2)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Runnable reports whether the command can be run; otherwise
|
|
||||||
// it is a documentation pseudo-command such as importpath.
|
|
||||||
func (c *Command) Runnable() bool {
|
|
||||||
return c.Run != nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// checkFlags checks if the flag exists with correct format.
|
|
||||||
func checkFlags(flags map[string]bool, args []string, print func(string)) int {
|
|
||||||
num := 0 // Number of valid flags, use to cut out.
|
|
||||||
for i, f := range args {
|
|
||||||
// Check flag prefix '-'.
|
|
||||||
if !strings.HasPrefix(f, "-") {
|
|
||||||
// Not a flag, finish check process.
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if it a valid flag.
|
|
||||||
if v, ok := flags[f]; ok {
|
|
||||||
flags[f] = !v
|
|
||||||
if !v {
|
|
||||||
print(f)
|
|
||||||
} else {
|
|
||||||
fmt.Println("DISABLE: " + f)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
fmt.Printf("[ERRO] Unknown flag: %s.\n", f)
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
num = i + 1
|
|
||||||
}
|
|
||||||
|
|
||||||
return num
|
|
||||||
}
|
|
261
xorm/go.go
261
xorm/go.go
|
@ -1,261 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"go/format"
|
|
||||||
"reflect"
|
|
||||||
"strings"
|
|
||||||
"text/template"
|
|
||||||
|
|
||||||
"github.com/go-xorm/core"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
GoLangTmpl LangTmpl = LangTmpl{
|
|
||||||
template.FuncMap{"Mapper": mapper.Table2Obj,
|
|
||||||
"Type": typestring,
|
|
||||||
"Tag": tag,
|
|
||||||
"UnTitle": unTitle,
|
|
||||||
"gt": gt,
|
|
||||||
"getCol": getCol,
|
|
||||||
},
|
|
||||||
formatGo,
|
|
||||||
genGoImports,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
errBadComparisonType = errors.New("invalid type for comparison")
|
|
||||||
errBadComparison = errors.New("incompatible types for comparison")
|
|
||||||
errNoComparison = errors.New("missing argument for comparison")
|
|
||||||
)
|
|
||||||
|
|
||||||
type kind int
|
|
||||||
|
|
||||||
const (
|
|
||||||
invalidKind kind = iota
|
|
||||||
boolKind
|
|
||||||
complexKind
|
|
||||||
intKind
|
|
||||||
floatKind
|
|
||||||
integerKind
|
|
||||||
stringKind
|
|
||||||
uintKind
|
|
||||||
)
|
|
||||||
|
|
||||||
func basicKind(v reflect.Value) (kind, error) {
|
|
||||||
switch v.Kind() {
|
|
||||||
case reflect.Bool:
|
|
||||||
return boolKind, nil
|
|
||||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
||||||
return intKind, nil
|
|
||||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
|
||||||
return uintKind, nil
|
|
||||||
case reflect.Float32, reflect.Float64:
|
|
||||||
return floatKind, nil
|
|
||||||
case reflect.Complex64, reflect.Complex128:
|
|
||||||
return complexKind, nil
|
|
||||||
case reflect.String:
|
|
||||||
return stringKind, nil
|
|
||||||
}
|
|
||||||
return invalidKind, errBadComparisonType
|
|
||||||
}
|
|
||||||
|
|
||||||
// eq evaluates the comparison a == b || a == c || ...
|
|
||||||
func eq(arg1 interface{}, arg2 ...interface{}) (bool, error) {
|
|
||||||
v1 := reflect.ValueOf(arg1)
|
|
||||||
k1, err := basicKind(v1)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
if len(arg2) == 0 {
|
|
||||||
return false, errNoComparison
|
|
||||||
}
|
|
||||||
for _, arg := range arg2 {
|
|
||||||
v2 := reflect.ValueOf(arg)
|
|
||||||
k2, err := basicKind(v2)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
if k1 != k2 {
|
|
||||||
return false, errBadComparison
|
|
||||||
}
|
|
||||||
truth := false
|
|
||||||
switch k1 {
|
|
||||||
case boolKind:
|
|
||||||
truth = v1.Bool() == v2.Bool()
|
|
||||||
case complexKind:
|
|
||||||
truth = v1.Complex() == v2.Complex()
|
|
||||||
case floatKind:
|
|
||||||
truth = v1.Float() == v2.Float()
|
|
||||||
case intKind:
|
|
||||||
truth = v1.Int() == v2.Int()
|
|
||||||
case stringKind:
|
|
||||||
truth = v1.String() == v2.String()
|
|
||||||
case uintKind:
|
|
||||||
truth = v1.Uint() == v2.Uint()
|
|
||||||
default:
|
|
||||||
panic("invalid kind")
|
|
||||||
}
|
|
||||||
if truth {
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// lt evaluates the comparison a < b.
|
|
||||||
func lt(arg1, arg2 interface{}) (bool, error) {
|
|
||||||
v1 := reflect.ValueOf(arg1)
|
|
||||||
k1, err := basicKind(v1)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
v2 := reflect.ValueOf(arg2)
|
|
||||||
k2, err := basicKind(v2)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
if k1 != k2 {
|
|
||||||
return false, errBadComparison
|
|
||||||
}
|
|
||||||
truth := false
|
|
||||||
switch k1 {
|
|
||||||
case boolKind, complexKind:
|
|
||||||
return false, errBadComparisonType
|
|
||||||
case floatKind:
|
|
||||||
truth = v1.Float() < v2.Float()
|
|
||||||
case intKind:
|
|
||||||
truth = v1.Int() < v2.Int()
|
|
||||||
case stringKind:
|
|
||||||
truth = v1.String() < v2.String()
|
|
||||||
case uintKind:
|
|
||||||
truth = v1.Uint() < v2.Uint()
|
|
||||||
default:
|
|
||||||
panic("invalid kind")
|
|
||||||
}
|
|
||||||
return truth, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// le evaluates the comparison <= b.
|
|
||||||
func le(arg1, arg2 interface{}) (bool, error) {
|
|
||||||
// <= is < or ==.
|
|
||||||
lessThan, err := lt(arg1, arg2)
|
|
||||||
if lessThan || err != nil {
|
|
||||||
return lessThan, err
|
|
||||||
}
|
|
||||||
return eq(arg1, arg2)
|
|
||||||
}
|
|
||||||
|
|
||||||
// gt evaluates the comparison a > b.
|
|
||||||
func gt(arg1, arg2 interface{}) (bool, error) {
|
|
||||||
// > is the inverse of <=.
|
|
||||||
lessOrEqual, err := le(arg1, arg2)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
return !lessOrEqual, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getCol(cols map[string]*core.Column, name string) *core.Column {
|
|
||||||
return cols[name]
|
|
||||||
}
|
|
||||||
|
|
||||||
func formatGo(src string) (string, error) {
|
|
||||||
source, err := format.Source([]byte(src))
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return string(source), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func genGoImports(tables []*core.Table) map[string]string {
|
|
||||||
imports := make(map[string]string)
|
|
||||||
|
|
||||||
for _, table := range tables {
|
|
||||||
for _, col := range table.Columns() {
|
|
||||||
if typestring(col) == "time.Time" {
|
|
||||||
imports["time"] = "time"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return imports
|
|
||||||
}
|
|
||||||
|
|
||||||
func typestring(col *core.Column) string {
|
|
||||||
st := col.SQLType
|
|
||||||
t := core.SQLType2Type(st)
|
|
||||||
s := t.String()
|
|
||||||
if s == "[]uint8" {
|
|
||||||
return "[]byte"
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
func tag(table *core.Table, col *core.Column) string {
|
|
||||||
isNameId := (mapper.Table2Obj(col.Name) == "Id")
|
|
||||||
isIdPk := isNameId && typestring(col) == "int64"
|
|
||||||
|
|
||||||
res := make([]string, 0)
|
|
||||||
if !col.Nullable {
|
|
||||||
if !isIdPk {
|
|
||||||
res = append(res, "not null")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if col.IsPrimaryKey {
|
|
||||||
if !isIdPk {
|
|
||||||
res = append(res, "pk")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if col.Default != "" {
|
|
||||||
res = append(res, "default "+col.Default)
|
|
||||||
}
|
|
||||||
if col.IsAutoIncrement {
|
|
||||||
if !isIdPk {
|
|
||||||
res = append(res, "autoincr")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if col.IsCreated {
|
|
||||||
res = append(res, "created")
|
|
||||||
}
|
|
||||||
if col.IsUpdated {
|
|
||||||
res = append(res, "updated")
|
|
||||||
}
|
|
||||||
for name, _ := range col.Indexes {
|
|
||||||
index := table.Indexes[name]
|
|
||||||
var uistr string
|
|
||||||
if index.Type == core.UniqueType {
|
|
||||||
uistr = "unique"
|
|
||||||
} else if index.Type == core.IndexType {
|
|
||||||
uistr = "index"
|
|
||||||
}
|
|
||||||
if len(index.Cols) > 1 {
|
|
||||||
uistr += "(" + index.Name + ")"
|
|
||||||
}
|
|
||||||
res = append(res, uistr)
|
|
||||||
}
|
|
||||||
|
|
||||||
nstr := col.SQLType.Name
|
|
||||||
if col.Length != 0 {
|
|
||||||
if col.Length2 != 0 {
|
|
||||||
nstr += fmt.Sprintf("(%v,%v)", col.Length, col.Length2)
|
|
||||||
} else {
|
|
||||||
nstr += fmt.Sprintf("(%v)", col.Length)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
res = append(res, nstr)
|
|
||||||
|
|
||||||
var tags []string
|
|
||||||
if genJson {
|
|
||||||
tags = append(tags, "json:\""+col.Name+"\"")
|
|
||||||
}
|
|
||||||
if len(res) > 0 {
|
|
||||||
tags = append(tags, "xorm:\""+strings.Join(res, " ")+"\"")
|
|
||||||
}
|
|
||||||
if len(tags) > 0 {
|
|
||||||
return "`" + strings.Join(tags, " ") + "`"
|
|
||||||
} else {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
}
|
|
51
xorm/lang.go
51
xorm/lang.go
|
@ -1,51 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/go-xorm/core"
|
|
||||||
"io/ioutil"
|
|
||||||
"strings"
|
|
||||||
"text/template"
|
|
||||||
)
|
|
||||||
|
|
||||||
type LangTmpl struct {
|
|
||||||
Funcs template.FuncMap
|
|
||||||
Formater func(string) (string, error)
|
|
||||||
GenImports func([]*core.Table) map[string]string
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
mapper = &core.SnakeMapper{}
|
|
||||||
langTmpls = map[string]LangTmpl{
|
|
||||||
"go": GoLangTmpl,
|
|
||||||
"c++": CPlusTmpl,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
func loadConfig(f string) map[string]string {
|
|
||||||
bts, err := ioutil.ReadFile(f)
|
|
||||||
if err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
configs := make(map[string]string)
|
|
||||||
lines := strings.Split(string(bts), "\n")
|
|
||||||
for _, line := range lines {
|
|
||||||
line = strings.TrimRight(line, "\r")
|
|
||||||
vs := strings.Split(line, "=")
|
|
||||||
if len(vs) == 2 {
|
|
||||||
configs[strings.TrimSpace(vs[0])] = strings.TrimSpace(vs[1])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return configs
|
|
||||||
}
|
|
||||||
|
|
||||||
func unTitle(src string) string {
|
|
||||||
if src == "" {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(src) == 1 {
|
|
||||||
return strings.ToLower(string(src[0]))
|
|
||||||
} else {
|
|
||||||
return strings.ToLower(string(src[0])) + src[1:]
|
|
||||||
}
|
|
||||||
}
|
|
288
xorm/reverse.go
288
xorm/reverse.go
|
@ -1,288 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
"path/filepath"
|
|
||||||
"strconv"
|
|
||||||
"strings" //[SWH|+]
|
|
||||||
"text/template"
|
|
||||||
|
|
||||||
_ "github.com/bylevel/pq"
|
|
||||||
"github.com/dvirsky/go-pylog/logging"
|
|
||||||
_ "github.com/go-sql-driver/mysql"
|
|
||||||
"github.com/go-xorm/core"
|
|
||||||
"github.com/go-xorm/xorm"
|
|
||||||
|
|
||||||
_ "github.com/mattn/go-sqlite3"
|
|
||||||
_ "github.com/ziutek/mymysql/godrv"
|
|
||||||
)
|
|
||||||
|
|
||||||
var CmdReverse = &Command{
|
|
||||||
UsageLine: "reverse [-m] driverName datasourceName tmplPath [generatedPath]",
|
|
||||||
Short: "reverse a db to codes",
|
|
||||||
Long: `
|
|
||||||
according database's tables and columns to generate codes for Go, C++ and etc.
|
|
||||||
|
|
||||||
-m 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 model, then will
|
|
||||||
generated all codes in model dir
|
|
||||||
`,
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
CmdReverse.Run = runReverse
|
|
||||||
CmdReverse.Flags = map[string]bool{
|
|
||||||
"-s": false,
|
|
||||||
"-l": false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
genJson bool = false
|
|
||||||
)
|
|
||||||
|
|
||||||
func printReversePrompt(flag string) {
|
|
||||||
}
|
|
||||||
|
|
||||||
type Tmpl struct {
|
|
||||||
Tables []*core.Table
|
|
||||||
Imports map[string]string
|
|
||||||
Model 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(curPath)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var genDir string
|
|
||||||
var model string
|
|
||||||
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)
|
|
||||||
} else {
|
|
||||||
model = "model"
|
|
||||||
genDir = path.Join(curPath, model)
|
|
||||||
}
|
|
||||||
|
|
||||||
dir, err := filepath.Abs(args[2])
|
|
||||||
if err != nil {
|
|
||||||
logging.Error("%v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if !dirExists(dir) {
|
|
||||||
logging.Error("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 langTmpl, ok = langTmpls[lang]; !ok {
|
|
||||||
fmt.Println("Unsupported programing language", lang)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
os.MkdirAll(genDir, os.ModePerm)
|
|
||||||
|
|
||||||
Orm, err := xorm.NewEngine(args[0], args[1])
|
|
||||||
if err != nil {
|
|
||||||
logging.Error("%v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
tables, err := Orm.DBMetas()
|
|
||||||
if err != nil {
|
|
||||||
logging.Error("%v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
|
||||||
logging.Error("%v", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
t := template.New(f)
|
|
||||||
t.Funcs(langTmpl.Funcs)
|
|
||||||
|
|
||||||
tmpl, err := t.Parse(string(bs))
|
|
||||||
if err != nil {
|
|
||||||
logging.Error("%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.OpenFile(path.Join(genDir, newFileName), os.O_RDWR|os.O_CREATE, 0600)
|
|
||||||
if err != nil {
|
|
||||||
logging.Error("%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, Model: model}
|
|
||||||
err = tmpl.Execute(newbytes, t)
|
|
||||||
if err != nil {
|
|
||||||
logging.Error("%v", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
tplcontent, err := ioutil.ReadAll(newbytes)
|
|
||||||
if err != nil {
|
|
||||||
logging.Error("%v", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
var source string
|
|
||||||
if langTmpl.Formater != nil {
|
|
||||||
source, err = langTmpl.Formater(string(tplcontent))
|
|
||||||
if err != nil {
|
|
||||||
logging.Error("%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.OpenFile(path.Join(genDir, unTitle(mapper.Table2Obj(table.Name))+ext), os.O_RDWR|os.O_CREATE, 0600)
|
|
||||||
if err != nil {
|
|
||||||
logging.Error("%v", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
newbytes := bytes.NewBufferString("")
|
|
||||||
|
|
||||||
t := &Tmpl{Tables: tbs, Imports: imports, Model: model}
|
|
||||||
err = tmpl.Execute(newbytes, t)
|
|
||||||
if err != nil {
|
|
||||||
logging.Error("%v", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
tplcontent, err := ioutil.ReadAll(newbytes)
|
|
||||||
if err != nil {
|
|
||||||
logging.Error("%v", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
var source string
|
|
||||||
if langTmpl.Formater != nil {
|
|
||||||
source, err = langTmpl.Formater(string(tplcontent))
|
|
||||||
if err != nil {
|
|
||||||
logging.Error("%v-%v", err, string(tplcontent))
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
source = string(tplcontent)
|
|
||||||
}
|
|
||||||
|
|
||||||
w.WriteString(source)
|
|
||||||
w.Close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
|
|
||||||
}
|
|
147
xorm/shell.go
147
xorm/shell.go
|
@ -1,147 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/go-xorm/xorm"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
var CmdShell = &Command{
|
|
||||||
UsageLine: "shell driverName datasourceName",
|
|
||||||
Short: "a general shell to operate all kinds of database",
|
|
||||||
Long: `
|
|
||||||
general database's shell for sqlite3, mysql, postgres.
|
|
||||||
|
|
||||||
driverName Database driver name, now supported four: mysql mymysql sqlite3 postgres
|
|
||||||
datasourceName Database connection uri, for detail infomation please visit driver's project page
|
|
||||||
`,
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
CmdShell.Run = runShell
|
|
||||||
CmdShell.Flags = map[string]bool{}
|
|
||||||
}
|
|
||||||
|
|
||||||
var engine *xorm.Engine
|
|
||||||
|
|
||||||
func shellHelp() {
|
|
||||||
fmt.Println(`
|
|
||||||
show tables show all tables
|
|
||||||
columns <table_name> show table's column info
|
|
||||||
indexes <table_name> show table's index info
|
|
||||||
exit exit shell
|
|
||||||
source <sql_file> exec sql file to current database
|
|
||||||
dump [-nodata] <sql_file> dump structs or records to sql file
|
|
||||||
help show this document
|
|
||||||
<statement> SQL statement
|
|
||||||
`)
|
|
||||||
}
|
|
||||||
|
|
||||||
func runShell(cmd *Command, args []string) {
|
|
||||||
if len(args) != 2 {
|
|
||||||
fmt.Println("params error, please see xorm help shell")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var err error
|
|
||||||
engine, err = xorm.NewEngine(args[0], args[1])
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err = engine.Ping()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var scmd string
|
|
||||||
fmt.Print("xorm$ ")
|
|
||||||
for {
|
|
||||||
var input string
|
|
||||||
_, err := fmt.Scan(&input)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if strings.ToLower(input) == "exit" {
|
|
||||||
fmt.Println("bye")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !strings.HasSuffix(input, ";") {
|
|
||||||
scmd = scmd + " " + input
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
scmd = scmd + " " + input
|
|
||||||
lcmd := strings.TrimSpace(strings.ToLower(scmd))
|
|
||||||
if strings.HasPrefix(lcmd, "select") {
|
|
||||||
res, err := engine.Query(scmd + "\n")
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
} else {
|
|
||||||
if len(res) <= 0 {
|
|
||||||
fmt.Println("no records")
|
|
||||||
} else {
|
|
||||||
columns := make(map[string]int)
|
|
||||||
for k, _ := range res[0] {
|
|
||||||
columns[k] = len(k)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, m := range res {
|
|
||||||
for k, s := range m {
|
|
||||||
l := len(string(s))
|
|
||||||
if l > columns[k] {
|
|
||||||
columns[k] = l
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var maxlen = 0
|
|
||||||
for _, l := range columns {
|
|
||||||
maxlen = maxlen + l + 3
|
|
||||||
}
|
|
||||||
maxlen = maxlen + 1
|
|
||||||
|
|
||||||
fmt.Println(strings.Repeat("-", maxlen))
|
|
||||||
fmt.Print("|")
|
|
||||||
slice := make([]string, 0)
|
|
||||||
for k, l := range columns {
|
|
||||||
fmt.Print(" " + k + " ")
|
|
||||||
fmt.Print(strings.Repeat(" ", l-len(k)))
|
|
||||||
fmt.Print("|")
|
|
||||||
slice = append(slice, k)
|
|
||||||
}
|
|
||||||
fmt.Print("\n")
|
|
||||||
for _, r := range res {
|
|
||||||
fmt.Print("|")
|
|
||||||
for _, k := range slice {
|
|
||||||
fmt.Print(" " + string(r[k]) + " ")
|
|
||||||
fmt.Print(strings.Repeat(" ", columns[k]-len(string(r[k]))))
|
|
||||||
fmt.Print("|")
|
|
||||||
}
|
|
||||||
fmt.Print("\n")
|
|
||||||
}
|
|
||||||
fmt.Println(strings.Repeat("-", maxlen))
|
|
||||||
//fmt.Println(res)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if lcmd == "show tables;" {
|
|
||||||
/*tables, err := engine.DBMetas()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
} else {
|
|
||||||
|
|
||||||
}*/
|
|
||||||
} else {
|
|
||||||
cnt, err := engine.Exec(scmd)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
} else {
|
|
||||||
fmt.Printf("%d records changed.\n", cnt)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
scmd = ""
|
|
||||||
fmt.Print("xorm$ ")
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,21 +0,0 @@
|
||||||
{{ range .Imports}}
|
|
||||||
#include {{.}}
|
|
||||||
{{ end }}
|
|
||||||
|
|
||||||
{{range .Tables}}class {{Mapper .Name}} {
|
|
||||||
{{$table := .}}
|
|
||||||
public:
|
|
||||||
{{range .Columns}}{{$name := Mapper .Name}} {{Type .}} Get{{Mapper .Name}}() {
|
|
||||||
return this->m_{{UnTitle $name}};
|
|
||||||
}
|
|
||||||
|
|
||||||
void Set{{$name}}({{Type .}} {{UnTitle $name}}) {
|
|
||||||
this->m_{{UnTitle $name}} = {{UnTitle $name}};
|
|
||||||
}
|
|
||||||
|
|
||||||
{{end}}private:
|
|
||||||
{{range .Columns}}{{$name := Mapper .Name}} {{Type .}} m_{{UnTitle $name}};
|
|
||||||
{{end}}
|
|
||||||
}
|
|
||||||
|
|
||||||
{{end}}
|
|
|
@ -1 +0,0 @@
|
||||||
lang=c++
|
|
|
@ -1 +0,0 @@
|
||||||
lang=go
|
|
|
@ -1,14 +0,0 @@
|
||||||
package {{.Model}}
|
|
||||||
|
|
||||||
import (
|
|
||||||
{{range .Imports}}"{{.}}"{{end}}
|
|
||||||
)
|
|
||||||
|
|
||||||
{{range .Tables}}
|
|
||||||
type {{Mapper .Name}} struct {
|
|
||||||
{{$table := .}}
|
|
||||||
{{range .Columns}} {{Mapper .Name}} {{Type .}}
|
|
||||||
{{end}}
|
|
||||||
}
|
|
||||||
|
|
||||||
{{end}}
|
|
|
@ -1,3 +0,0 @@
|
||||||
lang=go
|
|
||||||
genJson=0
|
|
||||||
prefix=cos_
|
|
|
@ -1,18 +0,0 @@
|
||||||
package {{.Model}}
|
|
||||||
|
|
||||||
{{$ilen := len .Imports}}
|
|
||||||
{{if gt $ilen 0}}
|
|
||||||
import (
|
|
||||||
{{range .Imports}}"{{.}}"{{end}}
|
|
||||||
)
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
{{range .Tables}}
|
|
||||||
type {{Mapper .Name}} struct {
|
|
||||||
{{$table := .}}
|
|
||||||
{{$columns := .Columns}}
|
|
||||||
{{range .ColumnsSeq}}{{$col := getCol $columns .}} {{Mapper $col.Name}} {{Type $col}} {{Tag $table $col}}
|
|
||||||
{{end}}
|
|
||||||
}
|
|
||||||
|
|
||||||
{{end}}
|
|
162
xorm/xorm.go
162
xorm/xorm.go
|
@ -1,162 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/dvirsky/go-pylog/logging"
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
"runtime"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
"text/template"
|
|
||||||
"unicode"
|
|
||||||
"unicode/utf8"
|
|
||||||
)
|
|
||||||
|
|
||||||
// +build go1.1
|
|
||||||
|
|
||||||
// Test that go1.1 tag above is included in builds. main.go refers to this definition.
|
|
||||||
const go11tag = true
|
|
||||||
|
|
||||||
const version = "0.1"
|
|
||||||
|
|
||||||
// Commands lists the available commands and help topics.
|
|
||||||
// The order here is the order in which they are printed by 'gopm help'.
|
|
||||||
var commands = []*Command{
|
|
||||||
CmdReverse,
|
|
||||||
CmdShell,
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
runtime.GOMAXPROCS(runtime.NumCPU())
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
logging.SetLevel(logging.ALL)
|
|
||||||
// Check length of arguments.
|
|
||||||
args := os.Args[1:]
|
|
||||||
if len(args) < 1 {
|
|
||||||
usage()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show help documentation.
|
|
||||||
if args[0] == "help" {
|
|
||||||
help(args[1:])
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check commands and run.
|
|
||||||
for _, comm := range commands {
|
|
||||||
if comm.Name() == args[0] && comm.Run != nil {
|
|
||||||
comm.Run(comm, args[1:])
|
|
||||||
exit()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Fprintf(os.Stderr, "xorm: unknown subcommand %q\nRun 'xorm help' for usage.\n", args[0])
|
|
||||||
setExitStatus(2)
|
|
||||||
exit()
|
|
||||||
}
|
|
||||||
|
|
||||||
var exitStatus = 0
|
|
||||||
var exitMu sync.Mutex
|
|
||||||
|
|
||||||
func setExitStatus(n int) {
|
|
||||||
exitMu.Lock()
|
|
||||||
if exitStatus < n {
|
|
||||||
exitStatus = n
|
|
||||||
}
|
|
||||||
exitMu.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
var usageTemplate = `xorm is a database tool based xorm package.
|
|
||||||
Usage:
|
|
||||||
|
|
||||||
xorm command [arguments]
|
|
||||||
|
|
||||||
The commands are:
|
|
||||||
{{range .}}{{if .Runnable}}
|
|
||||||
{{.Name | printf "%-11s"}} {{.Short}}{{end}}{{end}}
|
|
||||||
|
|
||||||
Use "xorm help [command]" for more information about a command.
|
|
||||||
|
|
||||||
Additional help topics:
|
|
||||||
{{range .}}{{if not .Runnable}}
|
|
||||||
{{.Name | printf "%-11s"}} {{.Short}}{{end}}{{end}}
|
|
||||||
|
|
||||||
Use "xorm help [topic]" for more information about that topic.
|
|
||||||
|
|
||||||
`
|
|
||||||
|
|
||||||
var helpTemplate = `{{if .Runnable}}usage: xorm {{.UsageLine}}
|
|
||||||
|
|
||||||
{{end}}{{.Long | trim}}
|
|
||||||
`
|
|
||||||
|
|
||||||
// tmpl executes the given template text on data, writing the result to w.
|
|
||||||
func tmpl(w io.Writer, text string, data interface{}) {
|
|
||||||
t := template.New("top")
|
|
||||||
t.Funcs(template.FuncMap{"trim": strings.TrimSpace, "capitalize": capitalize})
|
|
||||||
template.Must(t.Parse(text))
|
|
||||||
if err := t.Execute(w, data); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func capitalize(s string) string {
|
|
||||||
if s == "" {
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
r, n := utf8.DecodeRuneInString(s)
|
|
||||||
return string(unicode.ToTitle(r)) + s[n:]
|
|
||||||
}
|
|
||||||
|
|
||||||
func printUsage(w io.Writer) {
|
|
||||||
tmpl(w, usageTemplate, commands)
|
|
||||||
}
|
|
||||||
|
|
||||||
func usage() {
|
|
||||||
printUsage(os.Stderr)
|
|
||||||
os.Exit(2)
|
|
||||||
}
|
|
||||||
|
|
||||||
// help implements the 'help' command.
|
|
||||||
func help(args []string) {
|
|
||||||
if len(args) == 0 {
|
|
||||||
printUsage(os.Stdout)
|
|
||||||
// not exit 2: succeeded at 'gopm help'.
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if len(args) != 1 {
|
|
||||||
fmt.Fprintf(os.Stderr, "usage: xorm help command\n\nToo many arguments given.\n")
|
|
||||||
os.Exit(2) // failed at 'gopm help'
|
|
||||||
}
|
|
||||||
|
|
||||||
arg := args[0]
|
|
||||||
|
|
||||||
for _, cmd := range commands {
|
|
||||||
if cmd.Name() == arg {
|
|
||||||
tmpl(os.Stdout, helpTemplate, cmd)
|
|
||||||
// not exit 2: succeeded at 'gopm help cmd'.
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Fprintf(os.Stderr, "Unknown help topic %#q. Run 'xorm help'.\n", arg)
|
|
||||||
os.Exit(2) // failed at 'gopm help cmd'
|
|
||||||
}
|
|
||||||
|
|
||||||
var atexitFuncs []func()
|
|
||||||
|
|
||||||
func atexit(f func()) {
|
|
||||||
atexitFuncs = append(atexitFuncs, f)
|
|
||||||
}
|
|
||||||
|
|
||||||
func exit() {
|
|
||||||
for _, f := range atexitFuncs {
|
|
||||||
f()
|
|
||||||
}
|
|
||||||
os.Exit(exitStatus)
|
|
||||||
}
|
|
Loading…
Reference in New Issue