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