Skip to content

Commit

Permalink
Merge pull request campoy#15 from campoy/newapi
Browse files Browse the repository at this point in the history
new API because @rakyll said so
  • Loading branch information
campoy authored Sep 1, 2017
2 parents f841641 + 9cb3dff commit 521dac5
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 64 deletions.
8 changes: 3 additions & 5 deletions imgcat/example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,25 +14,23 @@
package imgcat_test

import (
"io"
"log"
"os"

"github.com/campoy/tools/imgcat"
)

func ExampleNew() {
w, err := imgcat.New(os.Stdout, imgcat.Width(imgcat.Pixels(100)), imgcat.Inline(true), imgcat.Name("smiley.png"))
func ExampleNewEncoder() {
enc, err := imgcat.NewEncoder(os.Stdout, imgcat.Width(imgcat.Pixels(100)), imgcat.Inline(true), imgcat.Name("smiley.png"))
if err != nil {
log.Fatal(err)
}
defer w.Close()

f, err := os.Open("testdata/icon.png")
if err != nil {
log.Fatal(err)
}
defer f.Close()

io.Copy(w, f) // this will display the image in the terminal
enc.Encode(f) // this will display the image in the terminal
}
62 changes: 27 additions & 35 deletions imgcat/imgcat.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,58 +95,50 @@ var isSupported = func() bool {
return os.Getenv("TERM_PROGRAM") == "iTerm.app"
}

// New returns a writer that encodes an image for iterm2.
func New(w io.Writer, options ...Option) (io.WriteCloser, error) {
// NewEncoder returns a encoder that encodes images for iterm2.
func NewEncoder(w io.Writer, options ...Option) (*Encoder, error) {
if !IsSupported() {
return nil, fmt.Errorf("imgcat is only supported with iTerm2")
}

pr, pw := io.Pipe()
res := &writer{
encoder: base64.NewEncoder(base64.StdEncoding, pw),
pipeWriter: pw,
done: make(chan struct{}),
}
go res.copy(w, pr, options...)

return res, nil
}

type writer struct {
encoder io.WriteCloser
pipeWriter *io.PipeWriter
enc := &Encoder{out: w, options: options}

done chan struct{}
return enc, nil
}

func (w *writer) Write(p []byte) (int, error) { return w.encoder.Write(p) }

func (w *writer) Close() error {
if err := w.encoder.Close(); err != nil {
return fmt.Errorf("could not close base64 encoder: %v", err)
}
if err := w.pipeWriter.Close(); err != nil {
return fmt.Errorf("could not close pipe writer: %v", err)
}
<-w.done
return nil
// An Encoder is used to encode images to iterm2.
type Encoder struct {
out io.Writer
options []Option
}

func (w *writer) copy(out io.Writer, pr *io.PipeReader, options ...Option) {
defer close(w.done)

// Encode encodes the given image into the output.
func (enc *Encoder) Encode(r io.Reader) error {
header := new(bytes.Buffer)
fmt.Fprint(header, "\x1b]1337;File=")
for i, option := range options {
for i, option := range enc.options {
fmt.Fprintf(header, "%s", option)
if i < len(options)-1 {
if i < len(enc.options)-1 {
fmt.Fprintf(header, ";")
}
}
fmt.Fprintf(header, ":")

pr, pw := io.Pipe()
go func() {
enc := base64.NewEncoder(base64.StdEncoding, pw)
defer enc.Close()

_, err := io.Copy(enc, r)
if err != nil {
pw.CloseWithError(err)
} else {
pw.CloseWithError(enc.Close())
}
}()

footer := bytes.NewBufferString("\a\n")

_, err := io.Copy(out, io.MultiReader(header, pr, footer))
pr.CloseWithError(err)
_, err := io.Copy(enc.out, io.MultiReader(header, pr, footer))
return err
}
27 changes: 13 additions & 14 deletions imgcat/imgcat/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ package main

import (
"fmt"
"io"
"os"

"github.com/campoy/tools/imgcat"
Expand All @@ -28,27 +27,27 @@ func main() {
os.Exit(1)
}

enc, err := imgcat.NewEncoder(os.Stdout,
imgcat.Inline(true),
imgcat.Width(imgcat.Percent(100)))
if err != nil {
fmt.Fprintf(os.Stderr, "%s\n", err)
os.Exit(1)
}

for _, path := range os.Args[1:] {
if err := cat(path); err != nil {
if err := cat(enc, path); err != nil {
fmt.Fprintf(os.Stderr, "%s\n", err)
}
}
}

func cat(path string) error {
w, err := imgcat.New(os.Stdout,
imgcat.Inline(true),
imgcat.Width(imgcat.Percent(100)))
if err != nil {
return err
}
defer w.Close()

func cat(enc *imgcat.Encoder, path string) error {
f, err := os.Open(path)
if err != nil {
return errors.Wrapf(err, "could not open %s", path)
}
//defer f.Close()
_, err = io.Copy(w, f)
return err
defer f.Close()

return enc.Encode(f)
}
17 changes: 7 additions & 10 deletions imgcat/imgcat_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,19 @@ import (
"bytes"
"fmt"
"os"
"strings"
"testing"
)

func TestIsSupported(t *testing.T) {
defer func(old string) { os.Setenv("TERM_PROGRAM", old) }(os.Getenv("TERM_PROGRAM"))
os.Setenv("TERM_PROGRAM", "foo")
if _, err := New(nil); err == nil {
if _, err := NewEncoder(nil); err == nil {
t.Fatal("imgcat should not be supported now")
}
}

func TestNew(t *testing.T) {
func TestEncode(t *testing.T) {
// Change is supported to be always true and restore at the end.
defer func(old func() bool) { isSupported = old }(isSupported)
isSupported = func() bool { return true }
Expand Down Expand Up @@ -49,17 +50,13 @@ func TestNew(t *testing.T) {
for _, tt := range tc {
t.Run(tt.name, func(t *testing.T) {
var buf bytes.Buffer
w, err := New(&buf, tt.options...)
enc, err := NewEncoder(&buf, tt.options...)
if err != nil {
t.Fatalf("could not create writer: %v", err)
}
_, err = fmt.Fprint(w, tt.in)
if err != nil {
if err := enc.Encode(strings.NewReader(tt.in)); err != nil {
t.Fatalf("could not write: %v", err)
}
if err := w.Close(); err != nil {
t.Fatalf("could not close: %v", err)
}
if got := buf.String(); got != tt.out {
t.Fatalf("expected output %q; got %q", tt.out, got)
}
Expand All @@ -78,11 +75,11 @@ func TestWriter(t *testing.T) {
defer func(old func() bool) { isSupported = old }(isSupported)
isSupported = func() bool { return true }

w, err := New(badWriter{})
enc, err := NewEncoder(badWriter{})
if err != nil {
t.Fatalf("could not create writer: %v", err)
}
_, err = fmt.Fprint(w, "test")
err = enc.Encode(strings.NewReader("test"))
if err == nil {
t.Fatalf("expected error; got nothing")
}
Expand Down

0 comments on commit 521dac5

Please sign in to comment.