Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(704)

Unified Diff: common/errors/tags.go

Issue 2951393002: [errors] de-specialize Transient in favor of Tags. (Closed)
Patch Set: more refactor Created 3 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: common/errors/tags.go
diff --git a/common/errors/tags.go b/common/errors/tags.go
new file mode 100644
index 0000000000000000000000000000000000000000..b80f135c94bda7fa9280987201735df73a0155ed
--- /dev/null
+++ b/common/errors/tags.go
@@ -0,0 +1,160 @@
+// Copyright 2017 The LUCI Authors. All rights reserved.
+// Use of this source code is governed under the Apache License, Version 2.0
+// that can be found in the LICENSE file.
+
+package errors
+
+type (
+ // TagKeyOrValue is used for functions taking either a TagKey or a TagValue.
+ TagKeyOrValue interface {
+ isTagKeyOrValue()
+ }
+
+ tagDescription struct {
+ description string
+ }
+
+ // TagKey objects are used for applying tags and finding tags/values in
+ // errors. See NewTag for details.
+ TagKey *tagDescription
+
+ // TagValue represents a (tag, value) to be used with Annotate.Tag, or may be
+ // applied to an error directly with the Apply method.
+ //
+ // Usually tag implementations will have a typesafe With method that generates
+ // these. Avoid constructing these ad-hoc so that a given tag definition can
+ // control the type safety around these.
+ TagValue struct {
+ Key TagKey
+ Value interface{}
+ }
+
+ // A TagValueGenerator generates (TagKey, value) pairs, for use with Annoatator.Tag
+ // and New().
+ TagValueGenerator interface {
dnj 2017/06/27 03:57:03 There are a lot of tag functions. I'm seeing two c
iannucci 2017/06/27 18:14:27 I considered it; as it is, the errors library dele
+ GenerateErrorTagValue() TagValue
+ }
+)
+
+// TagValueIn will retrieve the tagged value from the error that's associated
+// with this key, and a boolean indicating if the tag was present or not.
+func TagValueIn(t TagKey, err error) (value interface{}, ok bool) {
+ Walk(err, func(err error) bool {
+ if sc, isSC := err.(stackContexter); isSC {
+ if value, ok = sc.stackContext().tags[t]; ok {
+ return false
+ }
+ }
+ return true
+ })
+ return
+}
+
+// GenerateErrorTagValue implements TagValueGenerator
+func (t TagValue) GenerateErrorTagValue() TagValue { return t }
+
+// Apply applies this tag value (key+value) directly to the error. This is
+// a shortcut for `errors.Annotate(err).Tag(t).Err()`.
+func (t TagValue) Apply(err error) error {
+ if err == nil {
+ return nil
+ }
+ a := &Annotator{err, stackContext{frameInfo: stackFrameInfoForError(1, err)}}
+ return a.Tag(t).Err()
+}
+
+// MkTagValue is a shortcut for errors.TagValue{Key: key, Value: val}.
+func MkTagValue(key TagKey, val interface{}) TagValue {
+ return TagValue{key, val}
+}
+
+// BoolTag is an error tag implementation which holds a boolean value.
+//
+// It should be constructed like:
+// var myTag = errors.BoolTag{Key: errors.NewTagKey("some description")}
+type BoolTag struct{ Key TagKey }
+
+// GenerateErrorTagValue implements TagValueGenerator, and returns a default
+// value for the tag of `true`. If you want to set this BoolTag value to false,
+// use BoolTag.Off().
+func (b BoolTag) GenerateErrorTagValue() TagValue { return TagValue{b.Key, true} }
+
+// Off allows you to "remove" this boolean tag from an error (by setting it to
dnj 2017/06/27 03:57:03 Not sure if this is a good thing or not. Oh well,
iannucci 2017/06/27 18:14:27 Yeah, we'll see. I put it in because otherwise it
+// false).
+func (b BoolTag) Off() TagValue { return TagValue{b.Key, false} }
+
+// Apply is a shortcut for With(true).Apply(err)
+func (b BoolTag) Apply(err error) error {
+ if err == nil {
+ return nil
+ }
+ a := &Annotator{err, stackContext{frameInfo: stackFrameInfoForError(1, err)}}
+ return a.Tag(b).Err()
+}
+
+// In returns true iff this tag value has been set to true on this error.
+func (b BoolTag) In(err error) bool {
+ v, ok := TagValueIn(b.Key, err)
+ if !ok {
+ return false
+ }
+ return v.(bool)
+}
+
+// NewTagKey creates a new TagKey.
+//
+// Use this with a BoolTag or your own custom tag implementation.
+//
+// Example (bool tag):
+// var myTag = errors.BoolTag{Key: errors.NewTagKey("this error is a user error")}
+//
+// err = myTag.Apply(err)
+// myTag.In(err) // == true
+//
+// err2 := myTag.Off().Apply(err)
+// myTag.In(err2) // == false
+//
+// Example (custom tag)
+// type SomeType int
+// type myTag struct { Key errors.TagKey }
+// func (m myTag) With(value SomeType) errors.TagValue {
+// return errors.MkTagValue(vm.Key, value)
+// }
+// func (m myTag) In(err error) (v SomeType, ok bool) {
+// d, ok := errors.TagValueIn(m.Key, err)
+// if ok {
+// v = d.(SomeType)
+// }
+// return
+// }
+// var MyTag = myTag{errors.NewTagKey("has a SomeType")}
+//
+// You could then use it like:
+// err = MyTag.With(100).Apply(err)
+// MyTag.In(err) // == true
+// MyTag.ValueIn(err) // == (SomeType(100), true)
+func NewTagKey(description string) TagKey {
+ return &tagDescription{description}
+}
+
+// GetTags returns a map of all TagKeys set in this error to their value.
+//
+// A nil value means that the tag is present, but has a nil associated value.
+//
+// This is done in a depth-first traversal of the error stack, with the
+// most-recently-set value of the tag taking precendence.
+func GetTags(err error) map[TagKey]interface{} {
+ ret := map[TagKey]interface{}{}
+ Walk(err, func(err error) bool {
+ if sc, ok := err.(stackContexter); ok {
+ ctx := sc.stackContext()
+ for k, v := range ctx.tags {
+ if _, ok := ret[k]; !ok {
+ ret[k] = v
+ }
+ }
+ }
+ return true
+ })
+ return ret
+}
« no previous file with comments | « common/errors/multierror.go ('k') | common/errors/tags_test.go » ('j') | grpc/grpcutil/errors.go » ('J')

Powered by Google App Engine
This is Rietveld 408576698