github.com/storacha/go-ucanto@v0.7.2/core/result/failure/faillure.go (about) 1 package failure 2 3 import ( 4 "fmt" 5 "runtime" 6 7 "github.com/pkg/errors" 8 "github.com/storacha/go-ucanto/core/ipld" 9 "github.com/storacha/go-ucanto/core/result/failure/datamodel" 10 ) 11 12 // Named is an error that you can read a name from 13 type Named interface { 14 Name() string 15 } 16 17 // WithStackTrace is an error that you can read a stack trace from 18 type WithStackTrace interface { 19 Stack() string 20 } 21 22 // IPLDConvertableError is an error with a custom method to convert to an IPLD Node 23 type IPLDConvertableError interface { 24 error 25 ipld.Builder 26 } 27 28 type Failure interface { 29 error 30 Named 31 } 32 33 type IPLDBuilderFailure interface { 34 IPLDConvertableError 35 Failure 36 } 37 38 type NamedWithStackTrace interface { 39 Named 40 WithStackTrace 41 } 42 43 type namedWithStackTrace struct { 44 name string 45 stack errors.StackTrace 46 } 47 48 func (n namedWithStackTrace) Name() string { 49 return n.name 50 } 51 52 func (n namedWithStackTrace) Stack() string { 53 return fmt.Sprintf("%+v", n.stack) 54 } 55 56 func NamedWithCurrentStackTrace(name string) NamedWithStackTrace { 57 const depth = 32 58 59 var pcs [depth]uintptr 60 n := runtime.Callers(3, pcs[:]) 61 62 f := make(errors.StackTrace, n) 63 for i := 0; i < n; i++ { 64 f[i] = errors.Frame(pcs[i]) 65 } 66 67 return namedWithStackTrace{name, f} 68 } 69 70 type failure struct { 71 model datamodel.FailureModel 72 toIPLD func() (ipld.Node, error) 73 } 74 75 func (f failure) Name() string { 76 if f.model.Name == nil { 77 return "" 78 } 79 return *f.model.Name 80 } 81 82 func (f failure) Message() string { 83 return f.model.Message 84 } 85 86 func (f failure) Error() string { 87 return f.model.Message 88 } 89 90 func (f failure) Stack() string { 91 if f.model.Stack == nil { 92 return "" 93 } 94 return *f.model.Stack 95 } 96 97 func (f failure) ToIPLD() (ipld.Node, error) { 98 if f.toIPLD != nil { 99 return f.toIPLD() 100 } 101 return f.model.ToIPLD() 102 } 103 104 func FromError(err error) IPLDBuilderFailure { 105 model := datamodel.FailureModel{Message: err.Error()} 106 if named, ok := err.(Named); ok { 107 name := named.Name() 108 model.Name = &name 109 } 110 if withStackTrace, ok := err.(WithStackTrace); ok { 111 stack := withStackTrace.Stack() 112 model.Stack = &stack 113 } 114 fail := failure{model: model} 115 if builder, ok := err.(ipld.Builder); ok { 116 fail.toIPLD = builder.ToIPLD 117 } 118 return fail 119 } 120 121 func FromFailureModel(model datamodel.FailureModel) IPLDBuilderFailure { 122 // treat a zero value failure as a non-error 123 if (model == datamodel.FailureModel{}) { 124 return nil 125 } 126 return failure{model: model} 127 }