| 
									
										
										
										
											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 ( | 
					
						
							|  |  |  | 	"encoding/json" | 
					
						
							|  |  |  | 	"errors" | 
					
						
							|  |  |  | 	"fmt" | 
					
						
							|  |  |  | 	"io/ioutil" | 
					
						
							|  |  |  | 	"path/filepath" | 
					
						
							|  |  |  | 	"reflect" | 
					
						
							|  |  |  | 	"strings" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	"gopkg.in/yaml.v2" | 
					
						
							| 
									
										
										
										
											2024-04-30 20:22:44 +08:00
										 |  |  | 	"gr_hz/meta" | 
					
						
							|  |  |  | 	"gr_hz/util" | 
					
						
							| 
									
										
										
										
											2024-04-30 19:30:09 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Layout contains the basic information of idl
 | 
					
						
							|  |  |  | type Layout struct { | 
					
						
							|  |  |  | 	OutDir          string | 
					
						
							|  |  |  | 	GoModule        string | 
					
						
							|  |  |  | 	ServiceName     string | 
					
						
							|  |  |  | 	UseApacheThrift bool | 
					
						
							|  |  |  | 	HasIdl          bool | 
					
						
							|  |  |  | 	NeedGoMod       bool | 
					
						
							|  |  |  | 	ModelDir        string | 
					
						
							|  |  |  | 	HandlerDir      string | 
					
						
							|  |  |  | 	RouterDir       string | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // LayoutGenerator contains the information generated by generating the layout template
 | 
					
						
							|  |  |  | type LayoutGenerator struct { | 
					
						
							|  |  |  | 	ConfigPath string | 
					
						
							|  |  |  | 	TemplateGenerator | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var ( | 
					
						
							|  |  |  | 	layoutConfig  = defaultLayoutConfig | 
					
						
							|  |  |  | 	packageConfig = defaultPkgConfig | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func SetDefaultTemplateConfig() { | 
					
						
							|  |  |  | 	layoutConfig = defaultLayoutConfig | 
					
						
							|  |  |  | 	packageConfig = defaultPkgConfig | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (lg *LayoutGenerator) Init() error { | 
					
						
							|  |  |  | 	config := layoutConfig | 
					
						
							|  |  |  | 	// unmarshal from user-defined config file if it exists
 | 
					
						
							|  |  |  | 	if lg.ConfigPath != "" { | 
					
						
							|  |  |  | 		cdata, err := ioutil.ReadFile(lg.ConfigPath) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return fmt.Errorf("read layout config from  %s failed, err: %v", lg.ConfigPath, err.Error()) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		config = TemplateConfig{} | 
					
						
							|  |  |  | 		if err = yaml.Unmarshal(cdata, &config); err != nil { | 
					
						
							|  |  |  | 			return fmt.Errorf("unmarshal layout config failed, err: %v", err.Error()) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if reflect.DeepEqual(config, TemplateConfig{}) { | 
					
						
							|  |  |  | 		return errors.New("empty config") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	lg.Config = &config | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return lg.TemplateGenerator.Init() | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // checkInited initialize template definition
 | 
					
						
							|  |  |  | func (lg *LayoutGenerator) checkInited() error { | 
					
						
							|  |  |  | 	if lg.tpls == nil || lg.dirs == nil { | 
					
						
							|  |  |  | 		if err := lg.Init(); err != nil { | 
					
						
							|  |  |  | 			return fmt.Errorf("init layout config failed, err: %v", err.Error()) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (lg *LayoutGenerator) Generate(data map[string]interface{}) error { | 
					
						
							|  |  |  | 	if err := lg.checkInited(); err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return lg.TemplateGenerator.Generate(data, "", "", false) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (lg *LayoutGenerator) GenerateByService(service Layout) error { | 
					
						
							|  |  |  | 	if err := lg.checkInited(); err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if len(service.HandlerDir) != 0 { | 
					
						
							|  |  |  | 		// override the default "biz/handler/ping.go" to "HANDLER_DIR/ping.go"
 | 
					
						
							|  |  |  | 		defaultPingDir := defaultHandlerDir + sp + "ping.go" | 
					
						
							|  |  |  | 		if tpl, exist := lg.tpls[defaultPingDir]; exist { | 
					
						
							|  |  |  | 			delete(lg.tpls, defaultPingDir) | 
					
						
							|  |  |  | 			newPingDir := filepath.Clean(service.HandlerDir + sp + "ping.go") | 
					
						
							|  |  |  | 			lg.tpls[newPingDir] = tpl | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if len(service.RouterDir) != 0 { | 
					
						
							|  |  |  | 		defaultRegisterDir := defaultRouterDir + sp + registerTplName | 
					
						
							|  |  |  | 		if tpl, exist := lg.tpls[defaultRegisterDir]; exist { | 
					
						
							|  |  |  | 			delete(lg.tpls, defaultRegisterDir) | 
					
						
							|  |  |  | 			newRegisterDir := filepath.Clean(service.RouterDir + sp + registerTplName) | 
					
						
							|  |  |  | 			lg.tpls[newRegisterDir] = tpl | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if !service.NeedGoMod { | 
					
						
							|  |  |  | 		gomodFile := "go.mod" | 
					
						
							|  |  |  | 		if _, exist := lg.tpls[gomodFile]; exist { | 
					
						
							|  |  |  | 			delete(lg.tpls, gomodFile) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if util.IsWindows() { | 
					
						
							|  |  |  | 		buildSh := "build.sh" | 
					
						
							|  |  |  | 		bootstrapSh := defaultScriptDir + sp + "bootstrap.sh" | 
					
						
							|  |  |  | 		if _, exist := lg.tpls[buildSh]; exist { | 
					
						
							|  |  |  | 			delete(lg.tpls, buildSh) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if _, exist := lg.tpls[bootstrapSh]; exist { | 
					
						
							|  |  |  | 			delete(lg.tpls, bootstrapSh) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	sd, err := serviceToLayoutData(service) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	rd, err := serviceToRouterData(service) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if service.HasIdl { | 
					
						
							|  |  |  | 		for k := range lg.tpls { | 
					
						
							|  |  |  | 			if strings.Contains(k, registerTplName) { | 
					
						
							|  |  |  | 				delete(lg.tpls, k) | 
					
						
							|  |  |  | 				break | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	data := map[string]interface{}{ | 
					
						
							|  |  |  | 		"*":                                    sd, | 
					
						
							|  |  |  | 		layoutConfig.Layouts[routerIndex].Path: rd, // router.go
 | 
					
						
							|  |  |  | 		layoutConfig.Layouts[routerGenIndex].Path: rd, // router_gen.go
 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return lg.Generate(data) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // serviceToLayoutData stores go mod, serviceName, UseApacheThrift mapping
 | 
					
						
							|  |  |  | func serviceToLayoutData(service Layout) (map[string]interface{}, error) { | 
					
						
							|  |  |  | 	goMod := service.GoModule | 
					
						
							|  |  |  | 	if goMod == "" { | 
					
						
							|  |  |  | 		return nil, errors.New("please specify go-module") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	handlerPkg := filepath.Base(defaultHandlerDir) | 
					
						
							|  |  |  | 	if len(service.HandlerDir) != 0 { | 
					
						
							|  |  |  | 		handlerPkg = filepath.Base(service.HandlerDir) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	routerPkg := filepath.Base(defaultRouterDir) | 
					
						
							|  |  |  | 	if len(service.RouterDir) != 0 { | 
					
						
							|  |  |  | 		routerPkg = filepath.Base(service.RouterDir) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	serviceName := service.ServiceName | 
					
						
							|  |  |  | 	if len(serviceName) == 0 { | 
					
						
							|  |  |  | 		serviceName = meta.DefaultServiceName | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return map[string]interface{}{ | 
					
						
							|  |  |  | 		"GoModule":        goMod, | 
					
						
							|  |  |  | 		"ServiceName":     serviceName, | 
					
						
							|  |  |  | 		"UseApacheThrift": service.UseApacheThrift, | 
					
						
							|  |  |  | 		"HandlerPkg":      handlerPkg, | 
					
						
							|  |  |  | 		"RouterPkg":       routerPkg, | 
					
						
							|  |  |  | 	}, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // serviceToRouterData stores the registers function, router import path, handler import path
 | 
					
						
							|  |  |  | func serviceToRouterData(service Layout) (map[string]interface{}, error) { | 
					
						
							|  |  |  | 	routerDir := sp + defaultRouterDir | 
					
						
							|  |  |  | 	handlerDir := sp + defaultHandlerDir | 
					
						
							|  |  |  | 	if len(service.RouterDir) != 0 { | 
					
						
							|  |  |  | 		routerDir = sp + service.RouterDir | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if len(service.HandlerDir) != 0 { | 
					
						
							|  |  |  | 		handlerDir = sp + service.HandlerDir | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return map[string]interface{}{ | 
					
						
							|  |  |  | 		"Registers":      []string{}, | 
					
						
							|  |  |  | 		"RouterPkgPath":  service.GoModule + util.PathToImport(routerDir, ""), | 
					
						
							|  |  |  | 		"HandlerPkgPath": service.GoModule + util.PathToImport(handlerDir, ""), | 
					
						
							|  |  |  | 	}, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (lg *LayoutGenerator) GenerateByConfig(configPath string) error { | 
					
						
							|  |  |  | 	if err := lg.checkInited(); err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	buf, err := ioutil.ReadFile(configPath) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return fmt.Errorf("read data file '%s' failed, err: %v", configPath, err.Error()) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	var data map[string]interface{} | 
					
						
							|  |  |  | 	if err := json.Unmarshal(buf, &data); err != nil { | 
					
						
							|  |  |  | 		return fmt.Errorf("unmarshal json data failed, err: %v", err.Error()) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return lg.Generate(data) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (lg *LayoutGenerator) Degenerate() error { | 
					
						
							|  |  |  | 	return lg.TemplateGenerator.Degenerate() | 
					
						
							|  |  |  | } |