go-hep.org/x/hep@v0.38.1/fwk/job/gocodec.go (about)

     1  // Copyright ©2017 The go-hep Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package job
     6  
     7  import (
     8  	"bytes"
     9  	"fmt"
    10  	"go/format"
    11  	"io"
    12  	"os"
    13  	"reflect"
    14  	"strings"
    15  )
    16  
    17  // NewGoEncoder returns a new encoder that writes to w
    18  func NewGoEncoder(w io.Writer) *GoEncoder {
    19  	if w == nil {
    20  		w = os.Stdout
    21  	}
    22  	return &GoEncoder{
    23  		w:   w,
    24  		buf: new(bytes.Buffer),
    25  	}
    26  }
    27  
    28  // A GoEncoder writes a go representation to an output stream
    29  type GoEncoder struct {
    30  	w    io.Writer
    31  	buf  *bytes.Buffer
    32  	init bool
    33  }
    34  
    35  // Encode encodes data into the underlying io.Writer
    36  func (enc *GoEncoder) Encode(data any) error {
    37  	var err error
    38  	stmts, ok := data.([]Stmt)
    39  	if !ok {
    40  		return fmt.Errorf("fwk/job: expected a []job.Stmt as input. got %T", data)
    41  	}
    42  
    43  	err = enc.encode(stmts)
    44  	if err != nil {
    45  		return err
    46  	}
    47  
    48  	buf, err := format.Source(enc.buf.Bytes())
    49  	if err != nil {
    50  		return err
    51  	}
    52  
    53  	_, err = enc.w.Write(buf)
    54  	if err != nil {
    55  		return err
    56  	}
    57  
    58  	return err
    59  }
    60  
    61  func (enc *GoEncoder) encode(stmts []Stmt) error {
    62  	var err error
    63  	if !enc.init {
    64  		fmt.Fprintf(enc.buf, `// automatically generated by go-hep.org/x/hep/fwk/job.
    65  // do NOT edit!
    66  
    67  package main
    68  
    69  `,
    70  		)
    71  
    72  		dpkgs := make(map[string]struct{}) // dash-import packages
    73  		ipkgs := make(map[string]struct{}) // import packages
    74  		for _, stmt := range stmts {
    75  			switch stmt.Type {
    76  			case StmtNewApp, StmtCreate:
    77  				typename := stmt.Data.Type
    78  				i := strings.LastIndex(typename, ".")
    79  				if i == -1 {
    80  					return fmt.Errorf("fwk/job: invalid package name %q (no dot)", typename)
    81  				}
    82  				//typ := typename[i+1:]
    83  				pkg := typename[:i]
    84  				dpkgs[pkg] = struct{}{}
    85  			}
    86  
    87  			for _, v := range stmt.Data.Props {
    88  				typ := reflect.TypeOf(v)
    89  				pkg := typ.PkgPath()
    90  				if pkg == "" {
    91  					continue
    92  				}
    93  				ipkgs[pkg] = struct{}{}
    94  			}
    95  		}
    96  
    97  		fmt.Fprintf(enc.buf, "import (\n")
    98  		fmt.Fprintf(enc.buf, "\t%q\n\n", "go-hep.org/x/hep/fwk/job")
    99  
   100  		for pkg := range ipkgs {
   101  			fmt.Fprintf(enc.buf, "\t%q\n", pkg)
   102  		}
   103  
   104  		for pkg := range dpkgs {
   105  			_, dup := ipkgs[pkg]
   106  			if dup {
   107  				// already imported.
   108  				continue
   109  			}
   110  			fmt.Fprintf(enc.buf, "\t_ %q\n", pkg)
   111  		}
   112  		fmt.Fprintf(enc.buf, ")\n")
   113  
   114  		// first stmt should be the NewApp one.
   115  		if stmts[0].Type != StmtNewApp {
   116  			return fmt.Errorf("fwk/job: invalid stmts! expected stmts[0].Type==%v. got=%v",
   117  				StmtNewApp,
   118  				stmts[0].Type,
   119  			)
   120  		}
   121  
   122  		if stmts[0].Data.Type != "go-hep.org/x/hep/fwk.appmgr" {
   123  			// only support fwk.appmgr for now...
   124  			return fmt.Errorf("fwk/job: invalid fwk.App concrete type (%v)", stmts[0].Data.Type)
   125  		}
   126  
   127  		fmt.Fprintf(enc.buf, `
   128  func newApp() *job.Job {
   129  	app := job.New(%s)
   130  	return app
   131  }
   132  
   133  `,
   134  			enc.repr(stmts[0].Data.Props),
   135  		)
   136  
   137  		enc.init = true
   138  	}
   139  
   140  	fmt.Fprintf(enc.buf, "\nfunc config(app *job.Job) {\n\n")
   141  
   142  	for _, stmt := range stmts {
   143  
   144  		switch stmt.Type {
   145  		case StmtNewApp:
   146  			continue
   147  
   148  		case StmtCreate:
   149  			const tmpl = `
   150  	app.Create(job.C{
   151  		Type:  %q,
   152  		Name:  %q,
   153  		Props: `
   154  			fmt.Fprintf(
   155  				enc.buf,
   156  				tmpl,
   157  				stmt.Data.Type,
   158  				stmt.Data.Name,
   159  			)
   160  			repr := enc.repr(stmt.Data.Props)
   161  			fmt.Fprintf(enc.buf, "%s,\n", string(repr))
   162  
   163  			fmt.Fprintf(enc.buf, "\t})\n")
   164  
   165  		case StmtSetProp:
   166  			var key string
   167  			var val any
   168  			for k, v := range stmt.Data.Props {
   169  				key = k
   170  				val = v
   171  			}
   172  			fmt.Fprintf(
   173  				enc.buf,
   174  				"app.SetProp(app.App().Component(%q), %q, %s)\n",
   175  				stmt.Data.Name,
   176  				key,
   177  				enc.value(val),
   178  			)
   179  
   180  		default:
   181  			return fmt.Errorf("fwk/job: invalid statement type (%#v)", stmt.Type)
   182  		}
   183  	}
   184  
   185  	fmt.Fprintf(enc.buf, "\n} // config\n")
   186  
   187  	return err
   188  }
   189  
   190  func (enc *GoEncoder) repr(props P) []byte {
   191  	var buf bytes.Buffer
   192  	if len(props) <= 0 {
   193  		fmt.Fprintf(&buf, "nil")
   194  		return buf.Bytes()
   195  	}
   196  
   197  	fmt.Fprintf(&buf, "job.P{\n")
   198  	for k, v := range props {
   199  		prop := enc.value(v)
   200  		fmt.Fprintf(&buf, "\t%q: %s,\n", k, prop)
   201  	}
   202  	fmt.Fprintf(&buf, "}")
   203  
   204  	return buf.Bytes()
   205  }
   206  
   207  func (enc *GoEncoder) value(v any) string {
   208  	typ := reflect.TypeOf(v)
   209  	prop := ""
   210  	switch typ.Kind() {
   211  	case reflect.Struct:
   212  		prop = fmt.Sprintf("%#v", v)
   213  	case reflect.String:
   214  		prop = fmt.Sprintf("%q", v)
   215  	default:
   216  		pkgname := typ.PkgPath()
   217  		if pkgname != "" {
   218  			idx := strings.LastIndex(pkgname, "/")
   219  			pkgname = pkgname[idx+1:] + "."
   220  		} else {
   221  			pkgname = ""
   222  		}
   223  		prop = fmt.Sprintf("%s%s(%v)", pkgname, typ.Name(), v)
   224  	}
   225  	return prop
   226  }