Skip to content

feat: better error handling #1

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jul 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@ linters:
- inamedparam # reports interfaces with unnamed method parameters
- interfacebloat # check for large interfaces
- intrange # find places where for loops could make use of an integer range
- ireturn # Accept Interfaces, Return Concrete Types.
- lll # check for long lines
- maintidx # measures the maintainability index of each function
- mirror # reports wrong mirror patterns of bytes/strings usage
Expand Down Expand Up @@ -123,6 +122,7 @@ linters:
- mnd # too many detections
- cyclop # covered by gocyclo
- gochecknoglobals # there are many valid reasons for global variables, depending on the project
- ireturn # there are too many exceptions

linters-settings:
wsl:
Expand All @@ -139,6 +139,9 @@ linters-settings:
line-length: 140
tab-width: 1

nlreturn:
block-size: 2

exhaustive:
check-generated: false
default-signifies-exhaustive: true
Expand Down
30 changes: 18 additions & 12 deletions file.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,28 +11,34 @@ import (
func ReadFile(path string) (string, error) {
res, err := os.ReadFile(path)
if err != nil {
return "", err
return "", fmt.Errorf("could not read file: %w", err)
}

return string(res), nil
}

// WriteFile writes the given content to the given file.
// Accepts a string or a byte slice.
func WriteFile[T string | []byte](path string, content T) error {
return os.WriteFile(path, []byte(content), 0644)
err := os.WriteFile(path, []byte(content), 0o600)
if err != nil {
return fmt.Errorf("could not write file: %w", err)
}

return nil
}

// AppendToFile appends the given content to the given file.
// Accepts a string or a byte slice.
func AppendToFile[T string | []byte](path string, content T) error {
f, err := os.OpenFile(path, os.O_APPEND|os.O_WRONLY, 0644)
file, err := os.OpenFile(path, os.O_APPEND|os.O_WRONLY, 0o600)
if err != nil {
return err
return fmt.Errorf("could not open file: %w", err)
}
defer f.Close()
defer file.Close()

if _, err = f.Write([]byte(content)); err != nil {
return err
if _, err = file.Write([]byte(content)); err != nil {
return fmt.Errorf("could not write to file: %w", err)
}

return nil
Expand All @@ -49,23 +55,23 @@ func FileExists(path string) bool {
func DownloadFile(url, path string) error {
out, err := os.Create(path)
if err != nil {
return err
return fmt.Errorf("could not create file: %w", err)
}
defer out.Close()

resp, err := http.Get(url)
resp, err := http.Get(url) //nolint:gosec // wrapper function
if err != nil {
return err
return fmt.Errorf("could not download file: %w", err)
}
defer resp.Body.Close()

if resp.StatusCode != http.StatusOK {
return fmt.Errorf("bad status: %s", resp.Status)
return fmt.Errorf("bad status: %s", resp.Status) //nolint: err113
}

_, err = io.Copy(out, resp.Body)
if err != nil {
return err
return fmt.Errorf("could not write file: %w", err)
}

return nil
Expand Down
48 changes: 26 additions & 22 deletions to.go
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
package utils

import (
"atomicgo.dev/constraints"
"encoding/json"
"fmt"
"strconv"
"strings"

"atomicgo.dev/constraints"
)

// ToJSON converts the given value to a JSON string.
func ToJSON(v any) (string, error) {
r, err := json.Marshal(v)
if err != nil {
return "", err
return "", fmt.Errorf("could not marshal json: %w", err)
}

return string(r), nil
}

Expand All @@ -24,8 +26,9 @@ func ToPrettyJSON(v any, indent ...string) (string, error) {

r, err := json.MarshalIndent(v, "", indent[0])
if err != nil {
return "", err
return "", fmt.Errorf("could not marshal json: %w", err)
}

return string(r), nil
}

Expand All @@ -36,46 +39,47 @@ func ToString(v any) string {

// ToInt converts the given value to an int.
// If the value is a float, it will be rounded to the nearest integer. (Rounds up if the decimal is 0.5 or higher)
func ToInt[T string | constraints.Number](v T) int {
switch v := any(v).(type) {
func ToInt[T string | constraints.Number](value T) int {
switch value := any(value).(type) {
case int:
return v
return value
case int8:
return int(v)
return int(value)
case int16:
return int(v)
return int(value)
case int32:
return int(v)
return int(value)
case int64:
return int(v)
return int(value)
case uint:
return int(v)
return int(value)
case uint8:
return int(v)
return int(value)
case uint16:
return int(v)
return int(value)
case uint32:
return int(v)
return int(value)
case uint64:
return int(v)
return int(value)
case float32:
v += 0.5
return int(v)
value += 0.5
return int(value)
case float64:
v += 0.5
return int(v)
value += 0.5
return int(value)
case string:
var i int

if !strings.Contains(v, ".") {
i, _ = strconv.Atoi(v)
if !strings.Contains(value, ".") {
i, _ = strconv.Atoi(value)
} else {
f, _ := strconv.ParseFloat(v, 64)
f, _ := strconv.ParseFloat(value, 64)
f += 0.5 // Round up
i = int(f)
}

return i

default:
return 0
}
Expand Down
9 changes: 5 additions & 4 deletions to_test.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
package utils_test

import (
"atomicgo.dev/utils"
"fmt"

"atomicgo.dev/utils"
)

type Person struct {
Expand All @@ -11,7 +12,7 @@ type Person struct {
}

func ExampleToJSON() {
var person = Person{"John Doe", 42}
person := Person{"John Doe", 42}

json, _ := utils.ToJSON(person)
fmt.Println(json)
Expand Down Expand Up @@ -52,8 +53,8 @@ func ExampleToString() {

func ExampleToPrettyJSON() {
person := Person{Name: "John Doe", Age: 42}
prettyJson, _ := utils.ToPrettyJSON(person)
fmt.Println(prettyJson)
prettyJSON, _ := utils.ToPrettyJSON(person)
fmt.Println(prettyJSON)

// Output:
// {
Expand Down
13 changes: 9 additions & 4 deletions utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package utils
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
)

Expand All @@ -12,37 +13,41 @@ func Ternary[T any](condition bool, a, b T) T {
if condition {
return a
}

return b
}

// PrettyJSON returns a pretty-printed JSON string.
// If indent is not provided, it defaults to " " (two spaces).
func PrettyJSON(inputJSON string, indent ...string) (string, error) {
var out bytes.Buffer

if len(indent) == 0 {
indent = append(indent, " ")
}

err := json.Indent(&out, []byte(inputJSON), "", indent[0])
if err != nil {
return "", err
return "", fmt.Errorf("failed to pretty-print JSON: %w", err)
}

return out.String(), nil
}

// Fetch returns the body of a GET request to the given URL.
func Fetch(url string) (string, error) {
resp, err := http.Get(url)
resp, err := http.Get(url) //nolint: gosec
if err != nil {
return "", err
return "", fmt.Errorf("failed to fetch URL: %w", err)
}

defer resp.Body.Close()

var buf bytes.Buffer

_, err = buf.ReadFrom(resp.Body)
if err != nil {
return "", err
return "", fmt.Errorf("failed to read response body: %w", err)
}

return buf.String(), nil
Expand Down
7 changes: 4 additions & 3 deletions utils_test.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
package utils_test

import (
"atomicgo.dev/utils"
"fmt"

"atomicgo.dev/utils"
)

func ExampleTernary() {
Expand All @@ -17,8 +18,8 @@ func ExampleTernary() {
func ExamplePrettyJSON() {
person := Person{Name: "John Doe", Age: 42}
json, _ := utils.ToJSON(person)
prettyJson, _ := utils.PrettyJSON(json)
fmt.Println(prettyJson)
prettyJSON, _ := utils.PrettyJSON(json)
fmt.Println(prettyJSON)

// Output:
// {
Expand Down
Loading