elvish/eval/error.go
2016-02-20 21:12:54 +01:00

136 lines
2.5 KiB
Go

package eval
import (
"bytes"
"fmt"
"strings"
"github.com/elves/elvish/parse"
)
// Error represents runtime errors in elvish constructs.
type Error struct {
Inner error
}
func (Error) Kind() string {
return "error"
}
func (e Error) Repr(indent int) string {
if e.Inner == nil {
return "$ok"
}
if r, ok := e.Inner.(Reprer); ok {
return r.Repr(indent)
}
return "?(error " + parse.Quote(e.Inner.Error()) + ")"
}
func (e Error) Bool() bool {
return e.Inner == nil
}
// OK is an alias for the zero value of Error.
var OK = Error{nil}
// multiError is multiple errors packed into one. It is used for reporting
// errors of pipelines, in which multiple forms may error.
type MultiError struct {
Errors []Error
}
func (me MultiError) Repr(indent int) string {
// TODO Make a more generalized ListReprBuilder and use it here.
b := new(bytes.Buffer)
b.WriteString("?(multi-error")
elemIndent := indent + len("?(multi-error ")
for _, e := range me.Errors {
if indent > 0 {
b.WriteString("\n" + strings.Repeat(" ", elemIndent))
} else {
b.WriteString(" ")
}
b.WriteString(e.Repr(elemIndent))
}
b.WriteString(")")
return b.String()
}
func (me MultiError) Error() string {
b := new(bytes.Buffer)
b.WriteString("(")
for i, e := range me.Errors {
if i > 0 {
b.WriteString(" | ")
}
if e.Inner == nil {
b.WriteString("<nil>")
} else {
b.WriteString(e.Inner.Error())
}
}
b.WriteString(")")
return b.String()
}
func newMultiError(es ...Error) Error {
return Error{MultiError{es}}
}
// Flow is a special type of Error used for control flows.
type flow uint
// Control flows.
const (
Return flow = iota
Break
Continue
)
var flowNames = [...]string{
"return", "break", "continue",
}
func (f flow) Repr(int) string {
return "?(" + f.Error() + ")"
}
func (f flow) Error() string {
if f >= flow(len(flowNames)) {
return fmt.Sprintf("!(BAD FLOW: %v)", f)
}
return flowNames[f]
}
func allok(es []Error) bool {
for _, e := range es {
if e.Inner != nil {
return false
}
}
return true
}
// PprintError pretty prints an error. It understands specialized error types
// defined in this package.
func PprintError(e error) {
switch e := e.(type) {
case nil:
fmt.Print("\033[32mok\033[m")
case MultiError:
fmt.Print("(")
for i, c := range e.Errors {
if i > 0 {
fmt.Print(" | ")
}
PprintError(c.Inner)
}
fmt.Print(")")
case flow:
fmt.Print("\033[33m" + e.Error() + "\033[m")
default:
fmt.Print("\033[31;1m" + e.Error() + "\033[m")
}
}