Beego/parser.go

250 lines
6.3 KiB
Go
Raw Normal View History

2014-08-18 08:41:43 +00:00
// Copyright 2014 beego Author. All Rights Reserved.
2014-07-03 15:40:21 +00:00
//
2014-08-18 08:41:43 +00:00
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
2014-07-03 15:40:21 +00:00
//
2014-08-18 08:41:43 +00:00
// http://www.apache.org/licenses/LICENSE-2.0
2014-07-03 15:40:21 +00:00
//
2014-08-18 08:41:43 +00:00
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
2014-06-08 12:24:01 +00:00
package beego
2014-06-09 02:11:37 +00:00
import (
"encoding/json"
2014-06-09 09:33:04 +00:00
"errors"
"fmt"
"go/ast"
"go/parser"
"go/token"
"io/ioutil"
2014-06-09 02:11:37 +00:00
"os"
2016-04-27 15:57:22 +00:00
"path/filepath"
"sort"
2014-06-09 09:33:04 +00:00
"strings"
"github.com/astaxie/beego/logs"
"github.com/astaxie/beego/utils"
2014-06-09 02:11:37 +00:00
)
2014-06-09 09:33:04 +00:00
var globalRouterTemplate = `package routers
2014-06-09 02:11:37 +00:00
import (
"github.com/astaxie/beego"
)
func init() {
{{.globalinfo}}
2014-06-09 02:11:37 +00:00
}
`
var (
2015-09-08 13:45:45 +00:00
lastupdateFilename = "lastupdate.tmp"
2014-11-05 14:23:54 +00:00
commentFilename string
pkgLastupdate map[string]int64
genInfoList map[string][]ControllerComments
)
2014-06-09 02:11:37 +00:00
2015-09-08 13:45:45 +00:00
const coomentPrefix = "commentsRouter_"
2014-11-05 14:23:54 +00:00
2014-06-09 09:33:04 +00:00
func init() {
pkgLastupdate = make(map[string]int64)
2014-06-09 09:33:04 +00:00
}
2014-06-09 02:11:37 +00:00
2014-06-09 09:33:04 +00:00
func parserPkg(pkgRealpath, pkgpath string) error {
2016-05-05 11:28:09 +00:00
rep := strings.NewReplacer("\\", "_", "/", "_", ".", "_")
2016-05-06 05:26:48 +00:00
commentFilename, _ = filepath.Rel(AppPath, pkgRealpath)
commentFilename = coomentPrefix + rep.Replace(commentFilename) + ".go"
if !compareFile(pkgRealpath) {
logs.Info(pkgRealpath + " no changed")
return nil
}
2014-11-06 08:25:47 +00:00
genInfoList = make(map[string][]ControllerComments)
2014-06-09 09:33:04 +00:00
fileSet := token.NewFileSet()
astPkgs, err := parser.ParseDir(fileSet, pkgRealpath, func(info os.FileInfo) bool {
name := info.Name()
return !info.IsDir() && !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".go")
}, parser.ParseComments)
2014-06-09 02:11:37 +00:00
2014-06-09 09:33:04 +00:00
if err != nil {
return err
}
for _, pkg := range astPkgs {
for _, fl := range pkg.Files {
for _, d := range fl.Decls {
switch specDecl := d.(type) {
case *ast.FuncDecl:
2014-08-05 00:56:04 +00:00
if specDecl.Recv != nil {
2015-09-10 09:35:57 +00:00
exp, ok := specDecl.Recv.List[0].Type.(*ast.StarExpr) // Check that the type is correct first beforing throwing to parser
if ok {
parserComments(specDecl.Doc, specDecl.Name.String(), fmt.Sprint(exp.X), pkgpath)
}
2014-08-05 00:56:04 +00:00
}
2014-06-09 09:33:04 +00:00
}
}
}
}
2016-04-27 15:57:22 +00:00
genRouterCode(pkgRealpath)
savetoFile(pkgRealpath)
2014-06-09 09:33:04 +00:00
return nil
}
func parserComments(comments *ast.CommentGroup, funcName, controllerName, pkgpath string) error {
if comments != nil && comments.List != nil {
for _, c := range comments.List {
t := strings.TrimSpace(strings.TrimLeft(c.Text, "//"))
if strings.HasPrefix(t, "@router") {
elements := strings.TrimLeft(t, "@router ")
e1 := strings.SplitN(elements, " ", 2)
if len(e1) < 1 {
2016-08-17 14:56:21 +00:00
return errors.New("you should has router information")
2014-06-09 09:33:04 +00:00
}
key := pkgpath + ":" + controllerName
cc := ControllerComments{}
cc.Method = funcName
cc.Router = e1[0]
2014-06-09 09:33:04 +00:00
if len(e1) == 2 && e1[1] != "" {
e1 = strings.SplitN(e1[1], " ", 2)
if len(e1) >= 1 {
cc.AllowHTTPMethods = strings.Split(strings.Trim(e1[0], "[]"), ",")
2014-06-09 09:33:04 +00:00
} else {
cc.AllowHTTPMethods = append(cc.AllowHTTPMethods, "get")
2014-06-09 09:33:04 +00:00
}
} else {
cc.AllowHTTPMethods = append(cc.AllowHTTPMethods, "get")
2014-06-09 09:33:04 +00:00
}
if len(e1) == 2 && e1[1] != "" {
keyval := strings.Split(strings.Trim(e1[1], "[]"), " ")
for _, kv := range keyval {
kk := strings.Split(kv, ":")
cc.Params = append(cc.Params, map[string]string{strings.Join(kk[:len(kk)-1], ":"): kk[len(kk)-1]})
2014-06-09 09:33:04 +00:00
}
}
genInfoList[key] = append(genInfoList[key], cc)
}
}
}
return nil
}
2016-04-27 15:57:22 +00:00
func genRouterCode(pkgRealpath string) {
os.Mkdir(getRouterDir(pkgRealpath), 0755)
logs.Info("generate router from comments")
2015-09-08 13:45:45 +00:00
var (
globalinfo string
sortKey []string
)
for k := range genInfoList {
sortKey = append(sortKey, k)
}
sort.Strings(sortKey)
for _, k := range sortKey {
cList := genInfoList[k]
2014-06-09 09:33:04 +00:00
for _, c := range cList {
allmethod := "nil"
if len(c.AllowHTTPMethods) > 0 {
2014-06-09 09:33:04 +00:00
allmethod = "[]string{"
for _, m := range c.AllowHTTPMethods {
2014-06-09 09:33:04 +00:00
allmethod += `"` + m + `",`
}
allmethod = strings.TrimRight(allmethod, ",") + "}"
}
params := "nil"
if len(c.Params) > 0 {
2014-06-09 09:33:04 +00:00
params = "[]map[string]string{"
for _, p := range c.Params {
2014-06-09 09:33:04 +00:00
for k, v := range p {
params = params + `map[string]string{` + k + `:"` + v + `"},`
}
}
params = strings.TrimRight(params, ",") + "}"
}
globalinfo = globalinfo + `
beego.GlobalControllerRouter["` + k + `"] = append(beego.GlobalControllerRouter["` + k + `"],
beego.ControllerComments{
2016-07-06 04:53:47 +00:00
Method: "` + strings.TrimSpace(c.Method) + `",
` + "Router: `" + c.Router + "`" + `,
AllowHTTPMethods: ` + allmethod + `,
Params: ` + params + `})
`
2014-06-10 03:02:41 +00:00
}
}
if globalinfo != "" {
2016-05-06 05:26:48 +00:00
f, err := os.Create(filepath.Join(getRouterDir(pkgRealpath), commentFilename))
2014-06-10 03:02:41 +00:00
if err != nil {
panic(err)
2014-06-09 09:33:04 +00:00
}
2014-06-10 03:02:41 +00:00
defer f.Close()
f.WriteString(strings.Replace(globalRouterTemplate, "{{.globalinfo}}", globalinfo, -1))
2014-06-09 09:33:04 +00:00
}
2014-06-09 02:11:37 +00:00
}
func compareFile(pkgRealpath string) bool {
2016-05-06 05:26:48 +00:00
if !utils.FileExists(filepath.Join(getRouterDir(pkgRealpath), commentFilename)) {
return true
}
2015-12-09 15:35:04 +00:00
if utils.FileExists(lastupdateFilename) {
content, err := ioutil.ReadFile(lastupdateFilename)
if err != nil {
return true
}
json.Unmarshal(content, &pkgLastupdate)
2014-06-30 07:57:36 +00:00
lastupdate, err := getpathTime(pkgRealpath)
if err != nil {
return true
}
if v, ok := pkgLastupdate[pkgRealpath]; ok {
2014-06-30 07:57:36 +00:00
if lastupdate <= v {
return false
}
}
}
return true
}
func savetoFile(pkgRealpath string) {
2014-06-30 07:57:36 +00:00
lastupdate, err := getpathTime(pkgRealpath)
if err != nil {
return
}
2014-06-30 07:57:36 +00:00
pkgLastupdate[pkgRealpath] = lastupdate
d, err := json.Marshal(pkgLastupdate)
if err != nil {
return
}
2015-12-09 15:35:04 +00:00
ioutil.WriteFile(lastupdateFilename, d, os.ModePerm)
}
2014-06-30 07:57:36 +00:00
func getpathTime(pkgRealpath string) (lastupdate int64, err error) {
fl, err := ioutil.ReadDir(pkgRealpath)
if err != nil {
return lastupdate, err
}
for _, f := range fl {
if lastupdate < f.ModTime().UnixNano() {
lastupdate = f.ModTime().UnixNano()
}
}
return lastupdate, nil
}
2016-04-27 15:57:22 +00:00
func getRouterDir(pkgRealpath string) string {
dir := filepath.Dir(pkgRealpath)
for {
2016-05-06 05:26:48 +00:00
d := filepath.Join(dir, "routers")
if utils.FileExists(d) {
2016-04-27 15:57:22 +00:00
return d
}
2016-05-06 05:26:48 +00:00
if r, _ := filepath.Rel(dir, AppPath); r == "." {
2016-04-27 15:57:22 +00:00
return d
}
// Parent dir.
dir = filepath.Dir(dir)
}
}