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

Commit 4dbdb1f

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

File tree

32 files changed

+386
-273
lines changed

32 files changed

+386
-273
lines changed

client/run.go

Lines changed: 11 additions & 3 deletions
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

Lines changed: 16 additions & 4 deletions
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

Lines changed: 36 additions & 0 deletions
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

Lines changed: 31 additions & 1 deletion
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

Lines changed: 7 additions & 7 deletions
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

Lines changed: 15 additions & 7 deletions
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

Lines changed: 13 additions & 8 deletions
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

Lines changed: 11 additions & 11 deletions
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

Lines changed: 11 additions & 10 deletions
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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy