github.com/mlmmr/revel-cmd@v0.21.2-0.20191112133115-68d8795776dd/model/source_info.go (about) 1 package model 2 3 // SourceInfo is the top-level struct containing all extracted information 4 // about the app source code, used to generate main.go. 5 import ( 6 "github.com/mlmmr/revel-cmd/utils" 7 "path/filepath" 8 "strings" 9 "unicode" 10 ) 11 12 type SourceInfo struct { 13 // StructSpecs lists type info for all structs found under the code paths. 14 // They may be queried to determine which ones (transitively) embed certain types. 15 StructSpecs []*TypeInfo 16 // ValidationKeys provides a two-level lookup. The keys are: 17 // 1. The fully-qualified function name, 18 // e.g. "github.com/revel/examples/chat/app/controllers.(*Application).Action" 19 // 2. Within that func's file, the line number of the (overall) expression statement. 20 // e.g. the line returned from runtime.Caller() 21 // The result of the lookup the name of variable being validated. 22 ValidationKeys map[string]map[int]string 23 // A list of import paths. 24 // Revel notices files with an init() function and imports that package. 25 InitImportPaths []string 26 27 // controllerSpecs lists type info for all structs found under 28 // app/controllers/... that embed (directly or indirectly) revel.Controller 29 controllerSpecs []*TypeInfo 30 // testSuites list the types that constitute the set of application tests. 31 testSuites []*TypeInfo 32 } 33 34 // TypesThatEmbed returns all types that (directly or indirectly) embed the 35 // target type, which must be a fully qualified type name, 36 // e.g. "github.com/revel/revel.Controller" 37 func (s *SourceInfo) TypesThatEmbed(targetType, packageFilter string) (filtered []*TypeInfo) { 38 // Do a search in the "embedded type graph", starting with the target type. 39 var ( 40 nodeQueue = []string{targetType} 41 processed []string 42 ) 43 for len(nodeQueue) > 0 { 44 typeSimpleName := nodeQueue[0] 45 nodeQueue = nodeQueue[1:] 46 processed = append(processed, typeSimpleName) 47 48 // Look through all known structs. 49 for _, spec := range s.StructSpecs { 50 // If this one has been processed or is already in nodeQueue, then skip it. 51 if utils.ContainsString(processed, spec.String()) || 52 utils.ContainsString(nodeQueue, spec.String()) { 53 continue 54 } 55 56 // Look through the embedded types to see if the current type is among them. 57 for _, embeddedType := range spec.EmbeddedTypes { 58 59 // If so, add this type's simple name to the nodeQueue, and its spec to 60 // the filtered list. 61 if typeSimpleName == embeddedType.String() { 62 nodeQueue = append(nodeQueue, spec.String()) 63 filtered = append(filtered, spec) 64 break 65 } 66 } 67 } 68 } 69 70 // Strip out any specifications that contain a lower case 71 for exit := false; !exit; exit = true { 72 for i, filteredItem := range filtered { 73 if unicode.IsLower([]rune(filteredItem.StructName)[0]) { 74 utils.Logger.Info("Debug: Skipping adding spec for unexported type", 75 "type", filteredItem.StructName, 76 "package", filteredItem.ImportPath) 77 filtered = append(filtered[:i], filtered[i+1:]...) 78 exit = false 79 break 80 } 81 } 82 } 83 84 // Check for any missed types that where from expected packages 85 for _, spec := range s.StructSpecs { 86 if spec.PackageName == packageFilter { 87 found := false 88 unfoundNames := "" 89 for _, filteredItem := range filtered { 90 if filteredItem.StructName == spec.StructName { 91 found = true 92 break 93 } else { 94 unfoundNames += filteredItem.StructName + "," 95 } 96 } 97 98 // Report non controller structures in controller folder. 99 if !found && !strings.HasPrefix(spec.StructName, "Test") { 100 utils.Logger.Warn("Type found in package: "+packageFilter+ 101 ", but did not embed from: "+filepath.Base(targetType), 102 "name", spec.StructName, "importpath", spec.ImportPath, "foundstructures", unfoundNames) 103 } 104 } 105 } 106 return 107 } 108 109 // ControllerSpecs returns the all the controllers that embeds 110 // `revel.Controller` 111 func (s *SourceInfo) ControllerSpecs() []*TypeInfo { 112 if s.controllerSpecs == nil { 113 s.controllerSpecs = s.TypesThatEmbed(RevelImportPath+".Controller", "controllers") 114 } 115 return s.controllerSpecs 116 } 117 118 // TestSuites returns the all the Application tests that embeds 119 // `testing.TestSuite` 120 func (s *SourceInfo) TestSuites() []*TypeInfo { 121 if s.testSuites == nil { 122 s.testSuites = s.TypesThatEmbed(RevelImportPath+"/testing.TestSuite", "testsuite") 123 } 124 return s.testSuites 125 }