// Licensed to Elasticsearch B.V. under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. Elasticsearch B.V. licenses this file to you 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 txerr provides common functions for querying and formatting errors
// generated by txfile.
//
// Creating errors in txfile for use with txerr must follow some common
// conventions, so to guarantee some level consistency among packages.
//
// Package txerr expects errors to implement methods for querying error
// internals.  This allows us to treat errors as interfaces, while having
// package specific implementations.  A common set of recommended methods is
// documented with the Error type.
//
// The Error type is designed to rely on builtin types only. No implementation
// must be forced to use a type define in txerr or any other common package.
//
// Errors are treated as a tree of individual error values. Each node in the
// tree can have 0, 1 or N children. Errors capturing a single cause only must
// implement `Cause() error`. Errors capturing multiple causes must implement
// `Causes() []error`. An error is a Leaf-Node if:
// - It does not implement `Cause()` or `Causes()`
// - It implements `Cause()` and `err.Cause() == nil`
// - It implements `Causes()` and `len(err.Causes()) == 0`.
//
// Each node in the tree provides addition context. The operation which caused
// the error (common format: `package-name/type-method`), an error kind for use
// by the application (query error type and recover), a custom error context
// and an user message.
//
// Error kinds use type error, but must be uniquely comparable. That is due to
// interface comparison rules every package defining an error kind must define
// a custom type + constants for every supported error kind. Common pattern
// used within txfile:
//
//    // this type declaration ensures comparisons with values from other package
//    // will not get us false positives
//    type errKind int
//
//    //go:generate stringer -type=errKind -linecomment=true
//    const (
//      Value1 errKind = iota // readable error message
//      Value2                // another readable error message
//    )
//
//    func (k errKind) Error() string { // satisfy error interface
//      return k.String()
//    }
//
// Errors can have a package specific context. When creating a context, a
// nested error should not repeat the variables also present in the parent
// context.  This requires us to implement some kind of context-merging when
// adding a cause to an error.
// Common pattern for error types used in txfile:
//
//    type Error struct {
//      op string
//      kind error
//      cause error
//      ctx errorCtx
//      msg string
//    }
//
//    type errorCtx struct {
//      k1 int
//      k2 uint
//
//      // optional context field (if you are too lazy to create a many error
//      // types use bool or check for empty values)
//      isK3 bool
//      k3 string
//    }
//
//    // fulfill txerr.Error interface:
//    func (e *Error) Error() string   { return txerr.Report(e) } // implement `error` with help of txerr
//    func (e *Error) Op() string      { return e.op }
//    func (e *Error) Kind() error     { return e.kind }
//    func (e *Error) Cause() error    { return e.cause }
//    func (e *Error) Context() string { return e.ctx.String() }
//    func (e *Error) Message() string { return e.msg }
//
//    // implement Format and Errors to make some logging libs formatting errors
//    // based on these methods happy (e.g. zap).
//    func (e *Error) Format(s fmt.State, c rune) { txerr.Format(e, s, c) }
//    func (e *Error) Errors() []error {
//      if e.cause == nil {
//        return nil
//      }
//      return []error{e.cause}
//    }
//
//    // context based accessors (optional)
//    func (e *Error) K1() int { return e.ctx.k1 }
//
//    func (ctx *errorCtx) String() string {
//      buf := &strbld.Builder{}
//        buf.Fmt("k1=%v k2=%v", ctx.k1, ctx.k2)
//        if ctx.isK3 {
//          buf.Fmt(" k3=%v", ctx.k3)
//        }
//      return buf.String()
//    }
//
//    // optional error building helpers (e.g. for wrapping another error):
//    func (e *Error) of(kind errKind) *Error {
//      e.kind = kind
//      return e
//    }
//
//    func (e *Error) report(s string) *Error {
//      e.msg = s
//      return e
//    }
//
//    func (e *Error) causedBy(cause error) *Error {
//      e.cause = cause
//      if other, ok := cause.(*Error); ok {
//        mergeCtx(e, other)
//      }
//      return e
//    }
//
//    func mergeCtx(err, cause *Error) { ... }
//
// Objects very often cary context to be added to an error. In txfile these
// kind of objects provide additional helpers for constructing errors. These
// helpers guarantee that the context is correctly reported:
//
//     type myObj struct {...}
//
//     func (obj *myObj) errWrap(op string, cause error) *Error {
//       return obj.err(op).causedBy(cause)
//     }
//
//     func (obj *myObj) err(op string) *Error {
//       return &Error{op: op, ctx: errCtx()}
//     }
//
//     func (obj *myObj) errCtx() errorCtx {
//       ...
//     }
//
// All error construction should use the appropriate constructors:
//
//     func (obj *myObj) methodThatFails() error {
//       const op = "<package-name>/obj-<readable-op>"
//
//       ...
//
//       // on failure
//       if err != nil {
//         return obj.errWrap(op, err).of(ForApplicationUse).report("custom user message")
//       }
//
//       ...
//     }
package txerr
