github.com/jgarto/itcv@v0.0.0-20180826224514-4eea09c1aa0d/cmd/reactGen/comp_gen.go (about)

     1  package main
     2  
     3  import (
     4  	"fmt"
     5  	"go/ast"
     6  	"go/format"
     7  	"io/ioutil"
     8  	"strings"
     9  	"unicode"
    10  	"unicode/utf8"
    11  
    12  	"myitcv.io/gogenerate"
    13  )
    14  
    15  type compGen struct {
    16  	*coreGen
    17  
    18  	Recv string
    19  	Name string
    20  
    21  	HasState                     bool
    22  	HasProps                     bool
    23  	HasGetInitState              bool
    24  	HasComponentWillReceiveProps bool
    25  
    26  	PropsHasEquals bool
    27  	StateHasEquals bool
    28  
    29  	RendersSlice bool
    30  
    31  	RendersMethods []RendersMethod
    32  }
    33  
    34  type RendersMethod struct {
    35  	Name string
    36  	Type string
    37  }
    38  
    39  func (g *gen) genComp(defName string) {
    40  
    41  	name := strings.TrimSuffix(defName, compDefSuffix)
    42  
    43  	if g.isReactCore {
    44  		panic(fmt.Errorf("don't yet know how to generate core components like %v", name))
    45  	}
    46  
    47  	r, _ := utf8.DecodeRuneInString(name)
    48  
    49  	cg := &compGen{
    50  		coreGen: newCoreGen(g),
    51  		Name:    name,
    52  		Recv:    string(unicode.ToLower(r)),
    53  	}
    54  
    55  	_, hasState := g.types[name+stateTypeSuffix]
    56  
    57  	_, hasPropsTmpl := g.types[propsTypeTmplPrefix+name+propsTypeSuffix]
    58  	_, hasPropsType := g.types[name+propsTypeSuffix]
    59  
    60  	hasProps := hasPropsTmpl || hasPropsType
    61  
    62  	cg.HasState = hasState
    63  	cg.HasProps = hasProps
    64  
    65  	for _, ff := range g.nonPointMeths[defName] {
    66  		m := ff.fn
    67  
    68  		// gather Renders* methods
    69  		func() {
    70  			if !strings.HasPrefix(m.Name.Name, "Renders") {
    71  				return
    72  			}
    73  
    74  			if m.Type.Params == nil || len(m.Type.Params.List) != 1 {
    75  				return
    76  			}
    77  
    78  			if m.Type.Results != nil && len(m.Type.Results.List) != 0 {
    79  				return
    80  			}
    81  
    82  			ap := m.Type.Params.List[0]
    83  
    84  			cg.RendersMethods = append(cg.RendersMethods, RendersMethod{
    85  				Name: m.Name.Name,
    86  				Type: astNodeString(ap.Type),
    87  			})
    88  		}()
    89  
    90  		func() {
    91  			if m.Name.Name != "Render" {
    92  				return
    93  			}
    94  
    95  			if m.Type.Params != nil && len(m.Type.Params.List) != 0 {
    96  				return
    97  			}
    98  
    99  			if m.Type.Results == nil || len(m.Type.Results.List) != 1 {
   100  				return
   101  			}
   102  
   103  			rp := m.Type.Results.List[0]
   104  
   105  			at, ok := rp.Type.(*ast.ArrayType)
   106  
   107  			if !ok {
   108  				return
   109  			}
   110  
   111  			if at.Len == nil {
   112  				cg.RendersSlice = true
   113  			}
   114  		}()
   115  	}
   116  
   117  	if hasState {
   118  		for _, ff := range g.nonPointMeths[defName] {
   119  			m := ff.fn
   120  
   121  			if m.Name.Name != getInitialState {
   122  				continue
   123  			}
   124  
   125  			if m.Type.Params != nil && len(m.Type.Params.List) > 0 {
   126  				continue
   127  			}
   128  
   129  			if m.Type.Results != nil && len(m.Type.Results.List) != 1 {
   130  				continue
   131  			}
   132  
   133  			rp := m.Type.Results.List[0]
   134  
   135  			id, ok := rp.Type.(*ast.Ident)
   136  			if !ok {
   137  				continue
   138  			}
   139  
   140  			if id.Name == name+stateTypeSuffix {
   141  				cg.HasGetInitState = true
   142  				break
   143  			}
   144  		}
   145  
   146  		for _, ff := range g.nonPointMeths[name+stateTypeSuffix] {
   147  			m := ff.fn
   148  
   149  			if m.Name.Name != equals {
   150  				continue
   151  			}
   152  
   153  			if m.Type.Params != nil && len(m.Type.Params.List) != 1 {
   154  				continue
   155  			}
   156  
   157  			if m.Type.Results != nil && len(m.Type.Results.List) != 1 {
   158  				continue
   159  			}
   160  
   161  			{
   162  				v := m.Type.Params.List[0]
   163  
   164  				id, ok := v.Type.(*ast.Ident)
   165  				if !ok {
   166  					continue
   167  				}
   168  
   169  				if id.Name != name+stateTypeSuffix {
   170  					continue
   171  				}
   172  			}
   173  
   174  			{
   175  				v := m.Type.Results.List[0]
   176  
   177  				id, ok := v.Type.(*ast.Ident)
   178  				if !ok {
   179  					continue
   180  				}
   181  
   182  				if id.Name != "bool" {
   183  					continue
   184  				}
   185  			}
   186  
   187  			cg.StateHasEquals = true
   188  		}
   189  	}
   190  
   191  	if hasProps {
   192  		for _, ff := range g.nonPointMeths[defName] {
   193  			m := ff.fn
   194  
   195  			if m.Name.Name != componentWillReceiveProps {
   196  				continue
   197  			}
   198  
   199  			if m.Type.Params != nil && len(m.Type.Params.List) != 1 {
   200  				continue
   201  			}
   202  
   203  			if m.Type.Results != nil && len(m.Type.Results.List) != 0 {
   204  				continue
   205  			}
   206  
   207  			p := m.Type.Params.List[0]
   208  
   209  			id, ok := p.Type.(*ast.Ident)
   210  			if !ok {
   211  				continue
   212  			}
   213  
   214  			if id.Name == name+propsTypeSuffix {
   215  				cg.HasComponentWillReceiveProps = true
   216  				break
   217  			}
   218  		}
   219  
   220  		for _, ff := range g.nonPointMeths[name+propsTypeSuffix] {
   221  			m := ff.fn
   222  
   223  			if m.Name.Name != equals {
   224  				continue
   225  			}
   226  
   227  			if m.Type.Params != nil && len(m.Type.Params.List) != 1 {
   228  				continue
   229  			}
   230  
   231  			if m.Type.Results != nil && len(m.Type.Results.List) != 1 {
   232  				continue
   233  			}
   234  
   235  			{
   236  				v := m.Type.Params.List[0]
   237  
   238  				id, ok := v.Type.(*ast.Ident)
   239  				if !ok {
   240  					continue
   241  				}
   242  
   243  				if id.Name != name+propsTypeSuffix {
   244  					continue
   245  				}
   246  			}
   247  
   248  			{
   249  				v := m.Type.Results.List[0]
   250  
   251  				id, ok := v.Type.(*ast.Ident)
   252  				if !ok {
   253  					continue
   254  				}
   255  
   256  				if id.Name != "bool" {
   257  					continue
   258  				}
   259  			}
   260  
   261  			cg.PropsHasEquals = true
   262  		}
   263  	}
   264  
   265  	cg.pf("// Code generated by %v. DO NOT EDIT.\n", reactGenCmd)
   266  	cg.pln()
   267  	cg.pf("package %v\n", cg.pkg)
   268  
   269  	cg.pf("import \"%v\"\n", reactPkg)
   270  	cg.pln()
   271  
   272  	cg.pt(`
   273  {{ $cg := . }}
   274  
   275  type {{.Name}}Elem struct {
   276  	react.Element
   277  }
   278  
   279  {{range $rm := .RendersMethods }}
   280  func ({{$cg.Recv}} *{{$cg.Name}}Elem) {{$rm.Name}}({{$rm.Type}}) {}
   281  {{end}}
   282  
   283  {{if .RendersMethods}}
   284  func ({{$cg.Recv}} *{{$cg.Name}}Elem) noop() {
   285  	var v {{.Name}}Def
   286  	r := v.Render()
   287  
   288  	{{range $rm := .RendersMethods }}
   289  	v.{{$rm.Name}}(r)
   290  	{{end -}}
   291  }
   292  {{end}}
   293  
   294  func build{{.Name}}(cd react.ComponentDef) react.Component {
   295  	return {{.Name}}Def{ComponentDef: cd}
   296  }
   297  
   298  func build{{.Name}}Elem({{if .HasProps}}props {{.Name}}Props,{{end}} children ...react.Element) *{{.Name}}Elem {
   299  	return &{{.Name}}Elem{
   300  		Element: react.CreateElement(build{{.Name}}, {{if .HasProps}}props{{else}}nil{{end}}, children...),
   301  	}
   302  }
   303  
   304  func ({{.Recv}} {{.Name}}Def) RendersElement() react.Element {
   305  	{{if .RendersSlice -}}
   306  	rr := t.Render()
   307  	elems := make([]react.Element, 0, len(rr))
   308  	for _, r := range rr {
   309  		elems = append(elems, r)
   310  	}
   311  	return react.Fragment(elems...)
   312  	{{else -}}
   313  	return {{.Recv}}.Render()
   314  	{{end -}}
   315  }
   316  
   317  {{if .HasState}}
   318  // SetState is an auto-generated proxy proxy to update the state for the
   319  // {{.Name}} component.  SetState does not immediately mutate {{.Recv}}.State()
   320  // but creates a pending state transition.
   321  func ({{.Recv}} {{.Name}}Def) SetState(state {{.Name}}State) {
   322  	{{.Recv}}.ComponentDef.SetState(state)
   323  }
   324  
   325  // State is an auto-generated proxy to return the current state in use for the
   326  // render of the {{.Name}} component
   327  func ({{.Recv}} {{.Name}}Def) State() {{.Name}}State {
   328  	return {{.Recv}}.ComponentDef.State().({{.Name}}State)
   329  }
   330  
   331  // IsState is an auto-generated definition so that {{.Name}}State implements
   332  // the myitcv.io/react.State interface.
   333  func ({{.Recv}} {{.Name}}State) IsState() {}
   334  
   335  var _ react.State = {{.Name}}State{}
   336  
   337  // GetInitialStateIntf is an auto-generated proxy to GetInitialState
   338  func ({{.Recv}} {{.Name}}Def) GetInitialStateIntf() react.State {
   339  {{if .HasGetInitState -}}
   340  	return {{.Recv}}.GetInitialState()
   341  {{else -}}
   342  	return {{.Name}}State{}
   343  {{end -}}
   344  }
   345  
   346  func ({{.Recv}} {{.Name}}State) EqualsIntf(val react.State) bool {
   347  	{{if .StateHasEquals -}}
   348  	return {{.Recv}}.Equals(val.({{.Name}}State))
   349  	{{else -}}
   350  	return {{.Recv}} == val.({{.Name}}State)
   351  	{{end -}}
   352  }
   353  {{end}}
   354  
   355  
   356  {{if .HasProps}}
   357  // IsProps is an auto-generated definition so that {{.Name}}Props implements
   358  // the myitcv.io/react.Props interface.
   359  func ({{.Recv}} {{.Name}}Props) IsProps() {}
   360  
   361  // Props is an auto-generated proxy to the current props of {{.Name}}
   362  func ({{.Recv}} {{.Name}}Def) Props() {{.Name}}Props {
   363  	uprops := {{.Recv}}.ComponentDef.Props()
   364  	return uprops.({{.Name}}Props)
   365  }
   366  
   367  {{if .HasComponentWillReceiveProps}}
   368  // ComponentWillReceivePropsIntf is an auto-generated proxy to
   369  // ComponentWillReceiveProps
   370  func ({{.Recv}} {{.Name}}Def) ComponentWillReceivePropsIntf(val interface{}) {
   371  	ourProps := val.({{.Name}}Props)
   372  	{{.Recv}}.ComponentWillReceiveProps(ourProps)
   373  }
   374  {{end}}
   375  
   376  func ({{.Recv}} {{.Name}}Props) EqualsIntf(val react.Props) bool {
   377  	{{if .PropsHasEquals -}}
   378  	return {{.Recv}}.Equals(val.({{.Name}}Props))
   379  	{{else -}}
   380  	return {{.Recv}} == val.({{.Name}}Props)
   381  	{{end -}}
   382  }
   383  
   384  var _ react.Props = {{.Name}}Props{}
   385  {{end}}
   386  	`, cg)
   387  
   388  	ofName := gogenerate.NameFile(name, reactGenCmd)
   389  	toWrite := cg.buf.Bytes()
   390  
   391  	out, err := format.Source(toWrite)
   392  	if err == nil {
   393  		toWrite = out
   394  	}
   395  
   396  	if err := ioutil.WriteFile(ofName, toWrite, 0644); err != nil {
   397  		fatalf("could not write %v: %v", ofName, err)
   398  	}
   399  }