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 }