1- import Foundation
21import ArgumentParser
3- import SwiftMETAR
2+ import Foundation
43import METARFormatting
4+ import SwiftMETAR
55
66@available ( macOS 15 . 0 , * )
77@main
@@ -13,31 +13,31 @@ struct DecodeMETAR: AsyncParsableCommand {
1313 in which case it will download the latest METARs from AWC; or it can be used with
1414 a raw METAR string, in which case that string will be parsed.
1515 """ )
16-
16+
1717 @Argument ( help: " The ICAO code of an airport, or a METAR string to decode " )
18- var airportCodeOrMETAR : String ? = nil
19-
20- @Option ( name: [ . customLong( " metar-url " ) , . short] , help: " The URL to load the METAR CSV from. " , transform: { URL ( string: $0) ! } )
18+ var airportCodeOrMETAR : String ?
19+
20+ @Option ( name: [ . customLong( " metar-url " ) , . short] , help: " The URL to load the METAR CSV from. " , transform: { . init ( string: $0) ! } )
2121 var METAR_URL = URL ( string: " https://aviationweather.gov/data/cache/metars.cache.csv " ) !
22-
22+
2323 @Flag ( name: . long, inversion: . prefixedNo, help: " Include raw METAR text " )
2424 var raw = false
25-
25+
2626 @Flag ( name: . shortAndLong, inversion: . prefixedNo, help: " Include remarks " )
2727 var remarks = true
28-
28+
2929 @Flag ( name: . long, help: " Parse and decode all METARs (!) " )
3030 var all = false
31-
31+
3232 @Flag ( name: . long, help: " Only show stations with parsing errors (intended to be used with `--all` " )
3333 var errorsOnly = false
34-
34+
3535 private var session : URLSession { . init( configuration: . ephemeral) }
36-
36+
3737 func run( ) async throws {
3838 try await all ? parseAll ( ) : parsePrompt ( )
3939 }
40-
40+
4141 private func parseAll( ) async throws {
4242 let METARs = try await loadMETARs { raw, error in
4343 print ( raw)
@@ -50,50 +50,50 @@ struct DecodeMETAR: AsyncParsableCommand {
5050 }
5151 }
5252 }
53-
53+
5454 private func parsePrompt( ) async throws {
5555 let airportCodeOrMETAR = promptMETAR ( )
5656 let metar = try await airportCodeOrMETAR. count == 4 ? parse ( code: airportCodeOrMETAR) : parse ( raw: airportCodeOrMETAR)
5757 printMETAR ( metar)
5858 }
59-
59+
6060 private func promptMETAR( ) -> String {
6161 var airportCodeOrMETAR = self . airportCodeOrMETAR
62-
62+
6363 while airportCodeOrMETAR == nil || airportCodeOrMETAR!. isEmpty {
6464 print ( " Enter airport code or METAR: " , terminator: " " )
6565 airportCodeOrMETAR = readLine ( strippingNewline: true )
6666 }
67-
67+
6868 return airportCodeOrMETAR!
6969 }
70-
70+
7171 private func parse( code: String ) async throws -> METAR {
7272 guard let metar = try await getMETAR ( airportCode: code) else {
7373 throw Errors . unknownAirportID ( code)
7474 }
7575 return metar
7676 }
77-
77+
7878 private func parse( raw: String ) async throws -> METAR {
7979 try await METAR . from ( string: raw)
8080 }
81-
82- private func loadMETARs( errorHandler: ( ( String , Swift . Error ) throws -> Void ) ) async throws -> Dictionary < String , METAR > {
81+
82+ private func loadMETARs( errorHandler: ( ( String , Swift . Error ) throws -> Void ) ) async throws -> [ String : METAR ] {
8383 print ( " Loading METARs… " )
8484 print ( )
85-
85+
8686 let ( data, response) = try await session. bytes ( from: METAR_URL)
8787 guard let response = response as? HTTPURLResponse else {
8888 throw Errors . badResponse ( response)
8989 }
90- guard response. statusCode/ 100 == 2 else {
90+ guard response. statusCode / 100 == 2 else {
9191 throw Errors . badStatus ( response: response)
9292 }
93-
94- var METARs = Dictionary < String , METAR > ( )
93+
94+ var METARs = [ String: METAR] ( )
9595 for try await line in data. lines {
96- guard let range = line. rangeOfCharacter ( from: CharacterSet ( charactersIn: " , " ) ) else { continue }
96+ guard let range = line. rangeOfCharacter ( from: CharacterSet ( charactersIn: " , " ) ) else { continue }
9797 let string = String ( line [ line. startIndex..< range. lowerBound] )
9898 guard string. starts ( with: " K " ) else { continue }
9999 do {
@@ -103,26 +103,26 @@ struct DecodeMETAR: AsyncParsableCommand {
103103 try errorHandler ( string, error)
104104 }
105105 }
106-
106+
107107 return METARs
108108 }
109-
109+
110110 private func getMETAR( airportCode: String ) async throws -> METAR ? {
111- let METARs = try await loadMETARs { raw, error in
111+ let METARs = try await loadMETARs { raw, _ in
112112 if raw. starts ( with: airportCode) {
113113 throw Errors . badMETAR ( raw: raw)
114114 }
115115 }
116-
116+
117117 return METARs [ airportCode]
118118 }
119-
119+
120120 private func printMETAR( _ metar: METAR ) {
121121 print ( " Airport: \( metar. stationID) " )
122122 if raw, let text = metar. text {
123123 print ( text)
124124 }
125-
125+
126126 lprint ( " Issued: \( metar. date, format: . dateTime) ( \( metar. issuance, format: . issuance) ) " )
127127 lprint ( " Observer: \( metar. observer, format: . observer) " )
128128 if let wind = metar. wind {
@@ -149,21 +149,21 @@ struct DecodeMETAR: AsyncParsableCommand {
149149 if let altimeter = metar. altimeter? . measurement {
150150 lprint ( " Altimeter: \( altimeter, format: . measurement( width: . abbreviated, usage: . asProvided) ) " )
151151 }
152-
152+
153153 if remarks {
154154 print ( )
155155 printRemarks ( metar: metar)
156156 }
157-
157+
158158 print ( )
159159 }
160-
160+
161161 private func printRemarks( metar: METAR ) {
162162 for remark in metar. remarks. sorted ( using: RemarkComparator ( ) ) {
163163 print ( RemarkEntry . FormatStyle. remark ( ) . format ( remark) )
164164 }
165165 }
166-
166+
167167 private func lprint( _ str: LocalizedStringResource ) {
168168 print ( String ( localized: str) )
169169 }
@@ -185,7 +185,7 @@ extension Errors: LocalizedError {
185185 case let . badMETAR( raw) : " Couldn’t parse METAR: \( raw) "
186186 }
187187 }
188-
188+
189189 var failureReason : String ? {
190190 switch self {
191191 case . badResponse, . badStatus: " The AWC API may have changed, or may not be functioning properly. "
0 commit comments