packer-cn/packer/ui.go

227 lines
4.6 KiB
Go
Raw Normal View History

package packer
import (
"bytes"
"errors"
"fmt"
"io"
2013-05-21 14:40:07 -04:00
"log"
2013-06-14 18:59:54 -04:00
"os"
"os/signal"
2013-07-31 17:43:34 -04:00
"runtime"
"strings"
"sync"
"unicode"
)
2013-06-03 16:35:43 -04:00
type UiColor uint
const (
UiColorRed UiColor = 31
UiColorGreen = 32
UiColorYellow = 33
UiColorBlue = 34
UiColorMagenta = 35
UiColorCyan = 36
)
// The Ui interface handles all communication for Packer with the outside
// world. This sort of control allows us to strictly control how output
// is formatted and various levels of output.
type Ui interface {
Ask(string) (string, error)
Say(string)
2013-06-03 14:30:38 -04:00
Message(string)
Error(string)
2013-08-11 21:16:00 -04:00
Machine(string, ...string)
}
2013-06-03 16:35:43 -04:00
// ColoredUi is a UI that is colored using terminal colors.
type ColoredUi struct {
2013-06-12 13:41:58 -04:00
Color UiColor
ErrorColor UiColor
Ui Ui
2013-06-03 16:35:43 -04:00
}
2013-05-21 16:20:51 -04:00
// PrefixedUi is a UI that wraps another UI implementation and adds a
// prefix to all the messages going out.
type PrefixedUi struct {
2013-06-03 14:30:38 -04:00
SayPrefix string
MessagePrefix string
Ui Ui
2013-05-21 16:20:51 -04:00
}
// The ReaderWriterUi is a UI that writes and reads from standard Go
// io.Reader and io.Writer.
type ReaderWriterUi struct {
Reader io.Reader
Writer io.Writer
l sync.Mutex
interrupted bool
}
func (u *ColoredUi) Ask(query string) (string, error) {
return u.Ui.Ask(u.colorize(query, u.Color, true))
}
2013-06-03 16:35:43 -04:00
func (u *ColoredUi) Say(message string) {
2013-06-12 13:41:58 -04:00
u.Ui.Say(u.colorize(message, u.Color, true))
2013-06-03 16:35:43 -04:00
}
func (u *ColoredUi) Message(message string) {
2013-06-12 13:41:58 -04:00
u.Ui.Message(u.colorize(message, u.Color, false))
2013-06-03 16:35:43 -04:00
}
func (u *ColoredUi) Error(message string) {
2013-06-12 13:41:58 -04:00
color := u.ErrorColor
if color == 0 {
color = UiColorRed
}
u.Ui.Error(u.colorize(message, color, true))
2013-06-03 16:35:43 -04:00
}
2013-08-11 21:16:00 -04:00
func (u *ColoredUi) Machine(t string, args ...string) {
// Don't colorize machine-readable output
u.Ui.Machine(t, args...)
}
2013-06-12 13:41:58 -04:00
func (u *ColoredUi) colorize(message string, color UiColor, bold bool) string {
if !u.supportsColors() {
return message
}
attr := 0
if bold {
attr = 1
}
2013-06-12 13:41:58 -04:00
return fmt.Sprintf("\033[%d;%d;40m%s\033[0m", attr, color, message)
2013-06-03 16:35:43 -04:00
}
func (u *ColoredUi) supportsColors() bool {
// For now, on non-Windows machine, just assume it does
if runtime.GOOS != "windows" {
return true
}
// On Windows, if we appear to be in Cygwin, then it does
cygwin := os.Getenv("CYGWIN") != "" ||
os.Getenv("OSTYPE") == "cygwin" ||
os.Getenv("TERM") == "cygwin"
return cygwin
}
func (u *PrefixedUi) Ask(query string) (string, error) {
return u.Ui.Ask(u.prefixLines(u.SayPrefix, query))
}
func (u *PrefixedUi) Say(message string) {
u.Ui.Say(u.prefixLines(u.SayPrefix, message))
2013-06-03 14:30:38 -04:00
}
func (u *PrefixedUi) Message(message string) {
u.Ui.Message(u.prefixLines(u.MessagePrefix, message))
2013-05-21 16:20:51 -04:00
}
func (u *PrefixedUi) Error(message string) {
u.Ui.Error(u.prefixLines(u.SayPrefix, message))
}
2013-08-11 21:16:00 -04:00
func (u *PrefixedUi) Machine(t string, args ...string) {
// Just pass it through for now.
u.Ui.Machine(t, args...)
}
func (u *PrefixedUi) prefixLines(prefix, message string) string {
var result bytes.Buffer
for _, line := range strings.Split(message, "\n") {
result.WriteString(fmt.Sprintf("%s: %s\n", prefix, line))
}
return strings.TrimRightFunc(result.String(), unicode.IsSpace)
2013-05-21 16:20:51 -04:00
}
func (rw *ReaderWriterUi) Ask(query string) (string, error) {
rw.l.Lock()
defer rw.l.Unlock()
if rw.interrupted {
return "", errors.New("interrupted")
}
2013-06-14 18:59:54 -04:00
sigCh := make(chan os.Signal, 1)
signal.Notify(sigCh, os.Interrupt)
defer signal.Stop(sigCh)
log.Printf("ui: ask: %s", query)
if query != "" {
if _, err := fmt.Fprint(rw.Writer, query+" "); err != nil {
return "", err
}
}
2013-06-14 18:59:54 -04:00
result := make(chan string, 1)
go func() {
var line string
if _, err := fmt.Fscanln(rw.Reader, &line); err != nil {
log.Printf("ui: scan err: %s", err)
}
2013-06-14 18:59:54 -04:00
result <- line
}()
select {
case line := <-result:
return line, nil
case <-sigCh:
// Print a newline so that any further output starts properly
// on a new line.
fmt.Fprintln(rw.Writer)
// Mark that we were interrupted so future Ask calls fail.
rw.interrupted = true
return "", errors.New("interrupted")
2013-06-14 18:59:54 -04:00
}
}
func (rw *ReaderWriterUi) Say(message string) {
rw.l.Lock()
defer rw.l.Unlock()
log.Printf("ui: %s", message)
_, err := fmt.Fprint(rw.Writer, message+"\n")
2013-05-08 20:09:10 -04:00
if err != nil {
panic(err)
}
}
2013-05-08 18:12:48 -04:00
2013-06-03 14:30:38 -04:00
func (rw *ReaderWriterUi) Message(message string) {
rw.l.Lock()
defer rw.l.Unlock()
2013-06-03 14:30:38 -04:00
log.Printf("ui: %s", message)
_, err := fmt.Fprint(rw.Writer, message+"\n")
2013-06-03 14:30:38 -04:00
if err != nil {
panic(err)
}
}
func (rw *ReaderWriterUi) Error(message string) {
rw.l.Lock()
defer rw.l.Unlock()
log.Printf("ui error: %s", message)
_, err := fmt.Fprint(rw.Writer, message+"\n")
2013-05-08 20:09:10 -04:00
if err != nil {
panic(err)
}
2013-05-08 18:12:48 -04:00
}
2013-08-11 21:16:00 -04:00
func (rw *ReaderWriterUi) Machine(t string, args ...string) {
// TODO
}