Skip to content

Commit

Permalink
✨ Add usage command, add filter flags
Browse files Browse the repository at this point in the history
  • Loading branch information
wesen committed Oct 2, 2023
1 parent 4212ae8 commit d3f4782
Show file tree
Hide file tree
Showing 4 changed files with 228 additions and 3 deletions.
43 changes: 40 additions & 3 deletions cmd/css-use/find-unused.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,24 @@ func NewFindUnusedClassesCommand() (*FindUnusedClassesCommand, error) {
parameters.WithHelp("Check if all classes in a file are unused"),
parameters.WithDefault(false),
),
parameters.NewParameterDefinition(
"filter-defining-files",
parameters.ParameterTypeStringList,
parameters.WithHelp("List of files to filter on (can be glob)"),
parameters.WithDefault([]string{}),
),
parameters.NewParameterDefinition(
"filter-using-files",
parameters.ParameterTypeStringList,
parameters.WithHelp("List of files to filter on (can be glob)"),
parameters.WithDefault([]string{}),
),
parameters.NewParameterDefinition(
"filter-classes",
parameters.ParameterTypeStringList,
parameters.WithHelp("List of classes to filter on (can be glob)"),
parameters.WithDefault([]string{}),
),
),
cmds.WithLayers(
glazedParameterLayer,
Expand Down Expand Up @@ -79,20 +97,26 @@ func (c *FindUnusedClassesCommand) Run(
// Structures to keep track of counts
fileToTotalClasses := make(map[string]int)
fileToUsedClasses := make(map[string]map[string]interface{})
classToFiles := make(map[string][]string)
definedClassesToFiles := make(map[string][]string)

// Count the total number of classes per file
for _, entry := range defined {
file := entry["file"].(string)
fileToTotalClasses[file]++
class := entry["class"].(string)
classToFiles[class] = append(classToFiles[class], file)
definedClassesToFiles[class] = append(definedClassesToFiles[class], file)
}

filterUsingFiles := ps["filter-using-files"].([]string)
// Count the number of used classes per file
for _, entry := range used {
if len(filterUsingFiles) > 0 {
if !containsGlob(filterUsingFiles, entry["file"].(string)) {
continue
}
}
class_ := entry["class"].(string)
for _, file := range classToFiles[class_] {
for _, file := range definedClassesToFiles[class_] {
if _, found := fileToUsedClasses[file]; !found {
fileToUsedClasses[file] = make(map[string]interface{})
}
Expand All @@ -102,10 +126,23 @@ func (c *FindUnusedClassesCommand) Run(

checkAllUnused := ps["check-all-unused"].(bool)

filterDefiningFiles := ps["filter-defining-files"].([]string)
filterClasses := ps["filter-classes"].([]string)

for _, entry := range defined {
class := entry["class"].(string)
if _, found := usedMap[class]; !found {
filename := entry["file"].(string)
if len(filterDefiningFiles) > 0 {
if !containsGlob(filterDefiningFiles, filename) {
continue
}
}
if len(filterClasses) > 0 {
if !containsGlob(filterClasses, class) {
continue
}
}
row := types.NewRow(
types.MRP("class", class),
types.MRP("file", filename),
Expand Down
6 changes: 6 additions & 0 deletions cmd/css-use/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,11 @@ func main() {
cobra.CheckErr(err)
rootCmd.AddCommand(command)

UsageCmd, err := NewFindUsageClassesCommand()
cobra.CheckErr(err)
command, err = cli.BuildCobraCommandFromGlazeCommand(UsageCmd)
cobra.CheckErr(err)
rootCmd.AddCommand(command)

_ = rootCmd.Execute()
}
145 changes: 145 additions & 0 deletions cmd/css-use/usage.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
package main

import (
"context"
"github.com/go-go-golems/glazed/pkg/cmds"
"github.com/go-go-golems/glazed/pkg/cmds/layers"
"github.com/go-go-golems/glazed/pkg/cmds/parameters"
"github.com/go-go-golems/glazed/pkg/helpers/cast"
"github.com/go-go-golems/glazed/pkg/middlewares"
"github.com/go-go-golems/glazed/pkg/settings"
"github.com/go-go-golems/glazed/pkg/types"
"github.com/pkg/errors"
"github.com/rs/zerolog/log"
)

type FindUsageClassesCommand struct {
*cmds.CommandDescription
}

func NewFindUsageClassesCommand() (*FindUsageClassesCommand, error) {
glazedParameterLayer, err := settings.NewGlazedParameterLayers()
if err != nil {
return nil, errors.Wrap(err, "could not create Glazed parameter layer")
}

return &FindUsageClassesCommand{
CommandDescription: cmds.NewCommandDescription(
"usage",
cmds.WithShort("Find classes usage command"),
cmds.WithFlags(
parameters.NewParameterDefinition(
"used",
parameters.ParameterTypeObjectListFromFile,
parameters.WithHelp("Path to the used.json file"),
parameters.WithRequired(true),
),
parameters.NewParameterDefinition(
"defined",
parameters.ParameterTypeObjectListFromFile,
parameters.WithHelp("Path to the defined.json file"),
parameters.WithRequired(true),
),
parameters.NewParameterDefinition(
"filter-defining-files",
parameters.ParameterTypeStringList,
parameters.WithHelp("List of files to filter on (can be glob)"),
parameters.WithDefault([]string{}),
),
parameters.NewParameterDefinition(
"filter-using-files",
parameters.ParameterTypeStringList,
parameters.WithHelp("List of files to filter on (can be glob)"),
parameters.WithDefault([]string{}),
),
parameters.NewParameterDefinition(
"filter-classes",
parameters.ParameterTypeStringList,
parameters.WithHelp("List of classes to filter on (can be glob)"),
parameters.WithDefault([]string{}),
),
),
cmds.WithLayers(
glazedParameterLayer,
),
),
}, nil
}

func (c *FindUsageClassesCommand) Run(
ctx context.Context,
parsedLayers map[string]*layers.ParsedParameterLayer,
ps map[string]interface{},
gp middlewares.Processor,
) error {
used_ := ps["used"].([]interface{})
used, ok := cast.CastList2[map[string]interface{}, interface{}](used_)
if !ok {
return errors.New("could not cast used")
}
defined_ := ps["defined"].([]interface{})
defined, ok := cast.CastList2[map[string]interface{}, interface{}](defined_)
if !ok {
return errors.New("could not cast defined")
}

definedClassesToFiles := make(map[string][]string)
fileToDefinedClasses := make(map[string][]string)

for _, entry := range defined {
file := entry["file"].(string)
class := entry["class"].(string)
definedClassesToFiles[class] = append(definedClassesToFiles[class], file)

fileToDefinedClasses[file] = append(fileToDefinedClasses[file], class)

if len(definedClassesToFiles[class]) > 1 {
log.Warn().Str("class", class).
Strs("files", definedClassesToFiles[class]).
Msg("class defined in multiple files")
}
}

usedClassesToFiles := make(map[string][]string)

for _, entry := range used {
class_ := entry["class"].(string)
file := entry["file"].(string)
usedClassesToFiles[class_] = append(usedClassesToFiles[class_], file)
}

filterDefiningFiles := ps["filter-defining-files"].([]string)
filterUsingFiles := ps["filter-using-files"].([]string)
filterClasses := ps["filter-classes"].([]string)

for file, classes := range fileToDefinedClasses {
for _, class := range classes {
filesUsingClass := usedClassesToFiles[class]
if len(filterDefiningFiles) > 0 {
if !containsGlob(filterDefiningFiles, file) {
continue
}
}
if len(filterUsingFiles) > 0 {
if !containsAnyGlob(filterUsingFiles, filesUsingClass) {
continue
}
}
if len(filterClasses) > 0 {
if !containsGlob(filterClasses, class) {
continue
}
}
row := types.NewRow(
types.MRP("class", class),
types.MRP("file", file),
types.MRP("used", filesUsingClass),
types.MRP("unused", len(filesUsingClass) == 0),
)
if err := gp.AddRow(ctx, row); err != nil {
return err
}
}
}
return nil
}
37 changes: 37 additions & 0 deletions cmd/css-use/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"io"
"net/http"
"os"
"path"
"strings"
)

Expand Down Expand Up @@ -134,3 +135,39 @@ func ReaderUrlOrFile(url string) (io.ReadCloser, error) {
}
return reader, nil
}

func containsAny(haystack []string, needles []string) bool {
for _, needle := range needles {
if contains(haystack, needle) {
return true
}
}
return false
}

func containsAnyGlob(globHaystack []string, needles []string) bool {
for _, needle := range needles {
if containsGlob(globHaystack, needle) {
return true
}
}
return false
}

func contains(haystack []string, needle string) bool {
for _, item := range haystack {
if item == needle {
return true
}
}
return false
}

func containsGlob(globHaystack []string, needle string) bool {
for _, glob := range globHaystack {
if match, _ := path.Match(glob, needle); match {
return true
}
}
return false
}

0 comments on commit d3f4782

Please sign in to comment.