From 301ba77e17f99eed4a734c47c13f0c4024dc91b7 Mon Sep 17 00:00:00 2001 From: Tim Morgan Date: Tue, 19 May 2026 12:58:29 -0700 Subject: [PATCH] Add Periphery config and CI workflow; remove dead code Adds .periphery.yml and .github/workflows/periphery.yml, and removes unused declarations/imports flagged by Periphery. Public API preserved for libraries; Codable/@objc/reflection/protocol-witness symbols retained. All tests pass with no regressions. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/periphery.yml | 14 +++++++++ .periphery.yml | 1 + .../Common/Parsers/DateParser.swift | 16 ---------- .../Common/Parsers/VisibilityParser.swift | 23 -------------- .../METAR/Parsers/METARXMLParser.swift | 5 --- .../Parsers/Types/NavalForecasterParser.swift | 4 --- .../Types/VicinityPrecipitationParser.swift | 30 ------------------ .../Parsers/Types/WindChangeParser.swift | 2 -- Sources/SwiftMETAR/Support/Date.swift | 13 -------- Sources/SwiftMETAR/Support/Ratio.swift | 6 ---- .../Support/String+SwiftMETAR.swift | 11 ------- Sources/SwiftMETAR/Support/XMLParsing.swift | 31 ------------------- .../SwiftMETAR/TAF/Parsers/TAFXMLParser.swift | 5 --- 13 files changed, 15 insertions(+), 146 deletions(-) create mode 100644 .github/workflows/periphery.yml create mode 100644 .periphery.yml delete mode 100644 Sources/SwiftMETAR/Remarks/Parsers/Types/VicinityPrecipitationParser.swift diff --git a/.github/workflows/periphery.yml b/.github/workflows/periphery.yml new file mode 100644 index 0000000..48ab9c1 --- /dev/null +++ b/.github/workflows/periphery.yml @@ -0,0 +1,14 @@ +name: Periphery +on: + push: + branches: [main] + pull_request: +jobs: + periphery: + runs-on: macos-latest + steps: + - uses: actions/checkout@v4 + - name: Install Periphery + run: brew install peripheryapp/periphery/periphery + - name: Run Periphery + run: periphery scan diff --git a/.periphery.yml b/.periphery.yml new file mode 100644 index 0000000..85b884a --- /dev/null +++ b/.periphery.yml @@ -0,0 +1 @@ +retain_public: true diff --git a/Sources/SwiftMETAR/Common/Parsers/DateParser.swift b/Sources/SwiftMETAR/Common/Parsers/DateParser.swift index 2a7de5b..10ddc31 100644 --- a/Sources/SwiftMETAR/Common/Parsers/DateParser.swift +++ b/Sources/SwiftMETAR/Common/Parsers/DateParser.swift @@ -220,22 +220,6 @@ class HourMinutePeriodParser { class HourMinuteParser { private let dateRef = Reference<(UInt8?, UInt8)>() - lazy var hourRequiredRx = Regex { - Capture(as: dateRef) { - Repeat(.digit, count: 4) - } transform: { string in - let hourRange = string.startIndex...string.index(string.startIndex, offsetBy: 1) - let minuteRange = - string.index(string.startIndex, offsetBy: 2)...string.index(string.startIndex, offsetBy: 3) - - guard let hour = UInt8(string[hourRange]), - var minute = UInt8(string[minuteRange]) - else { throw Error.invalidDate(String(string)) } - - return (hour, minute) - } - } - lazy var hourOptionalRx = Regex { Capture(as: dateRef) { Optionally { Repeat(.digit, count: 2) } diff --git a/Sources/SwiftMETAR/Common/Parsers/VisibilityParser.swift b/Sources/SwiftMETAR/Common/Parsers/VisibilityParser.swift index a569b91..9ac5f1e 100644 --- a/Sources/SwiftMETAR/Common/Parsers/VisibilityParser.swift +++ b/Sources/SwiftMETAR/Common/Parsers/VisibilityParser.swift @@ -5,7 +5,6 @@ import NumberKit class VisibilityParser { private let integerParser = IntegerDistanceParser() private let fractionParser = FractionalDistanceParser() - private let visibilityRef = Reference() private lazy var integerRx = Regex { Anchor.startOfSubject @@ -29,20 +28,6 @@ class VisibilityParser { Anchor.endOfSubject } - lazy var rx = Regex { - Capture(as: visibilityRef) { - ChoiceOf { - ChoiceOf { - "10SM" - "9999" - } - notRecordedRx - fractionRx - integerRx - } - } - } - func parse(_ parts: inout [String.SubSequence]) throws -> Visibility? { guard !parts.isEmpty else { return nil } @@ -61,14 +46,6 @@ class VisibilityParser { return nil } - func parse(_ match: Regex.Match) throws -> Visibility? { - let visStr = match[visibilityRef] - guard let visibility = try parse(String(visStr)) else { - preconditionFailure("Visibility rx should have captured parseable substring") - } - return visibility - } - private func parse(_ vizStr: String) throws -> Visibility? { if vizStr == "CAVOK" { return .greaterThan(.meters(9999)) diff --git a/Sources/SwiftMETAR/METAR/Parsers/METARXMLParser.swift b/Sources/SwiftMETAR/METAR/Parsers/METARXMLParser.swift index 049c143..6c77d19 100644 --- a/Sources/SwiftMETAR/METAR/Parsers/METARXMLParser.swift +++ b/Sources/SwiftMETAR/METAR/Parsers/METARXMLParser.swift @@ -120,7 +120,6 @@ actor METARXMLParser { var entries = [Entry]() private var currentEntry: Entry? - private var currentElement: String? private var characterBuffer = "" private var inQualityControlFlags = false @@ -131,8 +130,6 @@ actor METARXMLParser { qualifiedName _: String?, attributes attributeDict: [String: String] = [:] ) { - currentElement = elementName - if elementName == "METAR" { currentEntry = Entry() } else if elementName == "quality_control_flags" { @@ -205,8 +202,6 @@ actor METARXMLParser { } } } - - currentElement = nil } func parser(_: XMLParser, foundCharacters string: String) { diff --git a/Sources/SwiftMETAR/Remarks/Parsers/Types/NavalForecasterParser.swift b/Sources/SwiftMETAR/Remarks/Parsers/Types/NavalForecasterParser.swift index 15cc53b..b2d3742 100644 --- a/Sources/SwiftMETAR/Remarks/Parsers/Types/NavalForecasterParser.swift +++ b/Sources/SwiftMETAR/Remarks/Parsers/Types/NavalForecasterParser.swift @@ -1,10 +1,6 @@ import Foundation @preconcurrency import RegexBuilder -private let nocapWeatherCenterRx = Remark.NavalWeatherCenter.allCases.map(\.rawValue).joined( - separator: "|" -) - final class NavalForecasterParser: RemarkParser { var urgency = Remark.Urgency.routine diff --git a/Sources/SwiftMETAR/Remarks/Parsers/Types/VicinityPrecipitationParser.swift b/Sources/SwiftMETAR/Remarks/Parsers/Types/VicinityPrecipitationParser.swift deleted file mode 100644 index e16e29d..0000000 --- a/Sources/SwiftMETAR/Remarks/Parsers/Types/VicinityPrecipitationParser.swift +++ /dev/null @@ -1,30 +0,0 @@ -import Foundation -@preconcurrency import RegexBuilder - -final class VicinityPrecipitationParser: RemarkParser { - var urgency = Remark.Urgency.routine - - private let precipRef = Reference() - - // swiftlint:disable force_try - private lazy var rx = Regex { - Anchor.wordBoundary - "VC" - Capture(as: precipRef) { - try! Remark.ObservedPrecipitationType.rx - } transform: { - .init(rawValue: String($0))! - } - Anchor.wordBoundary - } - // swiftlint:enable force_try - - func parse(remarks: inout String, date _: DateComponents) throws -> Remark? { - guard let result = try rx.firstMatch(in: remarks) else { return nil } - - let precip = result[precipRef] - - remarks.removeSubrange(result.range) - return .observedPrecipitation(type: precip, proximity: .vicinity, directions: Set()) - } -} diff --git a/Sources/SwiftMETAR/Remarks/Parsers/Types/WindChangeParser.swift b/Sources/SwiftMETAR/Remarks/Parsers/Types/WindChangeParser.swift index 886914a..07ce81a 100644 --- a/Sources/SwiftMETAR/Remarks/Parsers/Types/WindChangeParser.swift +++ b/Sources/SwiftMETAR/Remarks/Parsers/Types/WindChangeParser.swift @@ -1,8 +1,6 @@ import Foundation @preconcurrency import RegexBuilder -private let nocapExtremesRegex = Remark.Extreme.allCases.map(\.rawValue).joined(separator: "|") - final class WindChangeParser: RemarkParser { var urgency = Remark.Urgency.routine diff --git a/Sources/SwiftMETAR/Support/Date.swift b/Sources/SwiftMETAR/Support/Date.swift index bd11d6c..2cc2344 100644 --- a/Sources/SwiftMETAR/Support/Date.swift +++ b/Sources/SwiftMETAR/Support/Date.swift @@ -36,19 +36,6 @@ extension Date { func next(_ components: DateComponents) -> Date? { return zuluCal.nextDate(after: self, matching: components, matchingPolicy: .nextTime) } - - func next( - year: Int? = nil, - month: Int? = nil, - day: Int? = nil, - hour: Int? = nil, - minute: Int? = nil, - second: Int? = nil - ) -> Date? { - return next( - .init(year: year, month: month, day: day, hour: hour, minute: minute, second: second) - ) - } } func applyComponents( diff --git a/Sources/SwiftMETAR/Support/Ratio.swift b/Sources/SwiftMETAR/Support/Ratio.swift index 707c879..e6b696a 100644 --- a/Sources/SwiftMETAR/Support/Ratio.swift +++ b/Sources/SwiftMETAR/Support/Ratio.swift @@ -4,12 +4,6 @@ import NumberKit /// A ratio of two integers. public typealias Ratio = Rational -extension Float { - init(_ ratio: Ratio) { - self = ratio.floatValue - } -} - extension Ratio { init(_ whole: Int, numerator: Int, denominator: Int) { self.init(denominator * whole + numerator, denominator) diff --git a/Sources/SwiftMETAR/Support/String+SwiftMETAR.swift b/Sources/SwiftMETAR/Support/String+SwiftMETAR.swift index 42cb217..5a91a54 100644 --- a/Sources/SwiftMETAR/Support/String+SwiftMETAR.swift +++ b/Sources/SwiftMETAR/Support/String+SwiftMETAR.swift @@ -1,8 +1,6 @@ import Foundation extension String { - var nsRange: NSRange { return NSRange(location: 0, length: count) } - func partitionSlices(by length: Int) -> [Substring] { var startIndex = self.startIndex var results = [Substring]() @@ -46,13 +44,4 @@ extension String { substrings.append(self[rangeStart...]) return substrings } - - func substring(with nsRange: NSRange) -> SubSequence { - let range = - index( - startIndex, - offsetBy: nsRange.location - ).. Ratio? { - if let intVal = Int(string) { - return Ratio(intVal) - } - - // Handle decimal like "1.75" - if string.contains(".") { - let parts = string.split(separator: ".") - guard parts.count == 2, - let whole = Int(parts[0]), - let fracStr = parts.last - else { return nil } - - let denominator = Int(pow(10.0, Double(fracStr.count))) - guard let numerator = Int(fracStr) else { return nil } - return Ratio(whole * denominator + numerator, denominator) - } - - // Handle fraction like "3/4" - if string.contains("/") { - let parts = string.split(separator: "/") - guard parts.count == 2, - let num = Int(parts[0]), - let den = Int(parts[1]) - else { return nil } - return Ratio(num, den) - } - - return nil - } - static func buildWind(dirDegrees: String?, speedKt: String?, gustKt: String?) throws -> Wind? { guard let speedStr = speedKt else { return nil } guard let speed = UInt16(speedStr) else { diff --git a/Sources/SwiftMETAR/TAF/Parsers/TAFXMLParser.swift b/Sources/SwiftMETAR/TAF/Parsers/TAFXMLParser.swift index e6939aa..a98167c 100644 --- a/Sources/SwiftMETAR/TAF/Parsers/TAFXMLParser.swift +++ b/Sources/SwiftMETAR/TAF/Parsers/TAFXMLParser.swift @@ -281,7 +281,6 @@ actor TAFXMLParser { private var currentEntry: Entry? private var currentForecast: ForecastEntry? - private var currentElement: String? private var characterBuffer = "" private var inForecast = false @@ -292,8 +291,6 @@ actor TAFXMLParser { qualifiedName _: String?, attributes attributeDict: [String: String] = [:] ) { - currentElement = elementName - if elementName == "TAF" { currentEntry = Entry() } else if elementName == "forecast" { @@ -393,8 +390,6 @@ actor TAFXMLParser { } } } - - currentElement = nil } func parser(_: XMLParser, foundCharacters string: String) {