From 56739125b358c74964d49aaaa1bd22d996b43de4 Mon Sep 17 00:00:00 2001
From: Paul Brinkmeier <hallo@pbrinkmeier.de>
Date: Thu, 18 Jul 2024 20:13:21 +0200
Subject: [PATCH] Add Departure type

---
 README.md |   4 +-
 main.go   | 111 +++++++++++++++++++++++++++++++++++-------------------
 2 files changed, 76 insertions(+), 39 deletions(-)

diff --git a/README.md b/README.md
index 8ce6546..ac5a864 100644
--- a/README.md
+++ b/README.md
@@ -6,5 +6,7 @@
 - [x] Add basic auth
 - [x] Create Nix package
 - [x] Create container
-- [ ] Write ESP8266 client
+- [x] Write ESP8266 client
 - [ ] Make port configurable
+- [ ] Transfer using JSON
+- [ ] Correctly implement basic auth
diff --git a/main.go b/main.go
index b566d11..a005775 100644
--- a/main.go
+++ b/main.go
@@ -1,6 +1,7 @@
 package main
 
 import "encoding/json"
+import "errors"
 import "fmt"
 import "log"
 import "net/http"
@@ -10,8 +11,10 @@ import "slices"
 import "strconv"
 import "time"
 
+// JSON unmarshaling types for departure monitor API
+
 type DMResponse struct {
-	Departures []DMDeparture `json:"departureList"`
+	DepartureList []DMDeparture `json:"departureList"`
 }
 
 type DMDeparture struct {
@@ -40,11 +43,11 @@ type DMDateTime struct {
 	Minute string `json:"minute"`
 }
 
-func FetchDepartures(stopId string) (*DMResponse, error) {
+func FetchDepartures(stopId string) (DMResponse, error) {
 	// Create request object
 	req, err := http.NewRequest("GET", "https://www.vrn.de/mngvrn/XML_DM_REQUEST", nil)
 	if err != nil {
-		return nil, err
+		return DMResponse{}, err
 	}
 
 	// Configure our request
@@ -68,17 +71,72 @@ func FetchDepartures(stopId string) (*DMResponse, error) {
 	}
 	res, err := client.Do(req)
 	if err != nil {
-		return nil, err
+		return DMResponse{}, err
 	}
 
 	defer res.Body.Close()
 	var dmResponse DMResponse
 	err = json.NewDecoder(res.Body).Decode(&dmResponse)
 	if err != nil {
-		return nil, err
+		return DMResponse{}, err
 	}
 
-	return &dmResponse, nil
+	return dmResponse, nil
+}
+
+// Types for JSON marshaling
+
+type Departures struct {
+	Departures []Departure `json:"departures"`
+}
+
+type Departure struct {
+	Symbol    string `json:"symbol"`
+	Direction string `json:"direction"`
+	Leaving   string `json:"leaving"`
+}
+
+func ParseDepartures(response DMResponse, allowedPlatform *string) (Departures, error) {
+	var ds []Departure
+
+	for _, d := range response.DepartureList {
+		direction := d.ServingLine.Direction
+		if len(d.OnwardStopSeq) != 0 {
+			last := d.OnwardStopSeq[len(d.OnwardStopSeq)-1]
+			if slices.Contains([]string{"5", "6"}, last.PlaceID) {
+				direction = last.NameWO
+			}
+		}
+
+		leaving := fmt.Sprintf("%s min", d.Countdown)
+		countdown, err := strconv.Atoi(d.Countdown)
+		if err != nil {
+			return Departures{}, errors.New("could not parse countdown")
+		}
+
+		if countdown == 0 {
+			leaving = "sofort"
+		}
+		if countdown > 20 {
+			dt := d.DateTime
+			if d.RealDateTime != nil {
+				dt = *d.RealDateTime
+			}
+			leaving = fmt.Sprintf("%02s:%02s", dt.Hour, dt.Minute)
+		}
+
+		if allowedPlatform != nil && d.Platform != *allowedPlatform {
+			continue
+		}
+
+		ds = append(ds, Departure{
+			d.ServingLine.Symbol,
+			direction,
+			leaving,
+		})
+	}
+
+	return Departures{ds}, nil
 }
 
 func main() {
@@ -113,40 +171,17 @@ func main() {
 			return
 		}
 
-		for _, d := range ds.Departures {
-			if platform != nil {
-				if d.Platform != *platform {
-					continue
-				}
-			}
-
-			direction := d.ServingLine.Direction
-			if len(d.OnwardStopSeq) != 0 {
-				last := d.OnwardStopSeq[len(d.OnwardStopSeq)-1]
-				if slices.Contains([]string{"5", "6"}, last.PlaceID) {
-					direction = last.NameWO
-				}
-			}
-
-			leaving := fmt.Sprintf("%s min", d.Countdown)
-			countdown, err := strconv.Atoi(d.Countdown)
-			if err == nil {
-				if countdown == 0 {
-					leaving = "sofort"
-				}
-				if countdown > 20 {
-					dt := d.DateTime
-					if d.RealDateTime != nil {
-						dt = *d.RealDateTime
-					}
-					leaving = fmt.Sprintf("%02s:%02s", dt.Hour, dt.Minute)
-				}
-			}
+		departures, err := ParseDepartures(ds, platform)
+		if err != nil {
+			http.Error(w, err.Error(), http.StatusInternalServerError)
+			return
+		}
 
+		for _, d := range departures.Departures {
 			fmt.Fprintf(w, "%2s %-11s %6s\n",
-				d.ServingLine.Symbol,
-				direction[:min(len(direction), 11)],
-				leaving,
+				d.Symbol,
+				d.Direction[:min(11, len(d.Direction))],
+				d.Leaving,
 			)
 		}
 	})