diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7cd1091 --- /dev/null +++ b/.gitignore @@ -0,0 +1,21 @@ +# If you prefer the allow list template instead of the deny list, see community template: +# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore +# +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Dependency directories (remove the comment below to include it) +# vendor/ + +# Go workspace file +go.work \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..7a0a258 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 phelmkamp + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..0e609fd --- /dev/null +++ b/README.md @@ -0,0 +1,16 @@ +# dontpanic + +Panic-free alternatives to common Go operations. + +## Installation + +`go get github.com/phelmkamp/dontpanic` + +## Usage + +```go +func f() (err error) { + defer dontpanic.Recover(&err) + // do stuff that might panic... +} +``` diff --git a/dontpanic.go b/dontpanic.go new file mode 100644 index 0000000..d0577d5 --- /dev/null +++ b/dontpanic.go @@ -0,0 +1,72 @@ +// Copyright 2022 phelmkamp. All rights reserved. +// Use of this source code is governed by a MIT +// license that can be found in the LICENSE file. + +// Package dontpanic provides panic-free alternatives to common operations. +package dontpanic + +import ( + "fmt" +) + +// SetMapIndex sets m[k] = v. +// Returns an error if m is nil. +func SetMapIndex[K comparable, V any, M ~map[K]V](m M, k K, v V) (err error) { + defer Recover(&err) + m[k] = v + return nil +} + +// SetSliceIndex sets s[i] = v. +// Returns an error if i is out of range. +func SetSliceIndex[E any, S ~[]E](s S, i int, v E) (err error) { + defer Recover(&err) + s[i] = v + return nil +} + +// SliceIndex returns s[i]. +// Returns an error if i is out of range. +func SliceIndex[E any, S ~[]E](s S, i int) (_ E, err error) { + defer Recover(&err) + return s[i], nil +} + +// Slice returns s[i:j:k]. +// +// Supports [:] +// Slice(s) +// or [i:] +// Slice(s, i) +// or [i:j] +// Slice(s, i, j) +// or [i:j:k] +// Slice(s, i, j, k) +// Returns an error if indexes are out of range or too many are specified. +func Slice[E any, S ~[]E](s S, ijk ...int) (_ S, err error) { + defer Recover(&err) + switch len(ijk) { + case 0: + return s[:], nil + case 1: + return s[ijk[0]:], nil + case 2: + return s[ijk[0]:ijk[1]], nil + case 3: + return s[ijk[0]:ijk[1]:ijk[2]], nil + default: + return nil, fmt.Errorf("ijk: expected 0-3 indexes; found %d", len(ijk)) + } +} + +// Recover calls recover() and writes the result to err. +func Recover(err *error) { + switch r := recover().(type) { + case nil: + return + case error: + *err = r + default: + *err = fmt.Errorf("%v", r) + } +} diff --git a/dontpanic_test.go b/dontpanic_test.go new file mode 100644 index 0000000..a9f53b1 --- /dev/null +++ b/dontpanic_test.go @@ -0,0 +1,66 @@ +package dontpanic + +import ( + "reflect" + "testing" +) + +func TestSliceIndex(t *testing.T) { + type args struct { + s []string + i int + } + tests := []struct { + name string + args args + want string + wantErr bool + }{ + { + name: "nil", + args: args{s: nil, i: 0}, + want: "", + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := SliceIndex(tt.args.s, tt.args.i) + if (err != nil) != tt.wantErr { + t.Errorf("SliceIndex() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("SliceIndex() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestSetMapIndex(t *testing.T) { + type args struct { + m map[string]int + k string + v int + } + tests := []struct { + name string + args args + wantErr bool + }{ + { + name: "nil", + args: args{m: nil, k: "foo", v: 0}, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := SetMapIndex(tt.args.m, tt.args.k, tt.args.v) + if (err != nil) != tt.wantErr { + t.Errorf("MapIndex() error = %v, wantErr %v", err, tt.wantErr) + return + } + }) + } +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..8d75eed --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module github.com/phelmkamp/dontpanic + +go 1.18