-
Notifications
You must be signed in to change notification settings - Fork 172
Expand file tree
/
Copy pathModels+FirstWithVersion.swift
More file actions
74 lines (66 loc) · 3.76 KB
/
Models+FirstWithVersion.swift
File metadata and controls
74 lines (66 loc) · 3.76 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
import Foundation
import Version
/// Returns the first XcodeType that unambiguously has the same version as `version`.
///
/// If there's an equivalent match that takes prerelease identifiers into account, that's returned.
/// Otherwise, if a version without prerelease or build metadata identifiers is provided, and there's a single match based on only the major, minor and patch numbers, that's returned.
/// If there are multiple matches, or no matches, nil is returned.
public func findXcode<XcodeType>(version: Version, in xcodes: [XcodeType], versionKeyPath: KeyPath<XcodeType, Version>) -> XcodeType? {
// Look for the equivalent provided version first
if let equivalentXcode = xcodes.first(where: { $0[keyPath: versionKeyPath].isEquivalent(to: version) }) {
return equivalentXcode
}
// If a version without prerelease or build identifiers is provided, then ignore all identifiers this time.
// There must be exactly one match.
else if version.prereleaseIdentifiers.isEmpty && version.buildMetadataIdentifiers.isEmpty,
xcodes.filter({ $0[keyPath: versionKeyPath].isEqualWithoutAllIdentifiers(to: version) }).count == 1 {
let matchedXcode = xcodes.first(where: { $0[keyPath: versionKeyPath].isEqualWithoutAllIdentifiers(to: version) })!
return matchedXcode
} else {
return nil
}
}
public extension Array where Element == Xcode {
/// Returns the first Xcode that unambiguously has the same version as `version`.
///
/// If there's an exact match that takes prerelease identifiers into account, that's returned.
/// Otherwise, if a version without prerelease or build metadata identifiers is provided, and there's a single match based on only the major, minor and patch numbers, that's returned.
/// If there are multiple matches, or no matches, nil is returned.
func first(withVersion version: Version) -> Xcode? {
findXcode(version: version, in: self, versionKeyPath: \.version)
}
}
public extension Array where Element == InstalledXcode {
/// Returns the first InstalledXcode that unambiguously has the same version as `version`.
///
/// If there's an exact match that takes prerelease identifiers into account, that's returned.
/// Otherwise, if a version without prerelease or build metadata identifiers is provided, and there's a single match based on only the major, minor and patch numbers, that's returned.
/// If there are multiple matches, or no matches, nil is returned.
func first(withVersion version: Version) -> InstalledXcode? {
findXcode(version: version, in: self, versionKeyPath: \.version)
}
/// Returns the newest InstalledXcode that shares the requested major and minor versions when:
/// - No prerelease or build metadata identifiers were specified.
/// - The requested patch version is 0 (i.e. not explicitly provided).
/// - An exact match via `first(withVersion:)` could not be found.
/// Prefers release builds when available, otherwise falls back to prerelease builds.
func latestMatchingMajorMinorVersion(withVersion version: Version) -> InstalledXcode? {
let matchingVersions = filter {
$0.version.major == version.major &&
$0.version.minor == version.minor
}
if matchingVersions.isEmpty {
return nil
}
let releaseVersions = matchingVersions.filter { $0.version.prereleaseIdentifiers.isEmpty }
let candidates = releaseVersions.isEmpty ? matchingVersions : releaseVersions
return candidates.max(by: { $0.version < $1.version })
}
}
extension Version {
func isEqualWithoutAllIdentifiers(to other: Version) -> Bool {
return major == other.major &&
minor == other.minor &&
patch == other.patch
}
}