« Back to Index

Go: Simple line-by-line diff in Go

View original Gist on GitHub

Tags: #go #utility

diff.go

// Probably should just use https://github.com/sergi/go-diff

// printDiff prints the differences between two files line by line.
func printDiff(file1, file2 string) error {
	f1, err := os.Open(file1)
	if err != nil {
		return fmt.Errorf("failed to open file1: %w", err)
	}
	defer f1.Close()

	f2, err := os.Open(file2)
	if err != nil {
		return fmt.Errorf("failed to open file2: %w", err)
	}
	defer f2.Close()

	scanner1 := bufio.NewScanner(f1)
	scanner2 := bufio.NewScanner(f2)

	lineNumber := 1
	for scanner1.Scan() || scanner2.Scan() {
		var line1, line2 string
		if scanner1.Scan() {
			line1 = scanner1.Text()
		}
		if scanner2.Scan() {
			line2 = scanner2.Text()
		}

		if line1 != line2 {
			fmt.Printf("Line %d:\n", lineNumber)
			fmt.Printf("- %s\n", line1)
			fmt.Printf("+ %s\n", line2)
		}
		lineNumber++
	}

	if err := scanner1.Err(); err != nil {
		return fmt.Errorf("error reading file1: %w", err)
	}
	if err := scanner2.Err(); err != nil {
		return fmt.Errorf("error reading file2: %w", err)
	}

	return nil
}

// printDiff prints the differences between two files using the standard diff
// CLI installed on the host machine.
func printDiff(file1, file2 string, logger *slog.Logger) error {
	cmd := exec.Command("diff", file1, file2)
	output, err := cmd.CombinedOutput()
	if err != nil {
		// NOTE: See `EXIT STATUS` in `man diff`.
		// 0:  No differences were found.
		// 1:  Differences were found.
		// >1: An error occurred.
		var exitErr *exec.ExitError
		if errors.As(err, &exitErr) && exitErr.ExitCode() == 1 {
			fmt.Printf("%s\n", output)
			return nil
		}
		// Actual error occurred.
		err = fmt.Errorf("failed to run diff command: %w", err)
		logger.LogAttrs(context.Background(), slog.LevelError, "diff_files", slog.Any("error", err))
		return err
	}
	return nil
}