Skip to content

Instantly share code, notes, and snippets.

@nanoninja
Last active July 22, 2017 13:28
Show Gist options
  • Save nanoninja/15e78c8f9007c264b511e9662c4ecc90 to your computer and use it in GitHub Desktop.
Save nanoninja/15e78c8f9007c264b511e9662c4ecc90 to your computer and use it in GitHub Desktop.
Concurrent Stack Of Errors

Concurrent Stack Of Errors

It just an experimental package to handle concurrent errors stack. The concept is based on the First Class Functions and inspired by Dave Cheney posts.

Example

func main() {
    s := NewStack()

    s.Add(WithName("test", "my error"))
    s.Add(WithName("test", "another error"))
    s.Add(WithName("gopher", "hello gopher"))

    for err := range s.Iter() {
        fmt.Println(err)
    }
}
// Copyright 2017 The Nanoninja Authors. All rights reserved.
package main
// New returns a new error type.
func New(message string) Error {
return Error{Message: message}
}
// WithName returns a new Error with a name.
func WithName(name, message string) Error {
return Error{Name: name, Message: message}
}
// WithCode returns a new Error with a code.
func WithCode(code int, message string) Error {
return Error{Message: message, Code: code}
}
// Error represents a basic Error and implements error interface.
type Error struct {
Code int
Domain string
Message string
Name string
Reason string
}
func (e *Error) Error() string {
return e.Message
}
package main
import (
"fmt"
"sync"
)
// go run -race !(*_test).go
var wg sync.WaitGroup
func main() {
s := NewStack()
s.Add(WithName("test", "my error"))
s.Add(WithName("test", "another error"))
s.Add(WithName("gopher", "hello gopher"))
wg.Add(1)
go func() {
s.Remove(1)
s.Add(WithName("concurrent", "error"))
wg.Done()
}()
wg.Wait()
for err := range s.Iter() {
fmt.Println(err.Name, err.Message)
}
}
// Copyright 2017 The Nanoninja Authors. All rights reserved.
package main
var _ Stackable = (*stack)(nil)
type Many []Error
type stack struct {
ops chan func(*Many)
}
type Stackable interface {
Add(Error)
Count() int
Clear()
IsEmpty() bool
Iter() <-chan Error
Find(func(Error) bool) Stackable
Remove(int)
}
func NewStack() Stackable {
s := &stack{
ops: make(chan func(*Many), 10),
}
go s.init()
return s
}
func (s *stack) init() {
m := make(Many, 0)
for op := range s.ops {
op(&m)
}
}
func (s *stack) Add(err Error) {
s.ops <- func(m *Many) {
*m = append(*m, err)
}
}
func (s *stack) Iter() <-chan Error {
err := make(chan Error)
s.ops <- func(m *Many) {
for _, e := range *m {
err <- e
}
close(err)
}
return err
}
func (s *stack) Count() int {
counter := make(chan int)
defer close(counter)
go func() {
s.ops <- func(m *Many) {
counter <- len(*m)
}
}()
return <-counter
}
func (s *stack) Find(finder func(Error) bool) Stackable {
sk := NewStack()
for err := range s.Iter() {
if finder(err) {
sk.Add(err)
}
}
return sk
}
func (s *stack) Clear() {
s.ops <- func(m *Many) {
*m = (*m)[:0]
}
}
func (s *stack) IsEmpty() bool {
return s.Count() == 0
}
func (s *stack) Remove(index int) {
s.ops <- func(m *Many) {
if index <= len(*m) {
*m = append((*m)[:index], (*m)[index+1:]...)
}
}
}
package main
import "testing"
// go test -race ./...
// go test -race -coverprofile cover.out ./... ./...
// go tool cover -html cover.out -o cover.html
var stackTest = []struct {
name, message string
}{
{"go", "error one"},
{"go", "error two"},
{"test", "error one"},
}
func TestStack(t *testing.T) {
s := NewStack()
if got, want := s.IsEmpty(), true; got != want {
t.Errorf("IsEmpty() got %v; want %v", got, want)
}
for _, tt := range stackTest {
s.Add(WithName(tt.name, tt.message))
}
if got, want := s.Count(), 3; got != want {
t.Errorf("Count() got %d; want %d", got, want)
}
stack := s.Find(func(err Error) bool {
return err.Name == "go"
})
if got, want := stack.Count(), 2; got != want {
t.Errorf("Count() got %d; want %d", got, want)
}
s.Remove(10)
if got, want := s.Count(), 3; got != want {
t.Errorf("Count() 10 got %d; want %d", got, want)
}
s.Remove(2)
if got, want := s.Count(), 2; got != want {
t.Errorf("Count() got %d; want %d", got, want)
}
s.Clear()
if got, want := s.IsEmpty(), true; got != want {
t.Errorf("IsEmpty() got %v; want %v", got, want)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment