github.com/alkemics/goflow@v0.2.1/gfutil/gfgo/playground.go (about) 1 package gfgo 2 3 import ( 4 "bytes" 5 "errors" 6 "fmt" 7 "io" 8 "sort" 9 "strings" 10 "text/template" 11 12 "golang.org/x/tools/go/packages" 13 14 "github.com/alkemics/goflow" 15 ) 16 17 func GeneratePlayground(w io.Writer, pkgPath string, nodes []Node) error { 18 pkgs, err := packages.Load(&packages.Config{ 19 Mode: packages.NeedName, 20 }, pkgPath) 21 if err != nil { 22 return err 23 } 24 25 var pkg string 26 for _, p := range pkgs { 27 if p.PkgPath == pkgPath { 28 pkg = p.Name 29 } 30 } 31 32 if pkg == "" { 33 return errors.New("could not extract package name") 34 } 35 36 ns := make([]Node, 0, len(nodes)) 37 imports := make([]goflow.Import, 0) 38 for _, n := range nodes { 39 if n.Method != "Run" { 40 continue 41 } 42 43 imports = append(imports, n.Imports...) 44 ns = append(ns, n) 45 } 46 47 sort.SliceStable(ns, func(i, j int) bool { 48 lhf := fmt.Sprintf("%s.%s", ns[i].Pkg, ns[i].Typ) 49 rhf := fmt.Sprintf("%s.%s", ns[j].Pkg, ns[j].Typ) 50 return lhf <= rhf 51 }) 52 53 imports = append( 54 imports, 55 goflow.Import{Pkg: "errors", Dir: "errors"}, 56 goflow.Import{Pkg: "fmt", Dir: "fmt"}, 57 goflow.Import{Pkg: "http", Dir: "net/http"}, 58 goflow.Import{Pkg: "json", Dir: "encoding/json"}, 59 goflow.Import{Pkg: "strings", Dir: "strings"}, 60 ) 61 62 dependencyMap := make(map[string]string) 63 for _, node := range nodes { 64 for _, dep := range node.Dependencies { 65 dependencyMap[dep.Name] = dep.Type 66 } 67 } 68 69 dependencies := make([]goflow.Field, 0, len(dependencyMap)) 70 for name, typ := range dependencyMap { 71 dep := goflow.Field{ 72 Name: name, 73 Type: typ, 74 } 75 dependencies = append(dependencies, dep) 76 } 77 78 sort.SliceStable(dependencies, func(i, j int) bool { 79 return dependencies[i].Name <= dependencies[j].Name 80 }) 81 82 tmplData := struct { 83 Pkg string 84 PkgPath string 85 Imports []goflow.Import 86 Dependencies []goflow.Field 87 Nodes []Node 88 }{ 89 Pkg: pkg, 90 PkgPath: pkgPath, 91 Imports: imports, 92 Dependencies: dependencies, 93 Nodes: ns, 94 } 95 96 buf := bytes.Buffer{} 97 if err := tmpl.Execute(&buf, tmplData); err != nil { 98 return err 99 } 100 101 _, err = w.Write(buf.Bytes()) 102 return err 103 } 104 105 var tmpl = template.Must( 106 template. 107 New("playground"). 108 Funcs(template.FuncMap{ 109 "JSONTag": func(s string) string { 110 return fmt.Sprintf("`json:\"%s\"`", s) 111 }, 112 "NameAndTypes": func(fields []goflow.Field) string { 113 s := make([]string, len(fields)) 114 for i, f := range fields { 115 s[i] = fmt.Sprintf("%s %s", f.Name, f.Type) 116 } 117 return strings.Join(s, ", ") 118 }, 119 "Names": func(fields []goflow.Field) string { 120 s := make([]string, len(fields)) 121 for i, f := range fields { 122 s[i] = f.Name 123 } 124 return strings.Join(s, ", ") 125 }, 126 "Public": func(s string) string { 127 if s == "" { 128 return "" 129 } 130 131 if len(s) == 1 { 132 return strings.ToUpper(s) 133 } 134 135 return strings.ToUpper(string(s[0])) + s[1:] 136 }, 137 }). 138 Parse(` 139 // Code generated by goflow DO NOT EDIT. 140 141 // +build !codeanalysis 142 143 {{ $pkg := .Pkg -}} 144 145 package {{ .Pkg }} 146 147 import ( 148 {{ range .Imports -}} 149 {{ .Pkg }} "{{ .Dir }}" 150 {{ end -}} 151 ) 152 153 type Playground struct{ 154 {{ range .Dependencies -}} 155 {{ .Name }} {{ .Type }} 156 {{ end -}} 157 } 158 159 func NewPlayground({{ NameAndTypes .Dependencies }}) Playground { 160 return Playground{ 161 {{ range .Dependencies -}} 162 {{ .Name }}: {{ .Name }}, 163 {{ end -}} 164 } 165 } 166 167 func (p Playground) Run(ctx context.Context, name string, jsonInputs *json.RawMessage) (jsonOutputs *json.RawMessage, err error) { 168 nodes := map[string]func(context.Context, *json.RawMessage) (*json.RawMessage, error) { 169 {{ range .Nodes -}} 170 "{{ .PkgPath }}.{{ .Typ }}": func(ctx context.Context, in *json.RawMessage) (*json.RawMessage, error) { 171 if in == nil { 172 return nil, nil 173 } 174 175 var params struct{ 176 {{ range .Inputs -}} 177 {{ if ne .Type "context.Context" -}} 178 {{ Public .Name }} {{ .Type }} {{ JSONTag .Name }} 179 {{ end -}} 180 {{ end -}} 181 } 182 if err := json.Unmarshal(*in, ¶ms); err != nil { 183 return nil, err 184 } 185 186 {{ if eq .Pkg $pkg -}} 187 g := New{{ .Typ }}({{ range .Dependencies }}p.{{ .Name }},{{ end }}) 188 {{ else -}} 189 g := {{ .Pkg }}.New{{ .Typ }}({{ range .Dependencies }}p.{{ .Name }},{{ end }}) 190 {{ end -}} 191 {{ Names .Outputs }} := g.Run( 192 {{ range .Inputs -}} 193 {{ if eq .Type "context.Context" -}} 194 ctx, 195 {{ else -}} 196 params.{{ Public .Name }}, 197 {{ end -}} 198 {{ end -}} 199 ) 200 201 res := map[string]interface{}{ 202 {{ range .Outputs -}} 203 "{{ .Name }}": {{ .Name }}, 204 {{ end -}} 205 } 206 207 var out json.RawMessage 208 if b, err := json.Marshal(res); err != nil { 209 return nil, err 210 } else { 211 out = json.RawMessage(b) 212 } 213 214 return &out, nil 215 }, 216 {{ end -}} 217 } 218 219 run := nodes[name] 220 if run == nil { 221 return nil, errors.New("not found") 222 } 223 return run(ctx, jsonInputs) 224 } 225 226 func (p Playground) List() (map[string]interface{}) { 227 return map[string]interface{}{ 228 {{ range .Nodes -}} 229 "{{ .PkgPath }}.{{ .Typ }}": 230 {{- if eq .Pkg $pkg -}} 231 New{{ .Typ }}({{ range .Dependencies }}p.{{ .Name }},{{ end }}), 232 {{ else -}} 233 {{ .Pkg }}.New{{ .Typ }}({{ range .Dependencies }}p.{{ .Name }},{{ end }}), 234 {{ end -}} 235 {{ end -}} 236 } 237 } 238 239 func (p Playground) Get(name string) (interface{}) { 240 nodes := p.List() 241 return nodes[name] 242 } 243 `))