Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: match before checking cache #447

Merged
merged 1 commit into from
Oct 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 61 additions & 38 deletions cmd/format/format.go
Original file line number Diff line number Diff line change
Expand Up @@ -205,33 +205,34 @@ func Run(v *viper.Viper, statz *stats.Stats, cmd *cobra.Command, paths []string)
return fmt.Errorf("failed to create walker: %w", err)
}

// start traversing
files := make([]*walk.File, BatchSize)

for {
// read the next batch
ctx, cancel := context.WithTimeout(ctx, 1*time.Second)

n, err := reader.Read(ctx, files)

for idx := 0; idx < n; idx++ {
file := files[idx]
// ensure context is cancelled to release resources
cancel()
brianmcgee marked this conversation as resolved.
Show resolved Hide resolved

// check if this file is new or has changed when compared to the cache entry
if file.Cache == nil || file.Cache.HasChanged(file.Info) {
filesCh <- file
statz.Add(stats.Emitted, 1)
}
// pass each file into the file channel for processing
for idx := 0; idx < n; idx++ {
brianmcgee marked this conversation as resolved.
Show resolved Hide resolved
filesCh <- files[idx]
}

cancel()

if errors.Is(err, io.EOF) {
// we have finished traversing
break
} else if err != nil {
// something went wrong
log.Errorf("failed to read files: %v", err)
cancel()
break
}
}

// indicate no further files for processing
close(filesCh)

// wait for everything to complete
Expand Down Expand Up @@ -263,6 +264,8 @@ func applyFormatters(
// formatters which should be applied to their respective files
batches := make(map[string][]*format.Task)

// apply check if the given batch key has enough tasks to trigger processing
// flush is used to force processing regardless of the number of tasks
apply := func(key string, flush bool) {
// lookup the batch and exit early if it's empty
batch := batches[key]
Expand Down Expand Up @@ -304,6 +307,7 @@ func applyFormatters(
}
}

// tryApply batches tasks by their batch key and processes the batch if there is enough ready
tryApply := func(task *format.Task) {
// append to batch
key := task.BatchKey
Expand All @@ -314,53 +318,68 @@ func applyFormatters(

return func() error {
defer func() {
// close processed channel
// indicate processing has finished
close(formattedCh)
}()

// parse unmatched log level
unmatchedLevel, err := log.ParseLevel(cfg.OnUnmatched)
if err != nil {
return fmt.Errorf("invalid on-unmatched value: %w", err)
}

// iterate the files channel
// iterate the file channel
for file := range filesCh {

// a list of formatters that match this file
var matches []*format.Formatter

// first check if this file has been globally excluded
if format.PathMatches(file.RelPath, globalExcludes) {
log.Debugf("path matched global excludes: %s", file.RelPath)
// mark it as processed and continue to the next
formattedCh <- &format.Task{
File: file,
} else {
// otherwise, check if any formatters are interested in it
for _, formatter := range formatters {
if formatter.Wants(file) {
matches = append(matches, formatter)
}
}
continue
}

// check if any formatters are interested in this file
var matches []*format.Formatter
for _, formatter := range formatters {
if formatter.Wants(file) {
matches = append(matches, formatter)
}
}
// indicates no further processing
var release bool

// see if any formatters matched
// check if there were no matches
if len(matches) == 0 {

// log that there was no match, exiting with an error if the unmatched level was set to fatal
if unmatchedLevel == log.FatalLevel {
return fmt.Errorf("no formatter for path: %s", file.RelPath)
}

log.Logf(unmatchedLevel, "no formatter for path: %s", file.RelPath)
// mark it as processed and continue to the next
formattedCh <- &format.Task{
File: file,
}

// no further processing
release = true
} else {
// record the match
// record there was a match
statz.Add(stats.Matched, 1)
// create a new format task, add it to a batch based on its batch key and try to apply if the batch is full
task := format.NewTask(file, matches)
tryApply(&task)

// check if the file is new or has changed when compared to the cache entry
if file.Cache == nil || file.Cache.HasChanged(file.Info) {
// if so, generate a format task, add it to the relevant batch (by batch key) and try to process
task := format.NewTask(file, matches)
tryApply(&task)
} else {
// indicate no further processing
release = true
}
}

if release {
// release the file as there is no more processing to be done on it
if err := file.Release(); err != nil {
return fmt.Errorf("failed to release file: %w", err)
}
}
}

Expand Down Expand Up @@ -398,16 +417,20 @@ func postProcessing(
break LOOP
}

// check if the file has changed
// grab the underlying file reference
file := task.File

// check if the file has changed
changed, newInfo, err := file.Stat()
if err != nil {
return err
}

statz.Add(stats.Formatted, 1)

if changed {
// record the change
statz.Add(stats.Formatted, 1)
// record that a change in the underlying file occurred
statz.Add(stats.Changed, 1)

logMethod := log.Debug
if cfg.FailOnChange {
Expand All @@ -434,8 +457,8 @@ func postProcessing(
}
}

// if fail on change has been enabled, check that no files were actually formatted, throwing an error if so
if cfg.FailOnChange && statz.Value(stats.Formatted) != 0 {
// if fail on change has been enabled, check that no files were actually changed, throwing an error if so
if cfg.FailOnChange && statz.Value(stats.Changed) != 0 {
return ErrFailOnChange
}

Expand Down
Loading