Skip to content
This repository was archived by the owner on Dec 28, 2024. It is now read-only.

Commit 4ccde7f

Browse files
committed
Make all services output proper JSON
1 parent 4797aa0 commit 4ccde7f

File tree

31 files changed

+381
-269
lines changed

31 files changed

+381
-269
lines changed

client/run.go

+11-3
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@ package main
22

33
import (
44
"bytes"
5+
"encoding/json"
56
"flag"
67
"fmt"
8+
"github.com/terminalnode/adventofcode2024/common/util"
79
"io"
810
"net/http"
911
"os"
@@ -64,17 +66,23 @@ func runSolution(
6466
url string,
6567
day int,
6668
part int,
67-
input string,
69+
rawInput string,
6870
) (string, error) {
6971
url = fmt.Sprintf("http://%s/day%02d/%d", url, day, part)
7072
fmt.Printf("Running day %d, part %d with URL '%s'\n", day, part, url)
7173
client := &http.Client{}
7274

73-
req, err := http.NewRequest("POST", url, bytes.NewBuffer([]byte(input)))
75+
input := util.AocInput{Input: rawInput}
76+
jsonData, err := json.Marshal(input)
77+
if err != nil {
78+
return "", fmt.Errorf("failed to marshal JSON: %v", err)
79+
}
80+
81+
req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonData))
7482
if err != nil {
7583
return "", err
7684
}
77-
req.Header.Set("Content-Type", "text/plain")
85+
req.Header.Set("Content-Type", "application/json")
7886

