-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
21 changed files
with
213 additions
and
141 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,7 @@ | ||
import Foundation | ||
|
||
extension Collection { | ||
subscript(safe index: Index) -> Element? { | ||
return indices.contains(index) ? self[index] : nil | ||
} | ||
subscript(safe index: Index) -> Element? { | ||
indices.contains(index) ? self[index] : nil | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
import Foundation | ||
|
||
public struct ListString< | ||
Value: LosslessStringConvertible & Equatable | ||
>: Codable, Equatable { | ||
public let values: [Value] | ||
|
||
internal init(values: [Value]) { | ||
self.values = values | ||
} | ||
|
||
public init(from decoder: Decoder) throws { | ||
let container = try decoder.singleValueContainer() | ||
let listString = try container.decode(String.self) | ||
let strings = listString.components(separatedBy: ",") | ||
let values = try strings | ||
.map { $0.trimmingCharacters(in: .whitespacesAndNewlines) } | ||
.filter { !$0.isEmpty } | ||
.map(Self.createValueFrom) | ||
self.init(values: values) | ||
} | ||
|
||
private static func createValueFrom(_ string: String) throws -> Value { | ||
guard let value: Value = .init(string) else { | ||
throw DecodingError.typeMismatch( | ||
Value.self, | ||
.init(codingPath: [], debugDescription: "Invalid value: \(string)") | ||
) | ||
} | ||
return value | ||
} | ||
|
||
public func encode(to encoder: Encoder) throws { | ||
var container = encoder.singleValueContainer() | ||
let strings = values.map(String.init) | ||
let listString = strings.joined(separator: ",") | ||
try container.encode(listString) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
104 changes: 74 additions & 30 deletions
104
Sources/SyndiKit/Formats/Media/Podcast/PodcastLocation+GeoURI.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,62 +1,106 @@ | ||
import Foundation | ||
|
||
extension PodcastLocation { | ||
public struct GeoURI: Codable, Equatable { | ||
let latitude: Double | ||
let longitude: Double | ||
let altitude: Double? | ||
let accuracy: Double? | ||
|
||
public init(latitude: Double, longitude: Double, altitude: Double? = nil, accuracy: Double? = nil) { | ||
public extension PodcastLocation { | ||
struct GeoURI: Codable, Equatable, LosslessStringConvertible { | ||
public let latitude: Double | ||
public let longitude: Double | ||
public let altitude: Double? | ||
public let accuracy: Double? | ||
|
||
public var description: String { | ||
var description = "geo:\(latitude),\(longitude)" | ||
|
||
if let altitude = altitude { | ||
description += ",\(altitude)" | ||
} | ||
|
||
if let accuracy = accuracy { | ||
description += ";u=\(accuracy)" | ||
} | ||
|
||
return description | ||
} | ||
|
||
public init( | ||
latitude: Double, | ||
longitude: Double, | ||
altitude: Double? = nil, | ||
accuracy: Double? = nil | ||
) { | ||
self.latitude = latitude | ||
self.longitude = longitude | ||
self.altitude = altitude | ||
self.accuracy = accuracy | ||
} | ||
|
||
public init(from decoder: Decoder) throws { | ||
let container = try decoder.singleValueContainer() | ||
let geoStr = try container.decode(String.self) | ||
public init?(_ description: String) { | ||
try? self.init(singleValue: description) | ||
} | ||
|
||
public init(singleValue: String) throws { | ||
let pathComponents = try Self.pathComponents(from: singleValue) | ||
|
||
guard | ||
let geoScheme = geoStr.split(separator: ":")[safe: 0], | ||
geoScheme == "geo" else { | ||
let geoCoords = pathComponents[safe: 0]?.split(separator: ","), | ||
let latitude = geoCoords[safe: 0]?.asDouble(), | ||
let longitude = geoCoords[safe: 1]?.asDouble() | ||
else { | ||
throw DecodingError.dataCorrupted( | ||
.init( | ||
codingPath: [PodcastLocation.CodingKeys.geo], | ||
debugDescription: "Invalid prefix for geo attribute: \(geoStr)" | ||
debugDescription: "Invalid coordinates for geo attribute: \(singleValue)" | ||
) | ||
) | ||
} | ||
guard let geoPath = geoStr.split(separator: ":")[safe: 1] else { | ||
|
||
let altitude = geoCoords[safe: 2]?.asDouble() | ||
|
||
let accuracy = pathComponents[safe: 1]? | ||
.split(separator: "=")[safe: 1]? | ||
.asDouble() | ||
|
||
self.init( | ||
latitude: latitude, | ||
longitude: longitude, | ||
altitude: altitude, | ||
accuracy: accuracy | ||
) | ||
} | ||
|
||
public init(from decoder: Decoder) throws { | ||
let container = try decoder.singleValueContainer() | ||
let singleValue = try container.decode(String.self) | ||
|
||
try self.init(singleValue: singleValue) | ||
} | ||
|
||
private static func pathComponents(from string: String) throws -> [Substring] { | ||
let components = string.split(separator: ":") | ||
|
||
guard | ||
components[safe: 0] == "geo" else { | ||
throw DecodingError.dataCorrupted( | ||
.init( | ||
codingPath: [PodcastLocation.CodingKeys.geo], | ||
debugDescription: "Invalid path for geo attribute: \(geoStr)" | ||
debugDescription: "Invalid prefix for geo attribute: \(string)" | ||
) | ||
) | ||
} | ||
guard | ||
let geoCoords = geoPath.split(separator: ";")[safe: 0], | ||
let latitude = geoCoords.split(separator: ",")[safe: 0]?.asDouble(), | ||
let longitude = geoCoords.split(separator: ",")[safe: 1]?.asDouble() | ||
else { | ||
guard let geoPath = components[safe: 1] else { | ||
throw DecodingError.dataCorrupted( | ||
.init( | ||
codingPath: [PodcastLocation.CodingKeys.geo], | ||
debugDescription: "Invalid coordinates for geo attribute: \(geoStr)" | ||
debugDescription: "Invalid path for geo attribute: \(string)" | ||
) | ||
) | ||
} | ||
let altitude = geoCoords.split(separator: ",")[safe: 2]?.asDouble() | ||
let accuracy = geoPath.split(separator: ";")[safe: 1]? | ||
.split(separator: "=")[safe: 1]? | ||
.asDouble() | ||
|
||
self.latitude = latitude | ||
self.longitude = longitude | ||
self.altitude = altitude | ||
self.accuracy = accuracy | ||
return geoPath.split(separator: ";") | ||
} | ||
|
||
public func encode(to encoder: Encoder) throws { | ||
var container = encoder.singleValueContainer() | ||
try container.encode(description) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.