Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
jbowes committed May 16, 2019
0 parents commit 1dbaf51
Show file tree
Hide file tree
Showing 9 changed files with 390 additions and 0 deletions.
31 changes: 31 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
dist: xenial
language: go

# Force-enable Go modules. Also force go to use the code in vendor/
# These will both be unnecessary when Go 1.13 lands.
env:
global:
- GO111MODULE=on

go:
- "1.12.x"

git:
depth: 1

# Skip the install step. Don't `go get` dependencies. Only build with the code
# in vendor/
install: true

# Anything in before_script that returns a nonzero exit code will flunk the
# build and immediately stop. It's sorta like having set -e enabled in bash.
# Make sure golangci-lint is vendored.
before_script:
- go install github.com/golangci/golangci-lint/cmd/golangci-lint

script:
- golangci-lint run # run a bunch of code checkers/linters in parallel
- go test -race -coverpkg ./... -coverprofile coverage.txt ./...

after_success:
- bash <(curl -s https://codecov.io/bash)
25 changes: 25 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
BSD 2-Clause License

Copyright (c) 2019, James Bowes
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
96 changes: 96 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
<!--
Attractive html formatting for rendering in github. sorry text editor
readers! Besides the header and section links, everything should be clean and
readable.
-->
<h1 align="center">cling</h1>
<p align="center"><i>Clear and obvious error wrapping for <a href="https://golang.org">Go</a> errors</i></p>

<div align="center">
<a href="https://godoc.org/github.com/jbowes/cling"><img src="https://godoc.org/github.com/jbowes/cling?status.svg" alt="GoDoc"></a>
<img alt="Alpha Quality" src="https://img.shields.io/badge/status-ALPHA-orange.svg" >
<a href="https://travis-ci.org/jbowes/cling"><img alt="Build Status" src="https://travis-ci.org/jbowes/cling.svg?branch=master"></a>
<a href="https://github.com/jbowes/cling/releases/latest"><img alt="GitHub release" src="https://img.shields.io/github/release/jbowes/cling.svg"></a>
<a href="./LICENSE"><img alt="BSD license" src="https://img.shields.io/badge/license-BSD-blue.svg"></a>
<a href="https://codecov.io/gh/jbowes/cling"><img alt="codecov" src="https://img.shields.io/codecov/c/github/jbowes/cling.svg"></a>
<a href="https://goreportcard.com/report/github.com/jbowes/cling"><img alt="Go Report Card" src="https://goreportcard.com/badge/github.com/jbowes/cling"></a>
</div><br /><br />


## Introduction
Introduction | [Examples] | [Contributing] <br /><br />

🚧 ___Disclaimer___: _`cling` is alpha quality software. The API may change
without warning between revisions._ 🚧

`cling` provides a clear and obvious error wrapping API for the new [Go] 2/1.13+
[errors](https://godoc.org/golang.org/x/xerrors) package. If you prefer a
specific function over a [formatting directive](https://godoc.org/golang.org/x/xerrors#Errorf),
and an API that returns a nil error when the error to wrap is nil, then `cling`
is for you.


## Examples
[Introduction] | Examples | [Contributing] <br /><br />

For complete examples and usage, see the [GoDoc documentation](https://godoc.org/github.com/jbowes/cling).

_Wrapping an error_
```go
err := errors.New("an error")
wrapped := cling.Wrap(err, "wrapped")

// Wrapped errors can be programatically inspected
fmt.Print(xerrors.Is(wrapped, err)) // true
```

_Sealing an error_
```go
err := errors.New("an error")
sealed := cling.Wrap(err, "sealed")

// Sealed errors cannot be programatically inspected
fmt.Print(xerrors.Is(sealed, err)) // false
```

Both `Wrap` and `Seal` provide format specifier versions(`Wrapf`, `Sealf`),
as well.

### Building APIs on `cling`

`cling/skip` implements the `cling` API with an additional `skip` argument,
allowing creation of APIs on top of `cling` that will no report themselves in
error caller frames.


## Contributing
[Introduction] | [Examples] | [Usage] | Contributing <br /><br />

I would love your help!

`cling` is still a work in progress. You can help by:

- Trying `oag` against different [OpenAPI] documents, and [reporting bugs][bug]
when the generated code is broken, or [suggesting improvements][enhancement]
to the generated code.
- Opening a pull request to resolve an [open issue][issues].
- Adding a feature or enhancement of your own! If it might be big, please
[open an issue][enhancement] first so we can discuss it.
- Improving this `README` or adding other documentation to `cling`.
- Letting [me] know if you're using `cling`.


<!-- Section link definitions -->
[introduction]: #introduction
[examples]: #examples
[usage]: #usage
[contributing]: #contributing

<!-- Other links -->
[go]: https://golang.org

[issues]: ./issues
[bug]: ./issues/new?labels=bug
[enhancement]: ./issues/new?labels=enhancement

[me]: https://twitter.com/jrbowes
41 changes: 41 additions & 0 deletions cling.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package cling // import "github.com/jbowes/cling"

import "github.com/jbowes/cling/skip"

// Wrap returns an error wrapping err with the supplied message, and a frame
// from the caller's stack. The argument skip is the number of frames to skip
// over. Caller(0) returns the frame for the caller of Wrap. If err is nil, Wrap
// returns nil.
//
// The error returned implments the Unwrap method, for programatically
// extracting the error chain.
//
// This func is intended to be used for implementing APIs on top of cling.
func Wrap(err error, message string) error { return skip.Wrap(err, 1, message) }

// Wrapf returns an error wrapping err with the supplied format specifier, and a
// frame from the caller's stack. The argument skip is the number of frames to
// skip over. Caller(0) returns the frame for the caller of Wrap. If err is nil,
// Wrapf returns nil.
//
// The error returned implments the Unwrap method, for programatically
// extracting the error chain.
func Wrapf(err error, format string, a ...interface{}) error { return skip.Wrapf(err, 1, format, a...) }

// Seal returns an error wrapping err with the supplied message, and a frame
// from the caller's stack. The argument skip is the number of frames to skip
// over. Caller(0) returns the frame for the caller of Wrap. If err is nil, Wrap
// returns nil.
//
// The error returned does not implement Unwrap; the error chain is available
// only for printing.
func Seal(err error, message string) error { return skip.Seal(err, 1, message) }

// Sealf returns an error wrapping err with the supplied format specifier, and a
// frame from the caller's stack. The argument skip is the number of frames to
// skip over. Caller(0) returns the frame for the caller of Wrap. If err is nil,
// Wrap returns nil.
//
// The error returned does not implement Unwrap; the error chain is available
// only for printing.
func Sealf(err error, format string, a ...interface{}) error { return skip.Sealf(err, 1, format, a...) }
5 changes: 5 additions & 0 deletions doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/*
Package cling provides a clear and obvious error wrapping API for Go 2/1.13+
error values.
*/
package cling
73 changes: 73 additions & 0 deletions example_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package cling_test

import (
"errors"
"fmt"

"golang.org/x/xerrors"

"github.com/jbowes/cling"
)

func Example() {
// All funcs in this package print error chains
err := errors.New("an error")
wrapped := cling.Wrap(err, "wrapped")
rewrapped := cling.Wrap(wrapped, "re-wrapped")
fmt.Printf("%v", rewrapped) // or use %+v to print the stack frames
// Output: re-wrapped: wrapped: an error
}

func Example_nil() {
// All funcs in this package return nil when passed a nil error.
err := error(nil)
wrapped := cling.Wrap(err, "wrapped")
fmt.Print(wrapped)
// Output: <nil>
}

func ExampleWrap() {
err := errors.New("an error")
wrapped := cling.Wrap(err, "wrapped")
fmt.Print(wrapped)
// Output: wrapped: an error
}

func ExampleWrap_is() {
err := errors.New("an error")
wrapped := cling.Wrap(err, "wrapped")

// Wrapped errors can be programatically inspected
fmt.Print(xerrors.Is(wrapped, err))
// Output: true
}

func ExampleWrapf() {
err := errors.New("an error")
wrapped := cling.Wrapf(err, "wrapped, especially for %s", "you")
fmt.Print(wrapped)
// Output: wrapped, especially for you: an error
}

func ExampleSeal() {
err := errors.New("an error")
sealed := cling.Seal(err, "sealed")
fmt.Print(sealed)
// Output: sealed: an error
}

func ExampleSeal_is() {
err := errors.New("an error")
sealed := cling.Seal(err, "sealed")

// Sealed errors are opaque for programmatic inspection
fmt.Print(xerrors.Is(sealed, err))
// Output: false
}

func ExampleSealf() {
err := errors.New("an error")
sealed := cling.Sealf(err, "sealed, especially for %s", "you")
fmt.Print(sealed)
// Output: sealed, especially for you: an error
}
5 changes: 5 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module github.com/jbowes/cling

go 1.12

require golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522 h1:bhOzK9QyoD0ogCnFro1m2mz41+Ib0oOhfJnBp5MR4K4=
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
112 changes: 112 additions & 0 deletions skip/cling.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
package skip

import (
"fmt"

"golang.org/x/xerrors"
)

// Wrap returns an error wrapping err with the supplied message, and a frame
// from the caller's stack. The argument skip is the number of frames to skip
// over. Caller(0) returns the frame for the caller of Wrap. If err is nil, Wrap
// returns nil.
//
// The error returned implments the Unwrap method, for programatically
// extracting the error chain.
//
// This func is intended to be used for implementing APIs on top of cling.
func Wrap(err error, skip uint, message string) error {
if err == nil {
return nil
}

return &wrapError{sealError{
msg: message,
err: err,
frame: xerrors.Caller(int(skip + 1)),
}}
}

// Wrapf returns an error wrapping err with the supplied format specifier, and a
// frame from the caller's stack. The argument skip is the number of frames to
// skip over. Caller(0) returns the frame for the caller of Wrap. If err is nil,
// Wrapf returns nil.
//
// The error returned implments the Unwrap method, for programatically
// extracting the error chain.
//
// This func is intended to be used for implementing APIs on top of cling.
func Wrapf(err error, skip uint, format string, a ...interface{}) error {
if err == nil {
return nil
}

return &wrapError{sealError{
msg: fmt.Sprintf(format, a...),
err: err,
frame: xerrors.Caller(int(skip + 1)),
}}
}

// Seal returns an error wrapping err with the supplied message, and a frame
// from the caller's stack. The argument skip is the number of frames to skip
// over. Caller(0) returns the frame for the caller of Wrap. If err is nil, Wrap
// returns nil.
//
// The error returned does not implement Unwrap; the error chain is available
// only for printing.
//
// This func is intended to be used for implementing APIs on top of cling.
func Seal(err error, skip uint, message string) error {
if err == nil {
return nil
}

return &sealError{
msg: message,
err: err,
frame: xerrors.Caller(int(skip + 1)),
}
}

// Sealf returns an error wrapping err with the supplied format specifier, and a
// frame from the caller's stack. The argument skip is the number of frames to
// skip over. Caller(0) returns the frame for the caller of Wrap. If err is nil,
// Wrap returns nil.
//
// The error returned does not implement Unwrap; the error chain is available
// only for printing.
//
// This func is intended to be used for implementing APIs on top of cling.
func Sealf(err error, skip uint, format string, a ...interface{}) error {
if err == nil {
return nil
}

return &sealError{
msg: fmt.Sprintf(format, a...),
err: err,
frame: xerrors.Caller(int(skip + 1)),
}
}

type sealError struct {
msg string
err error
frame xerrors.Frame
}

func (e *sealError) Error() string { return fmt.Sprint(e) }
func (e *sealError) Format(s fmt.State, v rune) { xerrors.FormatError(e, s, v) }

func (e *sealError) FormatError(p xerrors.Printer) (next error) {
p.Print(e.msg)
e.frame.Format(p)
return e.err
}

type wrapError struct {
sealError
}

func (e *wrapError) Unwrap() error { return e.err }

0 comments on commit 1dbaf51

Please sign in to comment.