diff --git a/internal/parsers/aviation_parser.go b/internal/parsers/aviation_parser.go index 84e3cf9..e31ef5b 100644 --- a/internal/parsers/aviation_parser.go +++ b/internal/parsers/aviation_parser.go @@ -2,67 +2,37 @@ package parsers import ( "caatsm/internal/domain" + "errors" // Import errors package to handle errors "strings" ) +const ( + StartIndicatorPrefix = "ZCZC" + EndHeaderMarker = "." + BeginPartMarker = "BEGIN PART" +) + // ParseHeader parses the header of the message and returns a ParsedMessage struct -func ParseHeader(fullMessage string) domain.ParsedMessage { - var ( - startIndicator string - messageID string - dateTime string - priorityIndicator string - primaryAddress string - secondaryAddresses []string - originator string - originatorDateTime string - bodyAndFooter strings.Builder - headerEnded bool - ) +func ParseHeader(fullMessage string) (domain.ParsedMessage, error) { + fullMessage = strings.TrimSpace(fullMessage) // Trim leading and trailing spaces + lines := strings.Split(fullMessage, "\n") // Split the message into lines - fullMessage = strings.TrimSpace(fullMessage) - lines := strings.Split(fullMessage, "\n") - - for lineCounter, line := range lines { - switch lineCounter { - case 0: - if strings.HasPrefix(line, "ZCZC") { - parts := strings.Fields(line) - if len(parts) >= 3 { - startIndicator = parts[0] - messageID = parts[1] - dateTime = parts[2] - } - } - case 1: - parts := strings.Fields(line) - if len(parts) >= 2 { - priorityIndicator = parts[0] - primaryAddress = parts[1] - } - default: - if !headerEnded { - if strings.TrimSpace(line) == "." { - continue - } else if strings.HasPrefix(line, ".") { - originatorInfo := strings.Fields(line[1:]) - if len(originatorInfo) >= 2 { - originator = originatorInfo[0] - originatorDateTime = originatorInfo[1] - } - headerEnded = true - } else if strings.HasPrefix(line, "BEGIN PART") || strings.HasPrefix(line, "(") { - headerEnded = true - bodyAndFooter.WriteString(line + "\n") - } else { - secondaryAddresses = append(secondaryAddresses, line) - } - } else { - bodyAndFooter.WriteString(line + "\n") - } - } + // Parse the start indicator line + startIndicator, messageID, dateTime, err := parseStartIndicator(lines[0]) + if err != nil { + return domain.ParsedMessage{}, err // Return error if parsing fails } + // Parse the priority indicator and primary address line + priorityIndicator, primaryAddress, err := parsePriorityAndPrimary(lines[1]) + if err != nil { + return domain.ParsedMessage{}, err // Return error if parsing fails + } + + // Parse the remaining lines + secondaryAddresses, originator, originatorDateTime, bodyAndFooter := parseRemainingLines(lines[2:]) + + // Return the parsed message as a domain.ParsedMessage struct return domain.ParsedMessage{ StartIndicator: startIndicator, MessageID: messageID, @@ -72,6 +42,67 @@ func ParseHeader(fullMessage string) domain.ParsedMessage { SecondaryAddresses: secondaryAddresses, Originator: originator, OriginatorDateTime: originatorDateTime, - BodyAndFooter: bodyAndFooter.String(), + BodyAndFooter: bodyAndFooter, + }, nil +} + +// parseStartIndicator parses the start indicator line +func parseStartIndicator(line string) (string, string, string, error) { + if strings.HasPrefix(line, StartIndicatorPrefix) { + parts := strings.Fields(line) + if len(parts) >= 3 { + return parts[0], parts[1], parts[2], nil + } } + return "", "", "", errors.New("invalid start indicator line format") +} + +// parsePriorityAndPrimary parses the priority indicator and primary address line +func parsePriorityAndPrimary(line string) (string, string, error) { + parts := strings.Fields(line) + if len(parts) >= 2 { + return parts[0], parts[1], nil + } + return "", "", errors.New("invalid priority indicator line format") +} + +// parseRemainingLines parses the remaining lines of the message +func parseRemainingLines(lines []string) ([]string, string, string, string) { + var ( + secondaryAddresses []string + originator string + originatorDateTime string + bodyAndFooter strings.Builder + headerEnded bool + ) + + for _, line := range lines { + line = strings.TrimSpace(line) + if headerEnded { + // Append line to body and footer if header has ended + bodyAndFooter.WriteString(line + "\n") + } else { + switch { + case line == EndHeaderMarker: + // Ignore end header marker + case strings.HasPrefix(line, "."): + // Parse originator and originatorDateTime + originatorInfo := strings.Fields(line[1:]) + if len(originatorInfo) >= 2 { + originator = originatorInfo[0] + originatorDateTime = originatorInfo[1] + } + headerEnded = true + case strings.HasPrefix(line, BeginPartMarker) || strings.HasPrefix(line, "("): + // Mark header as ended and append line to body and footer + headerEnded = true + bodyAndFooter.WriteString(line + "\n") + default: + // Append line to secondary addresses + secondaryAddresses = append(secondaryAddresses, line) + } + } + } + + return secondaryAddresses, originator, originatorDateTime, bodyAndFooter.String() } diff --git a/internal/parsers/aviation_parser_test.go b/internal/parsers/aviation_parser_test.go index 69e99ca..ed7569b 100644 --- a/internal/parsers/aviation_parser_test.go +++ b/internal/parsers/aviation_parser_test.go @@ -1,6 +1,8 @@ package parsers import ( + "fmt" + . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) @@ -26,7 +28,8 @@ THUNDERSTORMS EXPECTED ALTERNATE ROUTES ADVISED) NNNN` - parsedHeader := ParseHeader(message) + parsedHeader, err := ParseHeader(message) + Expect(err).ToNot(HaveOccurred()) Expect(parsedHeader.StartIndicator).To(Equal("ZCZC")) Expect(parsedHeader.MessageID).To(Equal("TAF6789")) Expect(parsedHeader.DateTime).To(Equal("160530")) @@ -68,7 +71,8 @@ SEVERE THUNDERSTORM FORECASTED ALL DEPARTURES/ARRIVALS EXPECTED TO BE DELAYED) NNNN` - parsedHeader := ParseHeader(message) + parsedHeader, err := ParseHeader(message) + Expect(err).ToNot(HaveOccurred()) Expect(parsedHeader.StartIndicator).To(Equal("ZCZC")) Expect(parsedHeader.MessageID).To(Equal("NOTAM1122")) Expect(parsedHeader.DateTime).To(Equal("171000")) @@ -94,10 +98,12 @@ Q) EDMM/QOATT/IV/BO/A/000/999/4814N01120E005 A) EDDM B) 2307150600 C) 2307151800 E) AERODROME CONTROL TOWER HOURS OF SERVICE - 0600-1800 DUE TO MAINTENANCE -NNNN` + 0600-1800 DUE TO MAINTENANCE +NNNN +` - parsedMessage := ParseHeader(message) + parsedMessage, err := ParseHeader(message) + Expect(err).ToNot(HaveOccurred()) Expect(parsedMessage.StartIndicator).To(Equal("ZCZC")) Expect(parsedMessage.MessageID).To(Equal("NOTAM1234")) Expect(parsedMessage.DateTime).To(Equal("230715")) @@ -106,7 +112,15 @@ NNNN` Expect(parsedMessage.SecondaryAddresses).To(Equal([]string{"GG EDDNYNYX"})) Expect(parsedMessage.Originator).To(Equal("BERLINTWR")) Expect(parsedMessage.OriginatorDateTime).To(Equal("230714")) - Expect(parsedMessage.BodyAndFooter).To(ContainSubstring("Q) EDMM/QOATT/IV/BO/A/000/999/4814N01120E005\nA) EDDM\nB) 2307150600 C) 2307151800\nE) AERODROME CONTROL TOWER HOURS OF SERVICE\n 0600-1800 DUE TO MAINTENANCE\nNNNN")) + fmt.Print(parsedMessage.BodyAndFooter) + Expect(parsedMessage.BodyAndFooter).To(Equal(` +Q) EDMM/QOATT/IV/BO/A/000/999/4814N01120E005 +A) EDDM +B) 2307150600 C) 2307151800 +E) AERODROME CONTROL TOWER HOURS OF SERVICE +0600-1800 DUE TO MAINTENANCE +NNNN +`)) }) }) })