7987
resp, err := client.Do(req)
8088
if err != nil {

common/proto/grpc.go

+16-4
Original file line numberDiff line numberDiff line change
@@ -41,18 +41,30 @@ func CreateGRPCServer(
4141
return grpcServer
4242
}
4343

44+
// This part of the architecture is a bit #YOLO
45+
// But at least I got to play around a little with gRPC
46+
// Throughout the entire process I've only used the Kubernetes solution over HTTP
47+
4448
func (s *server) SolvePart1(
4549
ctx context.Context,
4650
req *InputData,
4751
) (*InputResponse, error) {
48-
result := s.solvePart1(req.Input)
49-
return &InputResponse{Result: result}, nil
52+
result, err := s.solvePart1(util.AocInput{Input: req.Input})
53+
if s := result.Solution; s != "" {
54+
return &InputResponse{Result: s}, nil
55+
} else {
56+
return &InputResponse{Result: err.Error()}, nil
57+
}
5058
}
5159

5260
func (s *server) SolvePart2(
5361
ctx context.Context,
5462
req *InputData,
5563
) (*InputResponse, error) {
56-
result := s.solvePart2(req.Input)
57-
return &InputResponse{Result: result}, nil
64+
result, err := s.solvePart2(util.AocInput{Input: req.Input})
65+
if s := result.Solution; s != "" {
66+
return &InputResponse{Result: s}, nil
67+
} else {
68+
return &InputResponse{Result: err.Error()}, nil
69+
}
5870
}

common/util/aoc_error.go

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package util
2+
3+
type ErrorType int
4+
5+
const (
6+
NotImplemented ErrorType = iota
7+
InputParsingError
8+
ParsingError
9+
StringToNumber
10+
ProcessingError
11+
)
12+
13+
func (et ErrorType) String() string {
14+
switch et {
15+
case NotImplemented:
16+
return "NotImplemented"
17+
case ParsingError:
18+
return "ParsingError"
19+
case StringToNumber:
20+
return "StringToNumber"
21+
default:
22+
return "UnknownError"
23+
}
24+
}
25+
26+
type AocError struct {
27+
Message string `json:"message"`
28+
Type string `json:"type"`
29+
IsError bool `json:"-"`
30+
}
31+
32+
func (
33+
e AocError,
34+
) Error() string {
35+
return e.Message
36+
}

common/util/solution.go

+31-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,33 @@
11
package util
22

3-
type Solution = func(string) string
3+
import "fmt"
4+
5+
type AocInput struct {
6+
Input string `json:"input"`
7+
}
8+
9+
type AocSolution struct {
10+
Solution string `json:"solution"`
11+
}
12+
13+
type Solution = func(AocInput) (AocSolution, AocError)
14+
15+
func NewAocError(
16+
m string,
17+
t ErrorType,
18+
) (AocSolution, AocError) {
19+
return AocSolution{}, AocError{Message: m, Type: t.String()}
20+
}
21+
22+
func NewAocSolution(
23+
solution string,
24+
) (AocSolution, AocError) {
25+
return AocSolution{Solution: solution}, AocError{}
26+
}
27+
28+
func FormatAocSolution(
29+
format string,
30+
a ...any,
31+
) (AocSolution, AocError) {
32+
return NewAocSolution(fmt.Sprintf(format, a...))
33+
}

common/web/http_util.go

+7-7
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
package web
22

33
import (
4+
"encoding/json"
45
"fmt"
5-
"io"
6+
"github.com/terminalnode/adventofcode2024/common/util"
67
"net/http"
78
)
89

@@ -13,12 +14,11 @@ func addPrefix(prefix string, url string) string {
1314
return fmt.Sprintf("/%s%s", prefix, url)
1415
}
1516

16-
func readInput(r *http.Request) (string, error) {
17-
body, err := io.ReadAll(r.Body)
18-
if err != nil {
19-
return "", err
17+
func readInput(r *http.Request) (util.AocInput, error) {
18+
var input util.AocInput
19+
if err := json.NewDecoder(r.Body).Decode(&input); err != nil {
20+
return util.AocInput{}, err
2021
}
21-
2222
defer r.Body.Close()
23-
return string(body), nil
23+
return input, nil
2424
}

common/web/solution_handler.go

+15-7
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package web
22

33
import (
4+
"encoding/json"
5+
"errors"
46
"fmt"
57
"github.com/terminalnode/adventofcode2024/common/util"
68
"net/http"
@@ -9,7 +11,7 @@ import (
911
func createSolutionHandler(
1012
day int,
1113
part int,
12-
solution func(string) string,
14+
solution util.Solution,
1315
) func(http.ResponseWriter, *http.Request) {
1416
if solution == nil {
1517
solution = defaultSolutionHandler(day, part)
@@ -27,10 +29,13 @@ func createSolutionHandler(
2729
return
2830
}
2931

30-
result := solution(input)
31-
if _, err = w.Write([]byte(result)); err != nil {
32-
http.Error(w, "Error", http.StatusInternalServerError)
33-
return
32+
aocSolution, aocErr := solution(input)
33+
w.Header().Set("Content-Type", "application/json")
34+
encoder := json.NewEncoder(w)
35+
if errors.Is(err, util.AocError{}) {
36+
encoder.Encode(aocErr)
37+
} else {
38+
encoder.Encode(aocSolution)
3439
}
3540
}
3641
}
@@ -39,7 +44,10 @@ func defaultSolutionHandler(
3944
day int,
4045
part int,
4146
) util.Solution {
42-
return func(input string) string {
43-
return fmt.Sprintf("Solution for day %d part %d not implemented yet", day, part)
47+
return func(input util.AocInput) (util.AocSolution, util.AocError) {
48+
return util.NewAocError(
49+
fmt.Sprintf("Solution for day %d part %d not implemented yet", day, part),
50+
util.NotImplemented,
51+
)
4452
}
4553
}

solutions/day01/main.go

+13-8
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package main
33
import (
44
"fmt"
55
"github.com/terminalnode/adventofcode2024/common"
6+
"github.com/terminalnode/adventofcode2024/common/util"
67
"slices"
78
"strconv"
89
"strings"
@@ -40,10 +41,12 @@ func createLists(input string) ([]int, []int, error) {
4041
return left, right, nil
4142
}
4243

43-
func part1(input string) string {
44-
left, right, err := createLists(input)
44+
func part1(
45+
input util.AocInput,
46+
) (util.AocSolution, util.AocError) {
47+
left, right, err := createLists(input.Input)
4548
if err != nil {
46-
return err.Error()
49+
return util.NewAocError(err.Error(), util.InputParsingError)
4750
}
4851

4952
// Sort the lists
@@ -60,13 +63,15 @@ func part1(input string) string {
6063
sum += diff
6164
}
6265

63-
return fmt.Sprintf("Result for part 1: %d", sum)
66+
return util.FormatAocSolution("Result for part 1: %d", sum)
6467
}
6568

66-
func part2(input string) string {
67-
left, right, err := createLists(input)
69+
func part2(
70+
input util.AocInput,
71+
) (util.AocSolution, util.AocError) {
72+
left, right, err := createLists(input.Input)
6873
if err != nil {
69-
return err.Error()
74+
return util.NewAocError(err.Error(), util.InputParsingError)
7075
}
7176

7277
rightMap := make(map[int]int)
@@ -79,5 +84,5 @@ func part2(input string) string {
7984
sum += l * rightMap[l]
8085
}
8186

82-
return fmt.Sprintf("Result for part 2: %d", sum)
87+
return util.FormatAocSolution("Result for part 2: %d", sum)
8388
}

solutions/day02/main.go

+11-11
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
package main
22

33
import (
4-
"fmt"
54
"github.com/terminalnode/adventofcode2024/common"
5+
"github.com/terminalnode/adventofcode2024/common/util"
66
"strconv"
77
"strings"
88
)
@@ -119,23 +119,23 @@ func countSafe(
119119
}
120120

121121
func part1(
122-
input string,
123-
) string {
124-
reports, err := parseAllReports(input, false)
122+
input util.AocInput,
123+
) (util.AocSolution, util.AocError) {
124+
reports, err := parseAllReports(input.Input, false)
125125
if err != nil {
126-
return fmt.Sprintf("Failed to parse reports: %v", err)
126+
return util.NewAocError(err.Error(), util.InputParsingError)
127127
}
128128

129-
return fmt.Sprintf("Number of safe reports: %d", countSafe(reports))
129+
return util.FormatAocSolution("Number of safe reports: %d", countSafe(reports))
130130
}
131131

132132
func part2(
133-
input string,
134-
) string {
135-
reports, err := parseAllReports(input, true)
133+
input util.AocInput,
134+
) (util.AocSolution, util.AocError) {
135+
reports, err := parseAllReports(input.Input, true)
136136
if err != nil {
137-
return fmt.Sprintf("Failed to parse reports: %v", err)
137+
return util.NewAocError(err.Error(), util.InputParsingError)
138138
}
139139

140-
return fmt.Sprintf("Number of safe reports: %d", countSafe(reports))
140+
return util.FormatAocSolution("Number of safe reports: %d", countSafe(reports))
141141
}

solutions/day03/main.go

+11-10
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package main
33
import (
44
"fmt"
55
"github.com/terminalnode/adventofcode2024/common"
6+
"github.com/terminalnode/adventofcode2024/common/util"
67
"regexp"
78
"strconv"
89
)
@@ -14,9 +15,9 @@ func main() {
1415
}
1516

1617
func part1(
17-
input string,
18-
) string {
19-
finds := r.FindAllSubmatch([]byte(input), -1)
18+
input util.AocInput,
19+
) (util.AocSolution, util.AocError) {
20+
finds := r.FindAllSubmatch([]byte(input.Input), -1)
2021
sum := 0
2122
for _, match := range finds {
2223
verb := string(match[1])
@@ -26,17 +27,17 @@ func part1(
2627

2728
multiplied, err := mul(string(match[3]), string(match[4]))
2829
if err != nil {
29-
return fmt.Sprintf("Failed to parse multiplication of %q:\n%v\n", match, err)
30+
return util.NewAocError(fmt.Sprintf("Failed to parse multiplication of %q:\n%v\n", match, err), util.ParsingError)
3031
}
3132
sum += multiplied
3233
}
33-
return fmt.Sprintf("Result: %d", sum)
34+
return util.FormatAocSolution("Result: %d", sum)
3435
}
3536

3637
func part2(
37-
input string,
38-
) string {
39-
finds := r.FindAllSubmatch([]byte(input), -1)
38+
input util.AocInput,
39+
) (util.AocSolution, util.AocError) {
40+
finds := r.FindAllSubmatch([]byte(input.Input), -1)
4041
sum := 0
4142

4243
enabled := true
@@ -47,7 +48,7 @@ func part2(
4748
if enabled {
4849
multiplied, err := mul(string(match[3]), string(match[4]))
4950
if err != nil {
50-
return fmt.Sprintf("Failed to parse multiplication of %q:\n%v\n", match, err)
51+
return util.NewAocError(fmt.Sprintf("Failed to parse multiplication of %q:\n%v\n", match, err), util.ParsingError)
5152
}
5253
sum += multiplied
5354
}
@@ -58,7 +59,7 @@ func part2(
5859
}
5960
}
6061

61-
return fmt.Sprintf("Result: %d", sum)
62+
return util.FormatAocSolution("Result: %d", sum)
6263
}
6364

6465
func mul(sub1 string, sub2 string) (int, error) {

0 commit comments

Comments
 (0)