1
0
mirror of https://github.com/openshift/installer.git synced 2026-02-06 18:47:19 +01:00
Files
installer/vendor/github.com/pkg/diff/diff.go
2022-12-06 03:29:24 -05:00

139 lines
4.5 KiB
Go

// Package diff contains high level routines that generate a textual diff.
//
// It is implemented in terms of the other packages in this module.
// If you want fine-grained control,
// want to inspect a diff programmatically,
// want to provide a context for cancellation,
// need to diff gigantic files that don't fit in memory,
// or want to diff unusual things,
// use the lower level packages.
package diff
import (
"bufio"
"bytes"
"context"
"fmt"
"io"
"os"
"reflect"
"strings"
"github.com/pkg/diff/ctxt"
"github.com/pkg/diff/intern"
"github.com/pkg/diff/myers"
"github.com/pkg/diff/write"
)
// lines returns the lines contained in text/filename.
// text and filename are interpreted as described in the docs for Text.
func lines(m intern.Strings, filename string, text interface{}) ([]*string, error) {
var r io.Reader
switch text := text.(type) {
case nil:
f, err := os.Open(filename)
if err != nil {
return nil, err
}
defer f.Close()
r = f
case string:
r = strings.NewReader(text)
case []byte:
r = bytes.NewReader(text)
case io.Reader:
r = text
default:
return nil, fmt.Errorf("unexpected type %T, want string, []byte, io.Reader, or nil", text)
}
var x []*string
scan := bufio.NewScanner(r)
for scan.Scan() {
x = append(x, m.FromBytes(scan.Bytes()))
}
return x, scan.Err()
}
// addNames adds a Names write.Option using aName and bName,
// taking care to put it at the end,
// so as not to overwrite any competing option.
func addNames(aName, bName string, options []write.Option) []write.Option {
opts := make([]write.Option, len(options)+1)
opts[0] = write.Names(aName, bName)
copy(opts[1:], options)
return opts
}
// Text diffs a and b and writes the result to w.
// It treats a and b as text, and splits their contents
// into lines using bufio.ScanLines.
// aFile and bFile are filenames to use in the output.
//
// a and b each may be nil or may have type string, []byte, or io.Reader.
// If nil, the text is read from the filename.
func Text(aFile, bFile string, a, b interface{}, w io.Writer, options ...write.Option) error {
m := make(intern.Strings)
aLines, err := lines(m, aFile, a)
if err != nil {
return err
}
bLines, err := lines(m, bFile, b)
if err != nil {
return err
}
ab := &diffStrings{a: aLines, b: bLines}
s := myers.Diff(context.Background(), ab)
s = ctxt.Size(s, 3)
opts := addNames(aFile, bFile, options)
err = write.Unified(s, w, ab, opts...)
return err
}
type diffStrings struct {
a, b []*string
}
func (ab *diffStrings) LenA() int { return len(ab.a) }
func (ab *diffStrings) LenB() int { return len(ab.b) }
func (ab *diffStrings) Equal(ai, bi int) bool { return ab.a[ai] == ab.b[bi] }
func (ab *diffStrings) WriteATo(w io.Writer, i int) (int, error) { return io.WriteString(w, *ab.a[i]) }
func (ab *diffStrings) WriteBTo(w io.Writer, i int) (int, error) { return io.WriteString(w, *ab.b[i]) }
// Slices diffs slices a and b and writes the result to w.
// It uses fmt.Print to print the elements of a and b.
// It uses reflect.DeepEqual to compare elements of a and b.
// It uses aName and bName as the names of a and b in the output.
func Slices(aName, bName string, a, b interface{}, w io.Writer, options ...write.Option) error {
ab := &diffSlices{a: reflect.ValueOf(a), b: reflect.ValueOf(b)}
if err := ab.validateTypes(); err != nil {
return err
}
s := myers.Diff(context.Background(), ab)
s = ctxt.Size(s, 3)
opts := addNames(aName, bName, options)
err := write.Unified(s, w, ab, opts...)
return err
}
type diffSlices struct {
a, b reflect.Value
}
func (ab *diffSlices) LenA() int { return ab.a.Len() }
func (ab *diffSlices) LenB() int { return ab.b.Len() }
func (ab *diffSlices) atA(i int) interface{} { return ab.a.Index(i).Interface() }
func (ab *diffSlices) atB(i int) interface{} { return ab.b.Index(i).Interface() }
func (ab *diffSlices) Equal(ai, bi int) bool { return reflect.DeepEqual(ab.atA(ai), ab.atB(bi)) }
func (ab *diffSlices) WriteATo(w io.Writer, i int) (int, error) { return fmt.Fprint(w, ab.atA(i)) }
func (ab *diffSlices) WriteBTo(w io.Writer, i int) (int, error) { return fmt.Fprint(w, ab.atB(i)) }
func (ab *diffSlices) validateTypes() error {
if t := ab.a.Type(); t.Kind() != reflect.Slice {
return fmt.Errorf("a has type %v, must be a slice", t)
}
if t := ab.b.Type(); t.Kind() != reflect.Slice {
return fmt.Errorf("b has type %v, must be a slice", t)
}
return nil
}