#!/bin/bash
# This scipt lints go files for common errors.
#
# Its runs gofmt and go vet, and optionally golint and
# gocyclo, if they are installed.
#
# With no arguments, it lints the current files staged
# for git commit.  Or you can pass it explicit filenames
# (or directories) and it will lint them.
#
# To use this script automatically, run:
#   ln -s ../../bin/lint .git/hooks/pre-commit

set -eu

function lint_go {
	filename="$1"
	result=0

	if [ -n "$(gofmt -s -l "${filename}")" ]; then
		result=1
		echo "${filename}: run gofmt -s -w ${filename}!"
	fi

	go tool vet "${filename}" || result=$?

	# golint is completely optional.  If you don't like it
	# don't have it installed.
	if type golint >/dev/null 2>&1; then
		# golint doesn't set an exit code it seems
		lintoutput=$(golint "${filename}")
		if [ "$lintoutput" != "" ]; then
			result=1
			echo "$lintoutput"
		fi
	fi

	# gocyclo is completely optional.  If you don't like it
	# don't have it installed.  Also never blocks a commit,
	# it just warns.
	if type gocyclo >/dev/null 2>&1; then
		gocyclo -over 25 "${filename}" | while read line; do
			echo "${filename}": higher than 25 cyclomatic complexity - "${line}"
		done
	fi

	return $result
}

function lint {
	filename="$1"
	ext="${filename##*\.}"

	# Don't lint deleted files
	if [ ! -f "$filename" ]; then
		return
	fi

	# Don't lint this script or static.go
	case "${filename}" in
	bin/lint) return;;
	./app/static.go) return;;
	esac

	case "$ext" in
		go) lint_go "${filename}"
		;;
	esac
}

function lint_files {
	lint_result=0
	while read filename; do
		lint "${filename}" || lint_result=1
	done
	exit $lint_result
}

function list_files {
	if [ $# -gt 0 ]; then
		find "$@" -type f
	else
		git diff --cached --name-only
	fi
}

list_files "$@" | lint_files
