<commit_before>package main

import (
	"fmt"
	"os"
	"path"
	"strings"

	"github.com/vito/gocart/dependency"
	"github.com/vito/gocart/dependency_fetcher"
	"github.com/vito/gocart/locker"
)

type Reason interface {
	Description() string
}

type VersionMismatch struct {
	Expected string
	Current  string
}

func (self VersionMismatch) Description() string {
	return fmt.Sprintf("version mismatch:\n  want %s\n  have %s\n", red(self.Expected), green(self.Current))
}

type DirtyState struct {
	Output string
}

func (self DirtyState) Description() string {
	output := ""

	for _, line := range strings.Split(strings.Trim(self.Output, " \n"), "\n") {
		output = output + "  " + line + "\n"
	}

	return fmt.Sprintf("dirty state:\n%s", output)
}

func check(root string) {
	dirtyDependencies := findDirtyDependencies(root)

	if len(dirtyDependencies) > 0 {
		count := 0

		for path, reason := range dirtyDependencies {
			fmt.Println(bold(path))
			fmt.Println(reason.Description())

			count++
		}

		os.Exit(1)
	}
}

func findDirtyDependencies(root string) map[string]Reason {
	requestedDependencies := loadFile(path.Join(root, CartridgeFile))
	lockedDependencies := loadFile(path.Join(root, CartridgeLockFile))

	dependencies := locker.GenerateLock(requestedDependencies, lockedDependencies)

	dirtyDependencies := make(map[string]Reason)

	for _, dep := range dependencies {
		reason := checkForDirtyState(dep)

		if reason != nil {
			dirtyDependencies[dep.FullPath(GOPATH)] = reason
		}

		for dep, reason := range findDirtyDependencies(dep.FullPath(GOPATH)) {
			dirtyDependencies[dep] = reason
		}
	}

	return dirtyDependencies
}

func checkForDirtyState(dep dependency.Dependency) Reason {
	repoPath := dep.FullPath(GOPATH)

	if _, err := os.Stat(repoPath); os.IsNotExist(err) {
		return nil
	}

	currentVersion := findCurrentVersion(dep)

	if currentVersion != dep.Version {
		return VersionMismatch{
			Expected: dep.Version,
			Current:  currentVersion,
		}
	}

	repo, err := dependency_fetcher.NewRepository(repoPath)
	if err != nil {
		fatal(err.Error())
	}

	status := repo.StatusCommand()
	status.Dir = repoPath

	output, err := status.Output()
	if err != nil {
		fatal(err.Error())
	}

	// Bazaar is bizarre
	if string(output) == "working tree is out of date, run 'bzr update'\n" {
		return nil
	}

	if len(output) != 0 {
		return DirtyState{
			Output: string(output),
		}
	}

	return nil
}<commit_msg>Favor displaying dirty repos over mismatched shas<commit_after>package main

import (
	"fmt"
	"os"
	"path"
	"strings"

	"github.com/vito/gocart/dependency"
	"github.com/vito/gocart/dependency_fetcher"
	"github.com/vito/gocart/locker"
)

type Reason interface {
	Description() string
}

type VersionMismatch struct {
	Expected string
	Current  string
}

func (self VersionMismatch) Description() string {
	return fmt.Sprintf("version mismatch:\n  want %s\n  have %s\n", red(self.Expected), green(self.Current))
}

type DirtyState struct {
	Output string
}

func (self DirtyState) Description() string {
	output := ""

	for _, line := range strings.Split(strings.Trim(self.Output, " \n"), "\n") {
		output = output + "  " + line + "\n"
	}

	return fmt.Sprintf("dirty state:\n%s", output)
}

func check(root string) {
	dirtyDependencies := findDirtyDependencies(root)

	if len(dirtyDependencies) > 0 {
		count := 0

		for path, reason := range dirtyDependencies {
			fmt.Println(bold(path))
			fmt.Println(reason.Description())

			count++
		}

		os.Exit(1)
	}
}

func findDirtyDependencies(root string) map[string]Reason {
	requestedDependencies := loadFile(path.Join(root, CartridgeFile))
	lockedDependencies := loadFile(path.Join(root, CartridgeLockFile))

	dependencies := locker.GenerateLock(requestedDependencies, lockedDependencies)

	dirtyDependencies := make(map[string]Reason)

	for _, dep := range dependencies {
		reason := checkForDirtyState(dep)

		if reason != nil {
			dirtyDependencies[dep.FullPath(GOPATH)] = reason
		}

		for dep, reason := range findDirtyDependencies(dep.FullPath(GOPATH)) {
			dirtyDependencies[dep] = reason
		}
	}

	return dirtyDependencies
}

func checkForDirtyState(dep dependency.Dependency) Reason {
	repoPath := dep.FullPath(GOPATH)

	if _, err := os.Stat(repoPath); os.IsNotExist(err) {
		return nil
	}

	repo, err := dependency_fetcher.NewRepository(repoPath)
	if err != nil {
		fatal(err.Error())
	}

	status := repo.StatusCommand()
	status.Dir = repoPath

	output, err := status.Output()
	if err != nil {
		fatal(err.Error())
	}

	// Bazaar is bizarre
	if string(output) == "working tree is out of date, run 'bzr update'\n" {
		return nil
	}

	if len(output) != 0 {
		return DirtyState{
			Output: string(output),
		}
	}

	currentVersion := findCurrentVersion(dep)

	if currentVersion != dep.Version {
		return VersionMismatch{
			Expected: dep.Version,
			Current:  currentVersion,
		}
	}

	return nil
}
{'fraction_non_alphanumeric': 0.12952302631578946, 'fraction_numerical': 0.001644736842105263, 'mean_word_length': 4.29379760609358, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '839898', 'n_tokens_mistral': 1790, 'n_tokens_neox': 1612, 'n_words': 368}