1 Star 0 Fork 2

安易科技(北京)有限公司/chameleon

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
template.go 7.80 KB
一键复制 编辑 原始数据 按行查看 历史
Derek Ray 提交于 2025-03-21 18:49 +08:00 . feat(template): update template sources
package template
import (
"context"
"fmt"
"gitee.com/anesec/chameleon/pkg/template/expr"
"gopkg.in/yaml.v3"
"io"
"net/http"
"path/filepath"
"strings"
)
type Template struct {
Name string `yaml:"template"`
Brief string `yaml:"brief"`
Author string `yaml:"author"`
Category string `yaml:"category"`
Annotations map[string]string `yaml:"annotations"`
Service *Service `yaml:"service"`
Frameworks []*Framework `yaml:"frameworks"`
}
func (template *Template) compile(reader io.Reader) error {
if err := yaml.NewDecoder(reader).Decode(template); err != nil {
return err
}
if template.Service != nil {
template.Service.manifest = template
return template.Service.compile()
} else if template.Frameworks != nil {
for _, framework := range template.Frameworks {
framework.manifest = template
if err := framework.compile(); err != nil {
return err
}
}
return nil
}
return fmt.Errorf("invalid template")
}
func (template *Template) eval(ctx context.Context, source expr.Source, kind Kind) bool {
if (kind == KindApplication || kind == KindUnknown) && template.Service != nil {
return template.Service.eval(ctx, source)
} else if (kind == KindFramework || kind == KindUnknown) && template.Frameworks != nil {
for _, framework := range template.Frameworks {
framework.eval(ctx, source)
}
}
return false
}
type templates []*Template
func (t templates) Len() int {
return len(t)
}
func (t templates) Less(i, j int) bool {
if t[i].Category == "ai" {
return true
} else if t[j].Category == "ai" {
return false
}
return t[i].Name < t[j].Name
}
func (t templates) Swap(i, j int) {
t[i], t[j] = t[j], t[i]
}
type httpRequest struct {
Urls []string `yaml:"urls"`
Method string `yaml:"method"`
Code int `yaml:"code"`
LimitBytes int `yaml:"limit_bytes"`
}
type Metadata struct {
Name string `yaml:"name"`
Value string `yaml:"value"`
Expr string `yaml:"expr"`
Exec string `yaml:"exec"`
Request *httpRequest `yaml:"http"`
Defaults []string `yaml:"defaults"` // defaults absolute file paths
Bases []string `yaml:"bases"` // defaults absolute base file paths, without filename
Files []string `yaml:"files"` // file paths, related to Bases or captured config path
appName string
expr expr.Expr
}
func (meta *Metadata) compile(app string) (err error) {
if _, ok := builtinAppConfigParsers[strings.ToLower(app)]; ok {
meta.appName = strings.ToLower(app)
}
if meta.Request != nil {
if len(meta.Request.Urls) == 0 {
return fmt.Errorf("invalid http request, no urls")
}
if meta.Request.Method != "" {
meta.Request.Method = strings.ToUpper(meta.Request.Method)
} else {
meta.Request.Method = http.MethodGet
}
if meta.Request.Code == 0 {
meta.Request.Code = http.StatusOK
}
switch meta.Request.Method {
case http.MethodGet, http.MethodPost, http.MethodPut, http.MethodHead:
default:
return fmt.Errorf("unsupported http method: %s", meta.Request.Method)
}
}
if meta.Expr != "" {
meta.expr, err = expr.Compile(meta.Expr)
}
return
}
func (meta *Metadata) eval(ctx context.Context, source expr.Source) bool {
if meta.Exec != "" {
cmd := &commandSourceWrapper{Source: source, exec: meta.Exec}
defer cmd.finalize()
source = cmd
}
if meta.Request != nil && len(meta.Request.Urls) > 0 {
source = &httpSourceWrapper{Source: source, request: meta.Request}
}
if meta.expr != nil {
if meta.expr.Eval(ctx, source) {
return true
}
} else if meta.Exec != "" {
value, ok := source.Provide("exec.output").(string)
if !ok || value == "" {
return false
}
source.Capture(meta.Name, value)
return true
} else if meta.Request != nil {
value, ok := source.Provide("http.body").(string)
if !ok || value == "" {
return false
}
source.Capture(meta.Name, value)
return true
}
if meta.Value != "" {
source.Capture(meta.Name, meta.Value)
return true
}
for i := range meta.Defaults {
source.Capture(meta.Name, meta.Defaults[i])
return true
}
if len(meta.Bases) > 0 && len(meta.Files) > 0 {
for _, basis := range meta.Bases {
for _, file := range meta.Files {
source.Capture(meta.Name, filepath.Join(basis, file))
}
}
return true
}
return false
}
type WebApp struct {
Name string `yaml:"name"`
Expr string `yaml:"expr"`
Exec string `yaml:"exec"`
Version *Metadata `yaml:"version"`
expr expr.Expr
}
func (webapp *WebApp) compile() (err error) {
if webapp.expr, err = expr.Compile(webapp.Expr); err != nil {
return
}
if webapp.Version != nil {
err = webapp.Version.compile("")
}
return
}
func (webapp *WebApp) eval(ctx context.Context, source expr.Source) bool {
if webapp.expr == nil {
return false
}
source = &webappSourceWrapper{Source: source}
if webapp.Exec != "" {
cmd := &commandSourceWrapper{Source: source, exec: webapp.Exec}
defer cmd.finalize()
source = cmd
}
if !webapp.expr.Eval(ctx, source) {
return false
}
source.Capture("webapp.name", webapp.Name)
if webapp.Version != nil {
webapp.Version.eval(ctx, source)
}
return true
}
type Service struct {
Expr string `yaml:"expr"`
Exec string `yaml:"exec"`
Keyword string `yaml:"keyword"`
Metadata []*Metadata `yaml:"metadata"`
Web []*WebApp `yaml:"webapp"`
manifest *Template
expr expr.Expr
}
func (svc *Service) compile() (err error) {
if svc.expr, err = expr.Compile(svc.Expr); err != nil {
return
}
for index := range svc.Metadata {
if err = svc.Metadata[index].compile(svc.manifest.Name); err != nil {
return
}
}
for _, webapp := range svc.Web {
if err = webapp.compile(); err != nil {
return
}
}
return
}
func (svc *Service) eval(ctx context.Context, source expr.Source) bool {
if svc.expr == nil {
return false
}
wrapper := &serviceSourceWrapper{Source: source, keyword: svc.Keyword}
source = wrapper
if svc.Exec != "" && strings.Index(svc.Expr, "exec.output") > 0 {
cmd := &commandSourceWrapper{Source: source, exec: svc.Exec}
defer cmd.finalize()
if !svc.expr.Eval(ctx, cmd) {
return false
}
} else if !svc.expr.Eval(ctx, source) {
return false
}
source.Capture("service.name", svc.manifest.Name)
source.Capture("service.category", svc.manifest.Category)
var config *configSourceWrapper
defer func() {
if config != nil {
config.finalize()
}
}()
for _, metadata := range svc.Metadata {
// skip version capture if already captured by previous expression
if metadata.Name == "version" && wrapper.captured("service.version") {
continue
}
if metadata.Name == "config" && config == nil {
config = &configSourceWrapper{Source: source, appName: metadata.appName, bases: metadata.Bases, files: metadata.Files}
source = config
}
metadata.eval(ctx, source)
}
for _, webapp := range svc.Web {
webapp.eval(ctx, source)
}
return true
}
type Framework struct {
Name string `yaml:"name"`
Maintainer string `yaml:"maintainer"`
License string `yaml:"license"`
Category string `yaml:"category"`
Expr string `yaml:"expr"`
manifest *Template
expr expr.Expr
}
func (framework *Framework) compile() (err error) {
if framework.Category == "" {
framework.Category = framework.manifest.Category
}
framework.expr, err = expr.Compile(framework.Expr, expr.CaptureValue("framework.version"))
return
}
func (framework *Framework) eval(ctx context.Context, source expr.Source) bool {
source = &frameworkSourceWrapper{Source: source}
if framework.expr == nil || !framework.expr.Eval(ctx, source) {
return false
}
source.Capture("framework.name", framework.Name)
source.Capture("framework.category", framework.manifest.Category)
source.Capture("framework.maintainer", framework.Maintainer)
source.Capture("framework.license", framework.License)
return true
}
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
Go
1
https://gitee.com/anesec/chameleon.git
git@gitee.com:anesec/chameleon.git
anesec
chameleon
chameleon
205da4a0ed50

搜索帮助