Skip to content

Commit 7abb760

Browse files
authored
Merge pull request #31 from pedramktb/feature/postgres-errors-refactor
feat: refactored packages, added postgres with test containers, added custom types and taggederror
2 parents 52e13c0 + f4a2e67 commit 7abb760

File tree

30 files changed

+1248
-439
lines changed

30 files changed

+1248
-439
lines changed

.github/workflows/_test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ jobs:
1010
- uses: actions/checkout@v4
1111
- uses: actions/setup-go@v5
1212
with:
13-
go-version: "1.23.5"
13+
go-version: "1.24.2"
1414

1515
- name: Install Taskfile
1616
run: go install github.com/go-task/task/v3/cmd/task@latest
File renamed without changes.

pkg/env/env.go renamed to env/env.go

Lines changed: 53 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,56 @@
11
package env
22

33
import (
4+
"errors"
45
"os"
56
"strconv"
7+
)
8+
9+
type Environment string
610

7-
"github.com/joho/godotenv"
11+
const (
12+
EnvironmentProd Environment = "prod"
13+
EnvironmentStaging Environment = "staging"
14+
EnvironmentDev Environment = "dev"
15+
EnvironmentLocal Environment = ""
816
)
917

10-
func init() {
11-
_ = godotenv.Load(".env")
18+
func (e Environment) String() string {
19+
if e == EnvironmentLocal {
20+
return "local"
21+
} else {
22+
return string(e)
23+
}
1224
}
1325

14-
// IsDebug returns true if the DEBUG environment variable is set to true
15-
func IsDebug() bool {
16-
return GetWithFallback("DEBUG", false)
26+
var env Environment
27+
var envSet bool
28+
29+
func SetEnvironment(e Environment) {
30+
env = e
31+
envSet = true
32+
}
33+
34+
func GetEnvironment() Environment {
35+
if envSet {
36+
return env
37+
}
38+
e, _ := GetWithFallback("ENVIRONMENT", string(EnvironmentLocal))
39+
return Environment(e)
1740
}
1841

19-
// GetOrFail returns the value of the environment variable or panics if it is not set
20-
func GetOrFail[
42+
// GetOrFail returns the value of the environment variable or returns an error if it is not set
43+
func GetOrFail[ //NOSONAR
2144
T string |
2245
bool |
2346
uintptr |
2447
int | int64 | int32 | int16 | int8 |
2548
uint | uint64 | uint32 | uint16 | uint8 |
2649
float64 | float32 |
27-
complex128 | complex64](env string) T {
50+
complex128 | complex64](env string) (T, error) {
2851
str := os.Getenv(env)
2952
if str == "" {
30-
panic("missing env variable: " + env)
53+
return *new(T), errors.New("missing env variable: " + env)
3154
}
3255

3356
var val any
@@ -38,104 +61,104 @@ func GetOrFail[
3861
case bool:
3962
b, err := strconv.ParseBool(str)
4063
if err != nil {
41-
panic("invalid bool: " + env)
64+
return *new(T), errors.New("invalid bool: " + env)
4265
}
4366
val = b
4467
case uintptr:
4568
u64, err := strconv.ParseUint(str, 10, 0)
4669
if err != nil {
47-
panic("invalid uintptr: " + env)
70+
return *new(T), errors.New("invalid uintptr: " + env)
4871
}
4972
val = uintptr(u64)
5073
case int:
5174
i64, err := strconv.ParseInt(str, 10, 0)
5275
if err != nil {
53-
panic("invalid int: " + env)
76+
return *new(T), errors.New("invalid int: " + env)
5477
}
5578
val = int(i64)
5679
case int64:
5780
i64, err := strconv.ParseInt(str, 10, 64)
5881
if err != nil {
59-
panic("invalid int64: " + env)
82+
return *new(T), errors.New("invalid int64: " + env)
6083
}
6184
val = i64
6285
case int32:
6386
i64, err := strconv.ParseInt(str, 10, 32)
6487
if err != nil {
65-
panic("invalid int32: " + env)
88+
return *new(T), errors.New("invalid int32: " + env)
6689
}
6790
val = int32(i64)
6891
case int16:
6992
i64, err := strconv.ParseInt(str, 10, 16)
7093
if err != nil {
71-
panic("invalid int16: " + env)
94+
return *new(T), errors.New("invalid int16: " + env)
7295
}
7396
val = int16(i64)
7497
case int8:
7598
i64, err := strconv.ParseInt(str, 10, 8)
7699
if err != nil {
77-
panic("invalid int8: " + env)
100+
return *new(T), errors.New("invalid int8: " + env)
78101
}
79102
val = int8(i64)
80103
case uint:
81104
i64, err := strconv.ParseUint(str, 10, 0)
82105
if err != nil {
83-
panic("invalid uint: " + env)
106+
return *new(T), errors.New("invalid uint: " + env)
84107
}
85108
val = uint(i64)
86109
case uint64:
87110
i64, err := strconv.ParseUint(str, 10, 64)
88111
if err != nil {
89-
panic("invalid uint64: " + env)
112+
return *new(T), errors.New("invalid uint64: " + env)
90113
}
91114
val = i64
92115
case uint32:
93116
i64, err := strconv.ParseUint(str, 10, 32)
94117
if err != nil {
95-
panic("invalid uint32: " + env)
118+
return *new(T), errors.New("invalid uint32: " + env)
96119
}
97120
val = uint32(i64)
98121
case uint16:
99122
i64, err := strconv.ParseUint(str, 10, 16)
100123
if err != nil {
101-
panic("invalid uint16: " + env)
124+
return *new(T), errors.New("invalid uint16: " + env)
102125
}
103126
val = uint16(i64)
104127
case uint8:
105128
i64, err := strconv.ParseUint(str, 10, 8)
106129
if err != nil {
107-
panic("invalid uint8: " + env)
130+
return *new(T), errors.New("invalid uint8: " + env)
108131
}
109132
val = uint8(i64)
110133
case float64:
111134
f64, err := strconv.ParseFloat(str, 64)
112135
if err != nil {
113-
panic("invalid float64: " + env)
136+
return *new(T), errors.New("invalid float64: " + env)
114137
}
115138
val = f64
116139
case float32:
117140
f64, err := strconv.ParseFloat(str, 32)
118141
if err != nil {
119-
panic("invalid float32: " + env)
142+
return *new(T), errors.New("invalid float32: " + env)
120143
}
121144
val = float32(f64)
122145
case complex128:
123146
c128, err := strconv.ParseComplex(str, 128)
124147
if err != nil {
125-
panic("invalid complex128: " + env)
148+
return *new(T), errors.New("invalid complex128: " + env)
126149
}
127150
val = c128
128151
case complex64:
129152
c128, err := strconv.ParseComplex(str, 64)
130153
if err != nil {
131-
panic("invalid complex64: " + env)
154+
return *new(T), errors.New("invalid complex64: " + env)
132155
}
133156
val = complex64(c128)
134157
default:
135-
panic("unsupported type")
158+
return *new(T), errors.New("unsupported type")
136159
}
137160

138-
return val.(T)
161+
return val.(T), nil
139162
}
140163

141164
// GetWithFallback returns the value of the environment variable or the fallback value if it is not set
@@ -146,10 +169,10 @@ func GetWithFallback[
146169
int | int64 | int32 | int16 | int8 |
147170
uint | uint64 | uint32 | uint16 | uint8 |
148171
float64 | float32 |
149-
complex128 | complex64](env string, fallback T) T {
172+
complex128 | complex64](env string, fallback T) (T, error) {
150173
str := os.Getenv(env)
151174
if str == "" {
152-
return fallback
175+
return fallback, nil
153176
}
154177

155178
return GetOrFail[T](env)
Lines changed: 24 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ func Test_GetOrFail(t *testing.T) {
1111
key string
1212
value string
1313
expected any
14+
wantErr bool
1415
}{
1516
{
1617
name: "rune",
@@ -132,47 +133,53 @@ func Test_GetOrFail(t *testing.T) {
132133
t.Run(tt.name, func(t *testing.T) {
133134
os.Setenv(tt.key, tt.value)
134135
var got any
136+
var err error
135137
switch tt.expected.(type) {
136138
case string:
137-
got = GetOrFail[string](tt.key)
139+
got, err = GetOrFail[string](tt.key)
138140
case bool:
139-
got = GetOrFail[bool](tt.key)
141+
got, err = GetOrFail[bool](tt.key)
140142
case uintptr:
141-
got = GetOrFail[uintptr](tt.key)
143+
got, err = GetOrFail[uintptr](tt.key)
142144
case int:
143-
got = GetOrFail[int](tt.key)
145+
got, err = GetOrFail[int](tt.key)
144146
case int64:
145-
got = GetOrFail[int64](tt.key)
147+
got, err = GetOrFail[int64](tt.key)
146148
case int32:
147-
got = GetOrFail[int32](tt.key)
149+
got, err = GetOrFail[int32](tt.key)
148150
case int16:
149-
got = GetOrFail[int16](tt.key)
151+
got, err = GetOrFail[int16](tt.key)
150152
case int8:
151-
got = GetOrFail[int8](tt.key)
153+
got, err = GetOrFail[int8](tt.key)
152154
case uint:
153-
got = GetOrFail[uint](tt.key)
155+
got, err = GetOrFail[uint](tt.key)
154156
case uint64:
155-
got = GetOrFail[uint64](tt.key)
157+
got, err = GetOrFail[uint64](tt.key)
156158
case uint32:
157-
got = GetOrFail[uint32](tt.key)
159+
got, err = GetOrFail[uint32](tt.key)
158160
case uint16:
159-
got = GetOrFail[uint16](tt.key)
161+
got, err = GetOrFail[uint16](tt.key)
160162
case uint8:
161-
got = GetOrFail[uint8](tt.key)
163+
got, err = GetOrFail[uint8](tt.key)
162164
case float64:
163-
got = GetOrFail[float64](tt.key)
165+
got, err = GetOrFail[float64](tt.key)
164166
case float32:
165-
got = GetOrFail[float32](tt.key)
167+
got, err = GetOrFail[float32](tt.key)
166168
case complex128:
167-
got = GetOrFail[complex128](tt.key)
169+
got, err = GetOrFail[complex128](tt.key)
168170
case complex64:
169-
got = GetOrFail[complex64](tt.key)
171+
got, err = GetOrFail[complex64](tt.key)
170172
default:
171173
t.Fatalf("unsupported type: %T", tt.expected)
172174
}
173175
if got != tt.expected {
174176
t.Errorf("got %v, expected %v", got, tt.expected)
175177
}
178+
if err != nil && !tt.wantErr {
179+
t.Errorf("unexpected error: %v", err)
180+
} else if err == nil && tt.wantErr {
181+
t.Errorf("expected error but got none")
182+
}
176183
})
177184
}
178185
}

env/prefix.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package env
2+
3+
import (
4+
"os"
5+
"strings"
6+
)
7+
8+
// RemovePrefix resets environment variables with a certain prefix without the prefix
9+
// (e.g. added by the cloud provider)
10+
func RemovePrefix(prefix string) {
11+
for _, env := range os.Environ() {
12+
pairs := strings.SplitN(env, "=", 2)
13+
if strings.HasPrefix(pairs[0], prefix) {
14+
newKey := strings.TrimPrefix(pairs[0], prefix)
15+
os.Setenv(newKey, pairs[1])
16+
}
17+
}
18+
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
1919
func randomString(n int) string {
2020
sb := strings.Builder{}
2121
sb.Grow(n)
22-
for i := 0; i < n; i++ {
22+
for range n {
2323
sb.WriteByte(charset[unsafeRand.Intn(len(charset))])
2424
}
2525
return sb.String()

0 commit comments

Comments
 (0)