2024-04-30 19:30:09 +08:00
|
|
|
/*
|
|
|
|
* Copyright 2022 CloudWeGo Authors
|
|
|
|
*
|
|
|
|
* 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
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
|
|
|
package generator
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"fmt"
|
|
|
|
"io/ioutil"
|
|
|
|
"path/filepath"
|
|
|
|
"strings"
|
|
|
|
"text/template"
|
|
|
|
|
2024-04-30 23:07:06 +08:00
|
|
|
"golib.gaore.com/GaoreGo/gr_hz/util"
|
|
|
|
"golib.gaore.com/GaoreGo/gr_hz/util/logs"
|
2024-04-30 19:30:09 +08:00
|
|
|
)
|
|
|
|
|
|
|
|
type FilePathRenderInfo struct {
|
|
|
|
MasterIDLName string // master IDL name
|
|
|
|
GenPackage string // master IDL generate code package
|
|
|
|
HandlerDir string // handler generate dir
|
|
|
|
ModelDir string // model generate dir
|
|
|
|
RouterDir string // router generate dir
|
|
|
|
ProjectDir string // projectDir
|
|
|
|
GoModule string // go module
|
|
|
|
ServiceName string // service name, changed as services are traversed
|
|
|
|
MethodName string // method name, changed as methods are traversed
|
|
|
|
HandlerGenPath string // "api.gen_path" value
|
|
|
|
}
|
|
|
|
|
|
|
|
type IDLPackageRenderInfo struct {
|
|
|
|
FilePathRenderInfo
|
|
|
|
ServiceInfos *HttpPackage
|
|
|
|
}
|
|
|
|
|
|
|
|
type CustomizedFileForMethod struct {
|
|
|
|
*HttpMethod
|
|
|
|
FilePath string
|
|
|
|
FilePackage string
|
|
|
|
ServiceInfo *Service // service info for this method
|
|
|
|
IDLPackageInfo *IDLPackageRenderInfo // IDL info for this service
|
|
|
|
}
|
|
|
|
|
|
|
|
type CustomizedFileForService struct {
|
|
|
|
*Service
|
|
|
|
FilePath string
|
|
|
|
FilePackage string
|
|
|
|
IDLPackageInfo *IDLPackageRenderInfo // IDL info for this service
|
|
|
|
}
|
|
|
|
|
|
|
|
type CustomizedFileForIDL struct {
|
|
|
|
*IDLPackageRenderInfo
|
|
|
|
FilePath string
|
|
|
|
FilePackage string
|
|
|
|
}
|
|
|
|
|
|
|
|
// todo: 1. how to import other file, if the other file name is a template
|
|
|
|
|
|
|
|
// genCustomizedFile generate customized file template
|
|
|
|
func (pkgGen *HttpPackageGenerator) genCustomizedFile(pkg *HttpPackage) error {
|
|
|
|
filePathRenderInfo := FilePathRenderInfo{
|
|
|
|
MasterIDLName: pkg.IdlName,
|
|
|
|
GenPackage: pkg.Package,
|
|
|
|
HandlerDir: pkgGen.HandlerDir,
|
|
|
|
ModelDir: pkgGen.ModelDir,
|
|
|
|
RouterDir: pkgGen.RouterDir,
|
|
|
|
ProjectDir: pkgGen.OutputDir,
|
|
|
|
GoModule: pkgGen.ProjPackage,
|
|
|
|
// methodName & serviceName will change as traverse
|
|
|
|
}
|
|
|
|
|
|
|
|
idlPackageRenderInfo := IDLPackageRenderInfo{
|
|
|
|
FilePathRenderInfo: filePathRenderInfo,
|
|
|
|
ServiceInfos: pkg,
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, tplInfo := range pkgGen.tplsInfo {
|
|
|
|
// the default template has been automatically generated by the tool, so skip
|
|
|
|
if tplInfo.Default {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
// loop generate file
|
|
|
|
if tplInfo.LoopService || tplInfo.LoopMethod {
|
|
|
|
loopMethod := tplInfo.LoopMethod
|
|
|
|
loopService := tplInfo.LoopService
|
|
|
|
if loopService && !loopMethod { // only loop service
|
|
|
|
for _, service := range idlPackageRenderInfo.ServiceInfos.Services {
|
|
|
|
filePathRenderInfo.ServiceName = service.Name
|
|
|
|
err := pkgGen.genLoopService(tplInfo, filePathRenderInfo, service, &idlPackageRenderInfo)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else { // loop service & method, because if loop method, the service must be looped
|
|
|
|
for _, service := range idlPackageRenderInfo.ServiceInfos.Services {
|
|
|
|
for _, method := range service.Methods {
|
|
|
|
filePathRenderInfo.ServiceName = service.Name
|
|
|
|
filePathRenderInfo.MethodName = method.Name
|
|
|
|
filePathRenderInfo.HandlerGenPath = method.OutputDir
|
|
|
|
err := pkgGen.genLoopMethod(tplInfo, filePathRenderInfo, method, service, &idlPackageRenderInfo)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else { // generate customized file single
|
|
|
|
err := pkgGen.genSingleCustomizedFile(tplInfo, filePathRenderInfo, idlPackageRenderInfo)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// renderFilePath used to render file path template to get real file path
|
|
|
|
func renderFilePath(tplInfo *Template, filePathRenderInfo FilePathRenderInfo) (string, error) {
|
|
|
|
tpl, err := template.New(tplInfo.Path).Funcs(funcMap).Parse(tplInfo.Path)
|
|
|
|
if err != nil {
|
|
|
|
return "", fmt.Errorf("parse file path template(%s) failed, err: %v", tplInfo.Path, err)
|
|
|
|
}
|
|
|
|
filePath := bytes.NewBuffer(nil)
|
|
|
|
err = tpl.Execute(filePath, filePathRenderInfo)
|
|
|
|
if err != nil {
|
|
|
|
return "", fmt.Errorf("render file path template(%s) failed, err: %v", tplInfo.Path, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return filePath.String(), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func renderInsertKey(tplInfo *Template, data interface{}) (string, error) {
|
|
|
|
tpl, err := template.New(tplInfo.UpdateBehavior.InsertKey).Funcs(funcMap).Parse(tplInfo.UpdateBehavior.InsertKey)
|
|
|
|
if err != nil {
|
|
|
|
return "", fmt.Errorf("parse insert key template(%s) failed, err: %v", tplInfo.UpdateBehavior.InsertKey, err)
|
|
|
|
}
|
|
|
|
insertKey := bytes.NewBuffer(nil)
|
|
|
|
err = tpl.Execute(insertKey, data)
|
|
|
|
if err != nil {
|
|
|
|
return "", fmt.Errorf("render insert key template(%s) failed, err: %v", tplInfo.UpdateBehavior.InsertKey, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return insertKey.String(), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// renderImportTpl will render import template
|
|
|
|
// it will return the []string, like blow:
|
|
|
|
// ["import", alias "import", import]
|
|
|
|
// other format will be error
|
|
|
|
func renderImportTpl(tplInfo *Template, data interface{}) ([]string, error) {
|
|
|
|
var importList []string
|
|
|
|
for _, impt := range tplInfo.UpdateBehavior.ImportTpl {
|
|
|
|
tpl, err := template.New(impt).Funcs(funcMap).Parse(impt)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("parse import template(%s) failed, err: %v", impt, err)
|
|
|
|
}
|
|
|
|
imptContent := bytes.NewBuffer(nil)
|
|
|
|
err = tpl.Execute(imptContent, data)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("render import template(%s) failed, err: %v", impt, err)
|
|
|
|
}
|
|
|
|
importList = append(importList, imptContent.String())
|
|
|
|
}
|
|
|
|
var ret []string
|
|
|
|
for _, impts := range importList {
|
|
|
|
// 'import render result' may have multiple imports
|
|
|
|
if strings.Contains(impts, "\n") {
|
|
|
|
for _, impt := range strings.Split(impts, "\n") {
|
|
|
|
ret = append(ret, impt)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
ret = append(ret, impts)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// renderAppendContent used to render append content for 'update' command
|
|
|
|
func renderAppendContent(tplInfo *Template, renderInfo interface{}) (string, error) {
|
|
|
|
tpl, err := template.New(tplInfo.Path).Funcs(funcMap).Parse(tplInfo.UpdateBehavior.AppendTpl)
|
|
|
|
if err != nil {
|
|
|
|
return "", fmt.Errorf("parse append content template(%s) failed, err: %v", tplInfo.Path, err)
|
|
|
|
}
|
|
|
|
appendContent := bytes.NewBuffer(nil)
|
|
|
|
err = tpl.Execute(appendContent, renderInfo)
|
|
|
|
if err != nil {
|
|
|
|
return "", fmt.Errorf("render append content template(%s) failed, err: %v", tplInfo.Path, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return appendContent.String(), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// appendUpdateFile used to append content to file for 'update' command
|
|
|
|
func appendUpdateFile(tplInfo *Template, renderInfo interface{}, fileContent []byte) ([]byte, error) {
|
|
|
|
// render insert content
|
|
|
|
appendContent, err := renderAppendContent(tplInfo, renderInfo)
|
|
|
|
if err != nil {
|
|
|
|
return []byte(""), err
|
|
|
|
}
|
|
|
|
buf := bytes.NewBuffer(nil)
|
|
|
|
_, err = buf.Write(fileContent)
|
|
|
|
if err != nil {
|
|
|
|
return []byte(""), fmt.Errorf("write file(%s) failed, err: %v", tplInfo.Path, err)
|
|
|
|
}
|
|
|
|
// "\r\n" && "\n" has the same suffix
|
|
|
|
if !bytes.HasSuffix(buf.Bytes(), []byte("\n")) {
|
|
|
|
_, err = buf.WriteString("\n")
|
|
|
|
if err != nil {
|
|
|
|
return []byte(""), fmt.Errorf("write file(%s) line break failed, err: %v", tplInfo.Path, err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_, err = buf.WriteString(appendContent)
|
|
|
|
if err != nil {
|
|
|
|
return []byte(""), fmt.Errorf("append file(%s) failed, err: %v", tplInfo.Path, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return buf.Bytes(), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func getInsertImportContent(tplInfo *Template, renderInfo interface{}, fileContent []byte) ([][2]string, error) {
|
|
|
|
importContent, err := renderImportTpl(tplInfo, renderInfo)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
var imptSlice [][2]string
|
|
|
|
for _, impt := range importContent {
|
|
|
|
// import has to format
|
|
|
|
// 1. alias "import"
|
|
|
|
// 2. "import"
|
|
|
|
// 3. import (can not contain '"')
|
|
|
|
impt = strings.TrimSpace(impt)
|
|
|
|
if !strings.Contains(impt, "\"") { // 3. import (can not contain '"')
|
|
|
|
if bytes.Contains(fileContent, []byte(impt)) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
imptSlice = append(imptSlice, [2]string{"", impt})
|
|
|
|
} else {
|
|
|
|
if !strings.HasSuffix(impt, "\"") {
|
|
|
|
return nil, fmt.Errorf("import can not has suffix \"\"\", for file: %s", tplInfo.Path)
|
|
|
|
}
|
|
|
|
if strings.HasPrefix(impt, "\"") { // 2. "import"
|
|
|
|
if bytes.Contains(fileContent, []byte(impt[1:len(impt)-1])) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
imptSlice = append(imptSlice, [2]string{"", impt[1 : len(impt)-1]})
|
|
|
|
} else { // 3. alias "import"
|
|
|
|
idx := strings.Index(impt, "\n")
|
|
|
|
if idx == -1 {
|
|
|
|
return nil, fmt.Errorf("error import format for file: %s", tplInfo.Path)
|
|
|
|
}
|
|
|
|
if bytes.Contains(fileContent, []byte(impt[idx+1:len(impt)-1])) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
imptSlice = append(imptSlice, [2]string{impt[:idx], impt[idx+1 : len(impt)-1]})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return imptSlice, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// genLoopService used to generate files by 'service'
|
|
|
|
func (pkgGen *HttpPackageGenerator) genLoopService(tplInfo *Template, filePathRenderInfo FilePathRenderInfo, service *Service, idlPackageRenderInfo *IDLPackageRenderInfo) error {
|
|
|
|
filePath, err := renderFilePath(tplInfo, filePathRenderInfo)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
// determine if a custom file exists
|
|
|
|
exist, err := util.PathExist(filePath)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("judge file(%s) exists failed, err: %v", filePath, err)
|
|
|
|
}
|
|
|
|
if !exist { // create file
|
|
|
|
data := CustomizedFileForService{
|
|
|
|
Service: service,
|
|
|
|
FilePath: filePath,
|
|
|
|
FilePackage: util.SplitPackage(filepath.Dir(filePath), ""),
|
|
|
|
IDLPackageInfo: idlPackageRenderInfo,
|
|
|
|
}
|
|
|
|
err = pkgGen.TemplateGenerator.Generate(data, tplInfo.Path, filePath, false)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
switch tplInfo.UpdateBehavior.Type {
|
|
|
|
case Skip:
|
|
|
|
// do nothing
|
|
|
|
logs.Infof("do not update file '%s', because the update behavior is 'Unchanged'", filePath)
|
|
|
|
case Cover:
|
|
|
|
// re-generate
|
|
|
|
logs.Infof("re-generate file '%s', because the update behavior is 'Regenerate'", filePath)
|
|
|
|
data := CustomizedFileForService{
|
|
|
|
Service: service,
|
|
|
|
FilePath: filePath,
|
|
|
|
FilePackage: util.SplitPackage(filepath.Dir(filePath), ""),
|
|
|
|
IDLPackageInfo: idlPackageRenderInfo,
|
|
|
|
}
|
|
|
|
err := pkgGen.TemplateGenerator.Generate(data, tplInfo.Path, filePath, false)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
case Append: // todo: append logic need to be optimized for method
|
|
|
|
fileContent, err := ioutil.ReadFile(filePath)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
var appendContent []byte
|
|
|
|
// get insert content
|
|
|
|
if tplInfo.UpdateBehavior.AppendKey == "method" {
|
|
|
|
for _, method := range service.Methods {
|
|
|
|
data := CustomizedFileForMethod{
|
|
|
|
HttpMethod: method,
|
|
|
|
FilePath: filePath,
|
|
|
|
FilePackage: util.SplitPackage(filepath.Dir(filePath), ""),
|
|
|
|
ServiceInfo: service,
|
|
|
|
IDLPackageInfo: idlPackageRenderInfo,
|
|
|
|
}
|
|
|
|
insertKey, err := renderInsertKey(tplInfo, data)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if bytes.Contains(fileContent, []byte(insertKey)) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
imptSlice, err := getInsertImportContent(tplInfo, data, fileContent)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
// insert new import to the fileContent
|
|
|
|
for _, impt := range imptSlice {
|
|
|
|
if bytes.Contains(fileContent, []byte(impt[1])) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
fileContent, err = util.AddImportForContent(fileContent, impt[0], impt[1])
|
|
|
|
// insert import error do not influence the generated file
|
|
|
|
if err != nil {
|
|
|
|
logs.Warnf("can not add import(%s) for file(%s), err: %v\n", impt[1], filePath, err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
appendContent, err = appendUpdateFile(tplInfo, data, appendContent)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if len(tplInfo.UpdateBehavior.AppendLocation) == 0 { // default, append to end of file
|
|
|
|
buf := bytes.NewBuffer(nil)
|
|
|
|
_, err = buf.Write(fileContent)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("write file(%s) failed, err: %v", tplInfo.Path, err)
|
|
|
|
}
|
|
|
|
_, err = buf.Write(appendContent)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("append file(%s) failed, err: %v", tplInfo.Path, err)
|
|
|
|
}
|
|
|
|
logs.Infof("append content for file '%s', because the update behavior is 'Append' and appendKey is 'method'", filePath)
|
|
|
|
pkgGen.files = append(pkgGen.files, File{filePath, buf.String(), false, ""})
|
|
|
|
} else { // 'append location', append new content after 'append location'
|
|
|
|
part := bytes.Split(fileContent, []byte(tplInfo.UpdateBehavior.AppendLocation))
|
|
|
|
if len(part) == 0 {
|
|
|
|
return fmt.Errorf("can not find append location '%s' for file '%s'\n", tplInfo.UpdateBehavior.AppendLocation, filePath)
|
|
|
|
}
|
|
|
|
if len(part) != 2 {
|
|
|
|
return fmt.Errorf("do not support multiple append location '%s' for file '%s'\n", tplInfo.UpdateBehavior.AppendLocation, filePath)
|
|
|
|
}
|
|
|
|
buf := bytes.NewBuffer(nil)
|
|
|
|
err = writeBytes(buf, part[0], []byte(tplInfo.UpdateBehavior.AppendLocation), appendContent, part[1])
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("write file(%s) failed, err: %v", tplInfo.Path, err)
|
|
|
|
}
|
|
|
|
logs.Infof("append content for file '%s', because the update behavior is 'Append' and appendKey is 'method'", filePath)
|
|
|
|
pkgGen.files = append(pkgGen.files, File{filePath, buf.String(), false, ""})
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
logs.Warnf("Loop 'service' field for '%s' only append content by appendKey for 'method', so cannot append content", filePath)
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
// do nothing
|
|
|
|
logs.Warnf("unknown update behavior, do nothing")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// genLoopMethod used to generate files by 'method'
|
|
|
|
func (pkgGen *HttpPackageGenerator) genLoopMethod(tplInfo *Template, filePathRenderInfo FilePathRenderInfo, method *HttpMethod, service *Service, idlPackageRenderInfo *IDLPackageRenderInfo) error {
|
|
|
|
filePath, err := renderFilePath(tplInfo, filePathRenderInfo)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
// determine if a custom file exists
|
|
|
|
exist, err := util.PathExist(filePath)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("judge file(%s) exists failed, err: %v", filePath, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if !exist { // create file
|
|
|
|
data := CustomizedFileForMethod{
|
|
|
|
HttpMethod: method,
|
|
|
|
FilePath: filePath,
|
|
|
|
FilePackage: util.SplitPackage(filepath.Dir(filePath), ""),
|
|
|
|
ServiceInfo: service,
|
|
|
|
IDLPackageInfo: idlPackageRenderInfo,
|
|
|
|
}
|
|
|
|
err := pkgGen.TemplateGenerator.Generate(data, tplInfo.Path, filePath, false)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
switch tplInfo.UpdateBehavior.Type {
|
|
|
|
case Skip:
|
|
|
|
// do nothing
|
|
|
|
logs.Infof("do not update file '%s', because the update behavior is 'Unchanged'", filePath)
|
|
|
|
case Cover:
|
|
|
|
// re-generate
|
|
|
|
logs.Infof("re-generate file '%s', because the update behavior is 'Regenerate'", filePath)
|
|
|
|
data := CustomizedFileForMethod{
|
|
|
|
HttpMethod: method,
|
|
|
|
FilePath: filePath,
|
|
|
|
FilePackage: util.SplitPackage(filepath.Dir(filePath), ""),
|
|
|
|
ServiceInfo: service,
|
|
|
|
IDLPackageInfo: idlPackageRenderInfo,
|
|
|
|
}
|
|
|
|
err := pkgGen.TemplateGenerator.Generate(data, tplInfo.Path, filePath, false)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
case Append:
|
|
|
|
// for loop method, no need to append something; so do nothing
|
|
|
|
logs.Warnf("do not append content for file '%s', because the update behavior is 'Append' and loop 'method' have no need to append content", filePath)
|
|
|
|
default:
|
|
|
|
// do nothing
|
|
|
|
logs.Warnf("unknown update behavior, do nothing")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// genSingleCustomizedFile used to generate file by 'master IDL'
|
|
|
|
func (pkgGen *HttpPackageGenerator) genSingleCustomizedFile(tplInfo *Template, filePathRenderInfo FilePathRenderInfo, idlPackageRenderInfo IDLPackageRenderInfo) error {
|
|
|
|
// generate file single
|
|
|
|
filePath, err := renderFilePath(tplInfo, filePathRenderInfo)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
// determine if a custom file exists
|
|
|
|
exist, err := util.PathExist(filePath)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("judge file(%s) exists failed, err: %v", filePath, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if !exist { // create file
|
|
|
|
data := CustomizedFileForIDL{
|
|
|
|
IDLPackageRenderInfo: &idlPackageRenderInfo,
|
|
|
|
FilePath: filePath,
|
|
|
|
FilePackage: util.SplitPackage(filepath.Dir(filePath), ""),
|
|
|
|
}
|
|
|
|
err := pkgGen.TemplateGenerator.Generate(data, tplInfo.Path, filePath, false)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
switch tplInfo.UpdateBehavior.Type {
|
|
|
|
case Skip:
|
|
|
|
// do nothing
|
|
|
|
logs.Infof("do not update file '%s', because the update behavior is 'Unchanged'", filePath)
|
|
|
|
case Cover:
|
|
|
|
// re-generate
|
|
|
|
logs.Infof("re-generate file '%s', because the update behavior is 'Regenerate'", filePath)
|
|
|
|
data := CustomizedFileForIDL{
|
|
|
|
IDLPackageRenderInfo: &idlPackageRenderInfo,
|
|
|
|
FilePath: filePath,
|
|
|
|
FilePackage: util.SplitPackage(filepath.Dir(filePath), ""),
|
|
|
|
}
|
|
|
|
err := pkgGen.TemplateGenerator.Generate(data, tplInfo.Path, filePath, false)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
case Append: // todo: append logic need to be optimized for single file
|
|
|
|
fileContent, err := ioutil.ReadFile(filePath)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if tplInfo.UpdateBehavior.AppendKey == "method" {
|
|
|
|
var appendContent []byte
|
|
|
|
for _, service := range idlPackageRenderInfo.ServiceInfos.Services {
|
|
|
|
for _, method := range service.Methods {
|
|
|
|
data := CustomizedFileForMethod{
|
|
|
|
HttpMethod: method,
|
|
|
|
FilePath: filePath,
|
|
|
|
FilePackage: util.SplitPackage(filepath.Dir(filePath), ""),
|
|
|
|
ServiceInfo: service,
|
|
|
|
IDLPackageInfo: &idlPackageRenderInfo,
|
|
|
|
}
|
|
|
|
insertKey, err := renderInsertKey(tplInfo, data)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if bytes.Contains(fileContent, []byte(insertKey)) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
imptSlice, err := getInsertImportContent(tplInfo, data, fileContent)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
for _, impt := range imptSlice {
|
|
|
|
if bytes.Contains(fileContent, []byte(impt[1])) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
fileContent, err = util.AddImportForContent(fileContent, impt[0], impt[1])
|
|
|
|
if err != nil {
|
|
|
|
logs.Warnf("can not add import(%s) for file(%s)\n", impt[1], filePath)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
appendContent, err = appendUpdateFile(tplInfo, data, appendContent)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if len(tplInfo.UpdateBehavior.AppendLocation) == 0 { // default, append to end of file
|
|
|
|
buf := bytes.NewBuffer(nil)
|
|
|
|
_, err = buf.Write(fileContent)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("write file(%s) failed, err: %v", tplInfo.Path, err)
|
|
|
|
}
|
|
|
|
_, err = buf.Write(appendContent)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("append file(%s) failed, err: %v", tplInfo.Path, err)
|
|
|
|
}
|
|
|
|
logs.Infof("append content for file '%s', because the update behavior is 'Append' and appendKey is 'method'", filePath)
|
|
|
|
pkgGen.files = append(pkgGen.files, File{filePath, buf.String(), false, ""})
|
|
|
|
} else { // 'append location', append new content after 'append location'
|
|
|
|
part := bytes.Split(fileContent, []byte(tplInfo.UpdateBehavior.AppendLocation))
|
|
|
|
if len(part) == 0 {
|
|
|
|
return fmt.Errorf("can not find append location '%s' for file '%s'\n", tplInfo.UpdateBehavior.AppendLocation, filePath)
|
|
|
|
}
|
|
|
|
if len(part) != 2 {
|
|
|
|
return fmt.Errorf("do not support multiple append location '%s' for file '%s'\n", tplInfo.UpdateBehavior.AppendLocation, filePath)
|
|
|
|
}
|
|
|
|
buf := bytes.NewBuffer(nil)
|
|
|
|
err = writeBytes(buf, part[0], []byte(tplInfo.UpdateBehavior.AppendLocation), appendContent, part[1])
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("write file(%s) failed, err: %v", tplInfo.Path, err)
|
|
|
|
}
|
|
|
|
logs.Infof("append content for file '%s', because the update behavior is 'Append' and appendKey is 'method'", filePath)
|
|
|
|
pkgGen.files = append(pkgGen.files, File{filePath, buf.String(), false, ""})
|
|
|
|
}
|
|
|
|
} else if tplInfo.UpdateBehavior.AppendKey == "service" {
|
|
|
|
var appendContent []byte
|
|
|
|
for _, service := range idlPackageRenderInfo.ServiceInfos.Services {
|
|
|
|
data := CustomizedFileForService{
|
|
|
|
Service: service,
|
|
|
|
FilePath: filePath,
|
|
|
|
FilePackage: util.SplitPackage(filepath.Dir(filePath), ""),
|
|
|
|
IDLPackageInfo: &idlPackageRenderInfo,
|
|
|
|
}
|
|
|
|
insertKey, err := renderInsertKey(tplInfo, data)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if bytes.Contains(fileContent, []byte(insertKey)) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
imptSlice, err := getInsertImportContent(tplInfo, data, fileContent)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
for _, impt := range imptSlice {
|
|
|
|
if bytes.Contains(fileContent, []byte(impt[1])) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
fileContent, err = util.AddImportForContent(fileContent, impt[0], impt[1])
|
|
|
|
if err != nil {
|
|
|
|
logs.Warnf("can not add import(%s) for file(%s)\n", impt[1], filePath)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
appendContent, err = appendUpdateFile(tplInfo, data, appendContent)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if len(tplInfo.UpdateBehavior.AppendLocation) == 0 { // default, append to end of file
|
|
|
|
buf := bytes.NewBuffer(nil)
|
|
|
|
_, err = buf.Write(fileContent)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("write file(%s) failed, err: %v", tplInfo.Path, err)
|
|
|
|
}
|
|
|
|
_, err = buf.Write(appendContent)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("append file(%s) failed, err: %v", tplInfo.Path, err)
|
|
|
|
}
|
|
|
|
logs.Infof("append content for file '%s', because the update behavior is 'Append' and appendKey is 'service'", filePath)
|
|
|
|
pkgGen.files = append(pkgGen.files, File{filePath, buf.String(), false, ""})
|
|
|
|
} else { // 'append location', append new content after 'append location'
|
|
|
|
part := bytes.Split(fileContent, []byte(tplInfo.UpdateBehavior.AppendLocation))
|
|
|
|
if len(part) == 0 {
|
|
|
|
return fmt.Errorf("can not find append location '%s' for file '%s'\n", tplInfo.UpdateBehavior.AppendLocation, filePath)
|
|
|
|
}
|
|
|
|
if len(part) != 2 {
|
|
|
|
return fmt.Errorf("do not support multiple append location '%s' for file '%s'\n", tplInfo.UpdateBehavior.AppendLocation, filePath)
|
|
|
|
}
|
|
|
|
buf := bytes.NewBuffer(nil)
|
|
|
|
err = writeBytes(buf, part[0], []byte(tplInfo.UpdateBehavior.AppendLocation), appendContent, part[1])
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("write file(%s) failed, err: %v", tplInfo.Path, err)
|
|
|
|
}
|
|
|
|
logs.Infof("append content for file '%s', because the update behavior is 'Append' and appendKey is 'service'", filePath)
|
|
|
|
pkgGen.files = append(pkgGen.files, File{filePath, buf.String(), false, ""})
|
|
|
|
}
|
|
|
|
} else { // add append content to the file directly
|
|
|
|
data := CustomizedFileForIDL{
|
|
|
|
IDLPackageRenderInfo: &idlPackageRenderInfo,
|
|
|
|
FilePath: filePath,
|
|
|
|
FilePackage: util.SplitPackage(filepath.Dir(filePath), ""),
|
|
|
|
}
|
|
|
|
file, err := appendUpdateFile(tplInfo, data, fileContent)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
pkgGen.files = append(pkgGen.files, File{filePath, string(file), false, ""})
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
// do nothing
|
|
|
|
logs.Warnf("unknown update behavior, do nothing")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func writeBytes(buf *bytes.Buffer, bytes ...[]byte) error {
|
|
|
|
for _, b := range bytes {
|
|
|
|
_, err := buf.Write(b)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|