-
-
Notifications
You must be signed in to change notification settings - Fork 89
/
Copy pathserialization.go
134 lines (115 loc) · 3.26 KB
/
serialization.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
package fuego
import (
"context"
"encoding/json"
"encoding/xml"
"errors"
"log/slog"
"net/http"
"reflect"
)
// OutTransformer is an interface for entities that can be transformed.
// Useful for example for trimming strings, changing case, etc.
// Can also raise an error if the entity is not valid.
// Must be implemented by a POINTER RECEIVER.
// Example:
//
// type User struct {
// Name string `json:"name"`
// Password string `json:"password"`
// }
//
// // Not (u User) but (u *User)
//
// func (u *User) OutTransform(context.Context) error {
// u.Name = "M. " + u.Name
// u.Password = "*****"
// return nil
// }
type OutTransformer interface {
OutTransform(context.Context) error // Transforms an entity before sending it.
}
func transformOut[T any](ctx context.Context, ans T) (T, error) {
if reflect.TypeOf(ans).Kind() == reflect.Ptr {
// If ans is a nil pointer, we do not want to transform it.
if reflect.ValueOf(ans).IsNil() {
return ans, nil
}
outTransformer, ok := any(ans).(OutTransformer)
if !ok {
return ans, nil
}
err := outTransformer.OutTransform(ctx)
if err != nil {
return ans, err
}
return outTransformer.(T), nil
}
_, ok := any(ans).(OutTransformer)
if ok {
return ans, errors.New("OutTransformer must be implemented by a POINTER RECEIVER. Please read the [OutTransformer] documentation")
}
outTransformer, ok := any(&ans).(OutTransformer)
if !ok {
return ans, nil
}
err := outTransformer.OutTransform(ctx)
if err != nil {
return ans, err
}
ans = *any(outTransformer).(*T)
return ans, nil
}
// Send sends a string response.
func Send(w http.ResponseWriter, text string) {
_, _ = w.Write([]byte(text))
}
// SendJSON sends a JSON response.
func SendJSON(w http.ResponseWriter, ans any) {
w.Header().Set("Content-Type", "application/json")
err := json.NewEncoder(w).Encode(ans)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
slog.Error("Cannot serialize JSON", "error", err)
_, _ = w.Write([]byte(`{"error":"Cannot serialize JSON"}`))
return
}
}
// SendJSONError sends a JSON error response.
// If the error implements ErrorWithStatus, the status code will be set.
func SendJSONError(w http.ResponseWriter, err error) {
status := http.StatusInternalServerError
var errorStatus ErrorWithStatus
if errors.As(err, &errorStatus) {
status = errorStatus.StatusCode()
}
w.Header().Set("Content-Type", "application/json")
var httpError HTTPError
if errors.As(err, &httpError) {
w.Header().Set("Content-Type", "application/problem+json")
}
w.WriteHeader(status)
SendJSON(w, err)
}
// SendXML sends a XML response.
func SendXML(w http.ResponseWriter, ans any) {
w.Header().Set("Content-Type", "application/xml")
err := xml.NewEncoder(w).Encode(ans)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
slog.Error("Cannot serialize XML", "error", err)
_, _ = w.Write([]byte(`{"error":"Cannot serialize XML"}`))
return
}
}
// SendXMLError sends a XML error response.
// If the error implements ErrorWithStatus, the status code will be set.
func SendXMLError(w http.ResponseWriter, err error) {
status := http.StatusInternalServerError
var errorStatus ErrorWithStatus
if errors.As(err, &errorStatus) {
status = errorStatus.StatusCode()
}
w.WriteHeader(status)
SendXML(w, err)
}