mirror of
https://github.com/openshift/installer.git
synced 2026-02-06 18:47:19 +01:00
139 lines
4.5 KiB
Go
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
|
|
}
|