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, &params); 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  `))