github.com/yaegashi/msgraph.go@v0.1.4/gen/generator_generate.go (about)

     1  package main
     2  
     3  import (
     4  	"bufio"
     5  	"encoding/xml"
     6  	"fmt"
     7  	"io"
     8  	"os"
     9  	"path/filepath"
    10  	"sort"
    11  	"strings"
    12  	"text/template"
    13  )
    14  
    15  type Const struct {
    16  	Name, Value, Type string
    17  }
    18  
    19  type EnumType struct {
    20  	Name        string
    21  	Sym         string
    22  	Members     []*EnumTypeMember
    23  	Description string
    24  }
    25  
    26  type EnumTypeMember struct {
    27  	Name        string
    28  	Sym         string
    29  	Value       string
    30  	Description string
    31  }
    32  
    33  type EntityType struct {
    34  	Name        string
    35  	Sym         string
    36  	Type        string
    37  	Base        string
    38  	Members     []*EntityTypeMember
    39  	Navigations []*EntityTypeNavigation
    40  	Description string
    41  }
    42  
    43  type EntityTypeMember struct {
    44  	Name        string
    45  	Sym         string
    46  	Type        string
    47  	Description string
    48  }
    49  
    50  type EntityTypeNavigation struct {
    51  	Name        string
    52  	Sym         string
    53  	Type        string
    54  	Description string
    55  }
    56  
    57  type ActionType struct {
    58  	Name                 string
    59  	Sym                  string
    60  	BindingParameterType string
    61  	Parameters           []*ActionTypeParameter
    62  	ReturnType           string
    63  	Description          string
    64  }
    65  
    66  type ActionTypeParameter struct {
    67  	Name        string
    68  	Sym         string
    69  	Type        string
    70  	Description string
    71  }
    72  
    73  type EntitySet struct {
    74  	Name string
    75  	Sym  string
    76  	Type string
    77  }
    78  
    79  type Singleton struct {
    80  	Name string
    81  	Sym  string
    82  	Type string
    83  }
    84  
    85  func (g *Generator) Generate() error {
    86  	tmpl, err := template.ParseGlob("templates/*.tmpl")
    87  	if err != nil {
    88  		return err
    89  	}
    90  
    91  	inFile, err := os.Open(g.In)
    92  	if err != nil {
    93  		return err
    94  	}
    95  	defer inFile.Close()
    96  
    97  	var o Elem
    98  	dec := xml.NewDecoder(inFile)
    99  	err = dec.Decode(&o)
   100  	if err != nil {
   101  		return err
   102  	}
   103  
   104  	schema := o.Elems[0].Elems[0]
   105  	enumTypeMap := map[string]*EnumType{}
   106  	entityTypeMap := map[string]*EntityType{}
   107  	actionTypeMap := map[string][]*ActionType{}
   108  	entitySetMap := map[string]*EntitySet{}
   109  	singletonMap := map[string]*Singleton{}
   110  	requestModelMap := map[string]bool{}
   111  	actionRequestBuilderMap := map[string][]string{}
   112  
   113  	for _, x := range schema.Elems {
   114  		switch x.XMLName.Local {
   115  		case "EnumType":
   116  			m := x.AttrMap()
   117  			n := m["Name"]
   118  			t := &EnumType{Name: n, Sym: exported(n), Description: "undocumented"}
   119  			for _, y := range x.Elems {
   120  				n := y.Attrs[0].Value
   121  				// ex. Collection(String) -> StringCollection
   122  				if strings.HasPrefix(n, colPrefix) {
   123  					n = n[len(colPrefix):len(n)-1] + "Collection"
   124  				}
   125  				v := y.Attrs[1].Value
   126  				m := &EnumTypeMember{Name: n, Sym: exported(n), Value: v, Description: "undocumented"}
   127  				t.Members = append(t.Members, m)
   128  			}
   129  			enumTypeMap[n] = t
   130  		case "EntityType", "ComplexType":
   131  			m := x.AttrMap()
   132  			n := m["Name"]
   133  			if _, ok := reservedTypeTable[n]; ok {
   134  				continue
   135  			}
   136  			t := nsPrefix + n
   137  			b, _ := m["BaseType"]
   138  			et := &EntityType{Name: n, Sym: exported(n), Type: t, Base: b, Description: "undocumented"}
   139  			if strings.HasSuffix(et.Sym, "Request") {
   140  				et.Sym += "Object"
   141  				g.SymTypeMap[t] = et.Sym
   142  			}
   143  			for _, y := range x.Elems {
   144  				ma := y.AttrMap()
   145  				switch y.XMLName.Local {
   146  				case "Property":
   147  					n := ma["Name"]
   148  					t := ma["Type"]
   149  					m := &EntityTypeMember{Name: n, Sym: exported(n), Type: t, Description: "undocumented"}
   150  					et.Members = append(et.Members, m)
   151  				case "NavigationProperty":
   152  					n := ma["Name"]
   153  					t := ma["Type"]
   154  					m := &EntityTypeNavigation{Name: n, Sym: exported(n), Type: t, Description: "undocumented"}
   155  					if strings.HasSuffix(m.Sym, "Request") {
   156  						m.Sym += "Navigation"
   157  					}
   158  					et.Navigations = append(et.Navigations, m)
   159  				}
   160  			}
   161  			entityTypeMap[et.Name] = et
   162  		case "Action":
   163  			m := x.AttrMap()
   164  			n := m["Name"]
   165  			at := &ActionType{Name: n, Sym: exported(n), Description: "undocumented"}
   166  			if strings.HasSuffix(at.Sym, "Request") {
   167  				at.Sym += "Action"
   168  			}
   169  			for _, y := range x.Elems {
   170  				ma := y.AttrMap()
   171  				switch y.XMLName.Local {
   172  				case "Parameter":
   173  					n := ma["Name"]
   174  					t := ma["Type"]
   175  					m := &ActionTypeParameter{Name: n, Sym: exported(n), Type: t, Description: "undocumented"}
   176  					at.Parameters = append(at.Parameters, m)
   177  				case "ReturnType":
   178  					at.ReturnType = ma["Type"]
   179  				}
   180  			}
   181  			at.BindingParameterType = at.Parameters[0].Type
   182  			at.Parameters = at.Parameters[1:]
   183  			actionTypeMap[at.BindingParameterType] = append(actionTypeMap[at.BindingParameterType], at)
   184  		case "EntityContainer":
   185  			for _, y := range x.Elems {
   186  				ma := y.AttrMap()
   187  				switch y.XMLName.Local {
   188  				case "EntitySet":
   189  					s := &EntitySet{
   190  						Name: ma["Name"],
   191  						Sym:  exported(ma["Name"]),
   192  						Type: ma["EntityType"],
   193  					}
   194  					entitySetMap[s.Name] = s
   195  				case "Singleton":
   196  					s := &Singleton{
   197  						Name: ma["Name"],
   198  						Sym:  exported(ma["Name"]),
   199  						Type: ma["Type"],
   200  					}
   201  					singletonMap[s.Name] = s
   202  				}
   203  			}
   204  		case "Annotations":
   205  			mas := x.AttrMap()
   206  			target, _ := stripNSPrefix(mas["Target"])
   207  			targetList := strings.Split(target, "/")
   208  			targetMember := ""
   209  			if len(targetList) > 1 {
   210  				target = targetList[0]
   211  				targetMember = targetList[1]
   212  			}
   213  			for _, y := range x.Elems {
   214  				switch y.XMLName.Local {
   215  				case "Annotation":
   216  					ma := y.AttrMap()
   217  					term, _ := ma["Term"]
   218  					str, _ := ma["String"]
   219  					if term == "Org.OData.Core.V1.Description" {
   220  						if e, ok := entityTypeMap[target]; ok {
   221  							if targetMember == "" {
   222  								e.Description = str
   223  							} else {
   224  								for _, mem := range e.Members {
   225  									if mem.Name == targetMember {
   226  										mem.Description = str
   227  									}
   228  								}
   229  							}
   230  						}
   231  					}
   232  				}
   233  			}
   234  		}
   235  	}
   236  
   237  	// Copy all *.go files without template processing
   238  	// Copy everything below the first "// BEGIN" line to the output
   239  	paths, err := filepath.Glob("templates/*.go")
   240  	if err != nil {
   241  		return err
   242  	}
   243  	for _, path := range paths {
   244  		err := func() error {
   245  			inFile, err := os.Open(path)
   246  			if err != nil {
   247  				return err
   248  			}
   249  			defer inFile.Close()
   250  			outFile, err := g.Create(filepath.Base(path), "")
   251  			if err != nil {
   252  				return err
   253  			}
   254  			defer outFile.Close()
   255  			scanner := bufio.NewScanner(inFile)
   256  			enabled := false
   257  			for scanner.Scan() {
   258  				line := scanner.Text()
   259  				if strings.HasPrefix(line, "// BEGIN") {
   260  					enabled = true
   261  					continue
   262  				}
   263  				if enabled {
   264  					fmt.Fprintln(outFile, line)
   265  				}
   266  			}
   267  			return scanner.Err()
   268  		}()
   269  		if err != nil {
   270  			return err
   271  		}
   272  	}
   273  
   274  	// Copy some *.go.tmpl files with template processing
   275  	for _, path := range []string{"const.go.tmpl"} {
   276  		out, err := g.Create(path[:len(path)-5], "")
   277  		if err != nil {
   278  			return err
   279  		}
   280  		err = tmpl.ExecuteTemplate(out, path, g)
   281  		if err != nil {
   282  			return err
   283  		}
   284  		out.Close()
   285  	}
   286  
   287  	var out io.WriteCloser
   288  
   289  	keys := []string{}
   290  	for x := range enumTypeMap {
   291  		keys = append(keys, x)
   292  	}
   293  	sort.Strings(keys)
   294  	for _, key := range keys {
   295  		x := enumTypeMap[key]
   296  		out, err = g.Create("Enum", x.Sym)
   297  		g.X = x
   298  		if err != nil {
   299  			return err
   300  		}
   301  		err := tmpl.ExecuteTemplate(out, "enum.go.tmpl", g)
   302  		if err != nil {
   303  			return err
   304  		}
   305  		out.Close()
   306  	}
   307  
   308  	keys = nil
   309  	for x := range entityTypeMap {
   310  		keys = append(keys, x)
   311  	}
   312  	sort.Strings(keys)
   313  	for _, key := range keys {
   314  		x := entityTypeMap[key]
   315  		out, err = g.Create("Model", x.Sym)
   316  		if err != nil {
   317  			return err
   318  		}
   319  		g.X = x
   320  		err := tmpl.ExecuteTemplate(out, "model.go.tmpl", g)
   321  		if err != nil {
   322  			return err
   323  		}
   324  		out.Close()
   325  	}
   326  
   327  	keys = nil
   328  	for x := range actionTypeMap {
   329  		keys = append(keys, x)
   330  	}
   331  	sort.Strings(keys)
   332  	for _, a := range keys {
   333  		if _, ok := reservedTypeTable[a]; ok {
   334  			continue
   335  		}
   336  		x := actionTypeMap[a]
   337  		out, err = g.Create("Action", g.SymBaseType(a))
   338  		if err != nil {
   339  			return err
   340  		}
   341  		requestModelMap[g.SymBaseType(a)] = true
   342  		for _, y := range x {
   343  			g.X = y
   344  			err := tmpl.ExecuteTemplate(out, "action.go.tmpl", g)
   345  			if err != nil {
   346  				return err
   347  			}
   348  		}
   349  		out.Close()
   350  	}
   351  
   352  	for _, x := range entityTypeMap {
   353  		if len(x.Navigations) == 0 {
   354  			continue
   355  		}
   356  		requestModelMap[x.Sym] = true
   357  		for _, y := range x.Navigations {
   358  			requestModelMap[g.SymBaseType(y.Type)] = true
   359  		}
   360  	}
   361  	for _, x := range entitySetMap {
   362  		requestModelMap[g.SymBaseType(x.Type)] = true
   363  	}
   364  	for _, x := range singletonMap {
   365  		requestModelMap[g.SymBaseType(x.Type)] = true
   366  	}
   367  
   368  	keys = nil
   369  	for x := range requestModelMap {
   370  		keys = append(keys, x)
   371  	}
   372  	sort.Strings(keys)
   373  	for _, x := range keys {
   374  		out, err = g.Create("Request", x)
   375  		if err != nil {
   376  			return err
   377  		}
   378  		g.X = x
   379  		err := tmpl.ExecuteTemplate(out, "request_model.go.tmpl", g)
   380  		if err != nil {
   381  			return err
   382  		}
   383  		out.Close()
   384  	}
   385  
   386  	out, err = g.Create("GraphService", "")
   387  	if err != nil {
   388  		return err
   389  	}
   390  	keys = nil
   391  	for x := range entitySetMap {
   392  		keys = append(keys, x)
   393  	}
   394  	sort.Strings(keys)
   395  	for _, key := range keys {
   396  		g.X = &EntityType{Name: "GraphService", Sym: "GraphService"}
   397  		g.Y = entitySetMap[key]
   398  		err := tmpl.ExecuteTemplate(out, "request_collection_navigation.go.tmpl", g)
   399  		if err != nil {
   400  			return err
   401  		}
   402  	}
   403  	keys = nil
   404  	for x := range singletonMap {
   405  		keys = append(keys, x)
   406  	}
   407  	sort.Strings(keys)
   408  	for _, key := range keys {
   409  		g.X = &EntityType{Name: "GraphService", Sym: "GraphService"}
   410  		g.Y = singletonMap[key]
   411  		err := tmpl.ExecuteTemplate(out, "request_navigation.go.tmpl", g)
   412  		if err != nil {
   413  			return err
   414  		}
   415  	}
   416  	out.Close()
   417  
   418  	keys = nil
   419  	for x := range entityTypeMap {
   420  		keys = append(keys, x)
   421  	}
   422  	sort.Strings(keys)
   423  	for _, key := range keys {
   424  		x := entityTypeMap[key]
   425  		actionRequestBuilderMap[x.Type] = append(actionRequestBuilderMap[x.Type], x.Sym)
   426  		if len(x.Navigations) == 0 {
   427  			continue
   428  		}
   429  		out, err = g.Create("Action", x.Sym)
   430  		if err != nil {
   431  			return err
   432  		}
   433  		g.X = x
   434  		sort.Slice(x.Navigations, func(i, j int) bool { return x.Navigations[i].Name < x.Navigations[j].Name })
   435  		for _, y := range x.Navigations {
   436  			g.Y = y
   437  			if isCollectionType(y.Type) {
   438  				actionRequestBuilderMap[y.Type] = append(actionRequestBuilderMap[y.Type], x.Sym+y.Sym+"Collection")
   439  				err := tmpl.ExecuteTemplate(out, "request_collection_navigation.go.tmpl", g)
   440  				if err != nil {
   441  					return err
   442  				}
   443  			} else {
   444  				err := tmpl.ExecuteTemplate(out, "request_navigation.go.tmpl", g)
   445  				if err != nil {
   446  					return err
   447  				}
   448  			}
   449  		}
   450  		out.Close()
   451  	}
   452  
   453  	keys = nil
   454  	for x := range actionTypeMap {
   455  		keys = append(keys, x)
   456  	}
   457  	sort.Strings(keys)
   458  	for _, a := range keys {
   459  		x := actionTypeMap[a]
   460  		if _, ok := reservedTypeTable[a]; ok {
   461  			continue
   462  		}
   463  		for _, y := range x {
   464  			out, err = g.Create("Request", g.SymBaseType(a)+y.Sym)
   465  			if err != nil {
   466  				return err
   467  			}
   468  			g.Y = y
   469  			if b, ok := actionRequestBuilderMap[a]; ok {
   470  				g.X = b
   471  				if y.ReturnType == "" {
   472  					err = tmpl.ExecuteTemplate(out, "request_action_void.go.tmpl", g)
   473  				} else if isCollectionType(y.ReturnType) {
   474  					err = tmpl.ExecuteTemplate(out, "request_action_collection.go.tmpl", g)
   475  				} else {
   476  					err = tmpl.ExecuteTemplate(out, "request_action_single.go.tmpl", g)
   477  				}
   478  				if err != nil {
   479  					return err
   480  				}
   481  			}
   482  			out.Close()
   483  		}
   484  	}
   485  
   486  	return nil
   487  }