gitea

Development moved to Codeberg

  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  10. 10
  11. 11
  12. 12
  13. 13
  14. 14
  15. 15
  16. 16
  17. 17
  18. 18
  19. 19
  20. 20
  21. 21
  22. 22
  23. 23
  24. 24
  25. 25
  26. 26
  27. 27
  28. 28
  29. 29
  30. 30
  31. 31
  32. 32
  33. 33
  34. 34
  35. 35
  36. 36
  37. 37
  38. 38
  39. 39
  40. 40
  41. 41
  42. 42
  43. 43
  44. 44
  45. 45
  46. 46
  47. 47
  48. 48
  49. 49
  50. 50
  51. 51
  52. 52
  53. 53
  54. 54
  55. 55
  56. 56
  57. 57
  58. 58
  59. 59
  60. 60
  61. 61
  62. 62
  63. 63
  64. 64
  65. 65
  66. 66
  67. 67
  68. 68
  69. 69
  70. 70
  71. 71
  72. 72
  73. 73
  74. 74
  75. 75
  76. 76
  77. 77
  78. 78
  79. 79
  80. 80
  81. 81
  82. 82
  83. 83
  84. 84
  85. 85
  86. 86
  87. 87
  88. 88
  89. 89
  90. 90
  91. 91
  92. 92
  93. 93
  94. 94
  95. 95
  96. 96
  97. 97
  98. 98
  99. 99
  100. 100
  101. 101
  102. 102
  103. 103
  104. 104
  105. 105
  106. 106
  107. 107
  108. 108
  109. 109
  110. 110
  111. 111
  112. 112
  113. 113
  114. 114
  115. 115
  116. 116
  117. 117
  118. 118
  119. 119
  120. 120
// Copyright 2016, Joe Tsai. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE.md file.

// Package errors implements functions to manipulate compression errors.
//
// In idiomatic Go, it is an anti-pattern to use panics as a form of error
// reporting in the API. Instead, the expected way to transmit errors is by
// returning an error value. Unfortunately, the checking of "err != nil" in
// tight loops commonly found in compression causes non-negligible performance
// degradation. While this may not be idiomatic, the internal packages of this
// repository rely on panics as a normal means to convey errors. In order to
// ensure that these panics do not leak across the public API, the public
// packages must recover from these panics and present an error value.
//
// The Panic and Recover functions in this package provide a safe way to
// recover from errors only generated from within this repository.
//
// Example usage:
//	func Foo() (err error) {
//		defer errors.Recover(&err)
//
//		if rand.Intn(2) == 0 {
//			// Unexpected panics will not be caught by Recover.
//			io.Closer(nil).Close()
//		} else {
//			// Errors thrown by Panic will be caught by Recover.
//			errors.Panic(errors.New("whoopsie"))
//		}
//	}
//
package errors

import "strings"

const (
	// Unknown indicates that there is no classification for this error.
	Unknown = iota

	// Internal indicates that this error is due to an internal bug.
	// Users should file a issue report if this type of error is encountered.
	Internal

	// Invalid indicates that this error is due to the user misusing the API
	// and is indicative of a bug on the user's part.
	Invalid

	// Deprecated indicates the use of a deprecated and unsupported feature.
	Deprecated

	// Corrupted indicates that the input stream is corrupted.
	Corrupted

	// Closed indicates that the handlers are closed.
	Closed
)

var codeMap = map[int]string{
	Unknown:    "unknown error",
	Internal:   "internal error",
	Invalid:    "invalid argument",
	Deprecated: "deprecated format",
	Corrupted:  "corrupted input",
	Closed:     "closed handler",
}

type Error struct {
	Code int    // The error type
	Pkg  string // Name of the package where the error originated
	Msg  string // Descriptive message about the error (optional)
}

func (e Error) Error() string {
	var ss []string
	for _, s := range []string{e.Pkg, codeMap[e.Code], e.Msg} {
		if s != "" {
			ss = append(ss, s)
		}
	}
	return strings.Join(ss, ": ")
}

func (e Error) CompressError()     {}
func (e Error) IsInternal() bool   { return e.Code == Internal }
func (e Error) IsInvalid() bool    { return e.Code == Invalid }
func (e Error) IsDeprecated() bool { return e.Code == Deprecated }
func (e Error) IsCorrupted() bool  { return e.Code == Corrupted }
func (e Error) IsClosed() bool     { return e.Code == Closed }

func IsInternal(err error) bool   { return isCode(err, Internal) }
func IsInvalid(err error) bool    { return isCode(err, Invalid) }
func IsDeprecated(err error) bool { return isCode(err, Deprecated) }
func IsCorrupted(err error) bool  { return isCode(err, Corrupted) }
func IsClosed(err error) bool     { return isCode(err, Closed) }

func isCode(err error, code int) bool {
	if cerr, ok := err.(Error); ok && cerr.Code == code {
		return true
	}
	return false
}

// errWrap is used by Panic and Recover to ensure that only errors raised by
// Panic are recovered by Recover.
type errWrap struct{ e *error }

func Recover(err *error) {
	switch ex := recover().(type) {
	case nil:
		// Do nothing.
	case errWrap:
		*err = *ex.e
	default:
		panic(ex)
	}
}

func Panic(err error) {
	panic(errWrap{&err})
}