@@ -33,18 +33,24 @@ func EncodeError(ctx context.Context, err error) EncodedError {
33
33
if cause := UnwrapOnce (err ); cause != nil {
34
34
return encodeWrapper (ctx , err , cause )
35
35
}
36
- // Not a causer.
37
- return encodeLeaf (ctx , err )
36
+ return encodeLeaf (ctx , err , UnwrapMulti (err ))
38
37
}
39
38
40
- // encodeLeaf encodes a leaf error.
41
- func encodeLeaf (ctx context.Context , err error ) EncodedError {
39
+ // encodeLeaf encodes a leaf error. This function accepts a `causes`
40
+ // argument because we encode multi-cause errors using the Leaf
41
+ // protobuf. This was done to enable backwards compatibility when
42
+ // introducing this functionality since the Wrapper type already has a
43
+ // required single `cause` field.
44
+ func encodeLeaf (ctx context.Context , err error , causes []error ) EncodedError {
42
45
var msg string
43
46
var details errorspb.EncodedErrorDetails
44
47
45
48
if e , ok := err .(* opaqueLeaf ); ok {
46
49
msg = e .msg
47
50
details = e .details
51
+ } else if e , ok := err .(* opaqueLeafCauses ); ok {
52
+ msg = e .msg
53
+ details = e .details
48
54
} else {
49
55
details .OriginalTypeName , details .ErrorTypeMark .FamilyName , details .ErrorTypeMark .Extension = getTypeDetails (err , false /*onlyFamily*/ )
50
56
@@ -74,11 +80,21 @@ func encodeLeaf(ctx context.Context, err error) EncodedError {
74
80
details .FullDetails = encodeAsAny (ctx , err , payload )
75
81
}
76
82
83
+ var cs []* EncodedError
84
+ if len (causes ) > 0 {
85
+ cs = make ([]* EncodedError , len (causes ))
86
+ for i , ee := range causes {
87
+ ee := EncodeError (ctx , ee )
88
+ cs [i ] = & ee
89
+ }
90
+ }
91
+
77
92
return EncodedError {
78
93
Error : & errorspb.EncodedError_Leaf {
79
94
Leaf : & errorspb.EncodedErrorLeaf {
80
- Message : msg ,
81
- Details : details ,
95
+ Message : msg ,
96
+ Details : details ,
97
+ MultierrorCauses : cs ,
82
98
},
83
99
},
84
100
}
@@ -207,6 +223,8 @@ func getTypeDetails(
207
223
switch t := err .(type ) {
208
224
case * opaqueLeaf :
209
225
return t .details .OriginalTypeName , t .details .ErrorTypeMark .FamilyName , t .details .ErrorTypeMark .Extension
226
+ case * opaqueLeafCauses :
227
+ return t .details .OriginalTypeName , t .details .ErrorTypeMark .FamilyName , t .details .ErrorTypeMark .Extension
210
228
case * opaqueWrapper :
211
229
return t .details .OriginalTypeName , t .details .ErrorTypeMark .FamilyName , t .details .ErrorTypeMark .Extension
212
230
}
@@ -310,6 +328,28 @@ type LeafEncoder = func(ctx context.Context, err error) (msg string, safeDetails
310
328
// registry for RegisterLeafEncoder.
311
329
var leafEncoders = map [TypeKey ]LeafEncoder {}
312
330
331
+ // RegisterMultiCauseEncoder can be used to register new multi-cause
332
+ // error types to the library. Registered types will be encoded using
333
+ // their own Go type when an error is encoded. Multi-cause wrappers
334
+ // that have not been registered will be encoded using the
335
+ // opaqueWrapper type.
336
+ func RegisterMultiCauseEncoder (theType TypeKey , encoder MultiCauseEncoder ) {
337
+ // This implementation is a simple wrapper around `LeafEncoder`
338
+ // because we implemented multi-cause error wrapper encoding into a
339
+ // `Leaf` instead of a `Wrapper` for smoother backwards
340
+ // compatibility support. Exposing this detail to consumers of the
341
+ // API is confusing and hence avoided. The causes of the error are
342
+ // encoded separately regardless of this encoder's implementation.
343
+ RegisterLeafEncoder (theType , encoder )
344
+ }
345
+
346
+ // MultiCauseEncoder is to be provided (via RegisterMultiCauseEncoder
347
+ // above) by additional multi-cause wrapper types not yet known to this
348
+ // library. The encoder will automatically extract and encode the
349
+ // causes of this error by calling `Unwrap()` and expecting a slice of
350
+ // errors.
351
+ type MultiCauseEncoder = func (ctx context.Context , err error ) (msg string , safeDetails []string , payload proto.Message )
352
+
313
353
// RegisterWrapperEncoder can be used to register new wrapper types to
314
354
// the library. Registered wrappers will be encoded using their own
315
355
// Go type when an error is encoded. Wrappers that have not been
0 commit comments