// Copyright 2019 Dolthub, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package errhand import ( "fmt" "strings" "github.com/fatih/color" ) type DErrorBuilder struct { dispMsg string details string cause error printUsage bool } func BuildDError(dispFmt string, args ...interface{}) *DErrorBuilder { dispMsg := dispFmt if len(args) > 0 { dispMsg = fmt.Sprintf(dispFmt, args...) } return &DErrorBuilder{dispMsg, "", nil, false} } func BuildIf(err error, dispFmt string, args ...interface{}) *DErrorBuilder { if err == nil { return nil } dispMsg := dispFmt if len(args) > 0 { dispMsg = fmt.Sprintf(dispFmt, args...) } return &DErrorBuilder{dispMsg, "", err, false} } func (builder *DErrorBuilder) AddDetails(detailsFmt string, args ...interface{}) *DErrorBuilder { if builder == nil { return nil } var details string if len(args) > 0 { details = fmt.Sprintf(detailsFmt, args...) } else { details = detailsFmt } if len(builder.details) > 0 { builder.details += "\n" } builder.details += details return builder } func (builder *DErrorBuilder) AddCause(cause error) *DErrorBuilder { if builder == nil { return nil } builder.cause = cause return builder } func (builder *DErrorBuilder) SetPrintUsage() *DErrorBuilder { if builder == nil { return nil } builder.printUsage = true return builder } func (builder *DErrorBuilder) Build() VerboseError { if builder == nil { return nil } return &DError{builder.dispMsg, builder.details, builder.cause, builder.printUsage} } type DError struct { displayMsg string details string cause error printUsage bool } // VerboseErrorFromError returns a verbose error using the error given. If the error given is already a VerboseError, returns it. Otherwise, // creates a new VerboseError with the given error's error string. func VerboseErrorFromError(err error) VerboseError { if err == nil { return nil } if verr, ok := err.(VerboseError); ok { return verr } builder := &DErrorBuilder{err.Error(), "", nil, false} return builder.Build() } func NewDError(dispMsg, details string, cause error, printUsage bool) *DError { return &DError{dispMsg, details, cause, printUsage} } func (derr *DError) Error() string { return color.RedString(derr.displayMsg) } func (derr *DError) Verbose() string { sections := make([]string, 0, 6) if derr.displayMsg != "" { sections = append(sections, derr.Error()) } if derr.cause != nil { var causeStr string if vCause, ok := derr.cause.(VerboseError); ok { causeStr = vCause.Verbose() } else { causeStr = derr.cause.Error() } indentStr := indent(causeStr, " ") indentStrBytes := []byte(indentStr) copy(indentStrBytes, "cause:") sections = append(sections, string(indentStrBytes)) } if derr.details != "" { sections = append(sections, derr.details) } return strings.Join(sections, "\n") } func (derr *DError) ShouldPrintUsage() bool { return derr.printUsage } func indent(str, indentStr string) string { lines := strings.Split(str, "\n") return indentStr + strings.Join(lines, "\n"+indentStr) }