Skip to content

Commit

Permalink
remove adapters, create wrappers
Browse files Browse the repository at this point in the history
package diff now has a simple, single-call API.
for more control, people can use the subpackages.

Part of #18.
  • Loading branch information
josharian committed Dec 30, 2019
1 parent a78b239 commit 0a511de
Show file tree
Hide file tree
Showing 7 changed files with 211 additions and 199 deletions.
103 changes: 0 additions & 103 deletions adapter.go

This file was deleted.

47 changes: 4 additions & 43 deletions cmd/pkg-diff-example/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,16 @@
package main

import (
"bufio"
"context"
"flag"
"fmt"
"log"
"os"

"github.com/pkg/diff"
"github.com/pkg/diff/ctxt"
"github.com/pkg/diff/myers"
"github.com/pkg/diff/write"
)

var (
color = flag.Bool("color", false, "colorize the output")
timeout = flag.Duration("timeout", 0, "timeout")
unified = flag.Int("unified", 3, "lines of unified context")
)
var color = flag.Bool("color", false, "colorize the output")

// check logs a fatal error and exits if err is not nil.
func check(err error) {
Expand All @@ -29,22 +21,6 @@ func check(err error) {
}
}

// fileLines returns the lines of the file called name.
func fileLines(name string) ([]string, error) {
f, err := os.Open(name)
if err != nil {
return nil, err
}
defer f.Close()

var lines []string
s := bufio.NewScanner(f)
for s.Scan() {
lines = append(lines, s.Text())
}
return lines, s.Err()
}

func usage() {
fmt.Fprintf(os.Stderr, "pkg-diff-example [flags] file1 file2\n")
flag.PrintDefaults()
Expand All @@ -62,28 +38,13 @@ func main() {
}

aName := flag.Arg(0)
aLines, err := fileLines(aName)
check(err)

bName := flag.Arg(1)
bLines, err := fileLines(bName)
check(err)

ab := diff.Strings(aLines, bLines)
ctx := context.Background()
if *timeout != 0 {
var cancel context.CancelFunc
ctx, cancel = context.WithTimeout(ctx, *timeout)
defer cancel()
}
e := myers.Diff(ctx, ab)
e = ctxt.Size(e, *unified) // limit amount of output context
opts := []write.Option{
write.Names(aName, bName),
}
var opts []write.Option
if *color {
opts = append(opts, write.TerminalColor())
}
_, err = write.Unified(e, os.Stdout, ab, opts...)

err := diff.Text(aName, bName, nil, nil, os.Stdout, opts...)
check(err)
}
135 changes: 135 additions & 0 deletions diff.go
Original file line number Diff line number Diff line change
@@ -1 +1,136 @@
// Package diff and its subpackages create, modify, and print diffs.
//
// Package diff contains high level routines that generate a textual diff.
// It is implemented in terms of the diff/* subpackages.
// If you want fine-grained control,
// want to inspect an 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 subpackages.
package diff

import (
"bufio"
"bytes"
"context"
"fmt"
"io"
"os"
"reflect"
"strings"

"github.com/pkg/diff/ctxt"
"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(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() {
// TODO: intern? See intern comment in todo.go.
x = append(x, scan.Text())
}
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 them 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 {
aLines, err := lines(aFile, a)
if err != nil {
return err
}
bLines, err := lines(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
}
10 changes: 0 additions & 10 deletions doc.go

This file was deleted.

Loading

0 comments on commit 0a511de

Please sign in to comment.