Simplify Version comparison

This commit is contained in:
Brandon Evans 2021-01-12 20:44:05 -07:00
parent faad4c2ab7
commit ed023218b8
No known key found for this signature in database
GPG key ID: D58A4B8DB64F8E93
4 changed files with 24 additions and 44 deletions

View file

@ -44,7 +44,7 @@ extension AppState {
}
.handleEvents(receiveOutput: { installedXcode in
DispatchQueue.main.async {
guard let index = self.allXcodes.firstIndex(where: { $0.version == installedXcode.version || $0.version.isEquivalentForDeterminingIfInstalled(toInstalled: installedXcode.version) }) else { return }
guard let index = self.allXcodes.firstIndex(where: { $0.version.isEquivalent(to: installedXcode.version) }) else { return }
self.allXcodes[index].installState = .installed
}
})
@ -54,7 +54,7 @@ extension AppState {
private func getXcodeArchive(_ installationType: InstallationType, downloader: Downloader) -> AnyPublisher<(AvailableXcode, URL), Error> {
switch installationType {
case .version(let availableXcode):
if let installedXcode = Current.files.installedXcodes(Path.root/"Applications").first(where: { $0.version.isEqualWithoutBuildMetadataIdentifiers(to: availableXcode.version) }) {
if let installedXcode = Current.files.installedXcodes(Path.root/"Applications").first(where: { $0.version.isEquivalent(to: availableXcode.version) }) {
return Fail(error: InstallationError.versionAlreadyInstalled(installedXcode))
.eraseToAnyPublisher()
}
@ -344,7 +344,7 @@ extension AppState {
func setInstallationStep(of version: Version, to step: InstallationStep) {
DispatchQueue.main.async {
guard let index = self.allXcodes.firstIndex(where: { $0.version.buildMetadataIdentifiers == version.buildMetadataIdentifiers || $0.version.isEquivalentForDeterminingIfInstalled(toInstalled: version) }) else { return }
guard let index = self.allXcodes.firstIndex(where: { $0.version.isEquivalent(to: version) }) else { return }
self.allXcodes[index].installState = .installing(step)
}
}

View file

@ -70,7 +70,7 @@ extension AppState {
// /download/more doesn't include build numbers, so we trust that if the version number and prerelease identifiers are the same that they're the same build.
// If an Xcode version is listed on both sites then prefer the one on /download because the build metadata is used to compare against installed Xcodes.
let xcodes = releasedXcodes.filter { releasedXcode in
prereleaseXcodes.contains { $0.version.isEqualWithoutBuildMetadataIdentifiers(to: releasedXcode.version) } == false
prereleaseXcodes.contains { $0.version.isEquivalent(to: releasedXcode.version) } == false
} + prereleaseXcodes
return xcodes
}

View file

@ -369,14 +369,14 @@ class AppState: ObservableObject {
// Xcode Releases should have all versions
// Apple didn't used to keep all prerelease versions around but has started to recently
else if !allAvailableXcodeVersions.contains(where: { version in
version.isEquivalentForDeterminingIfInstalled(toInstalled: installedXcode.version)
version.isEquivalent(to: installedXcode.version)
}) {
allAvailableXcodeVersions.append(installedXcode.version)
}
// If an installed version is the same as one that's listed online which doesn't have build metadata, replace it with the installed version
// This was originally added for Apple versions
else if let index = allAvailableXcodeVersions.firstIndex(where: { version in
version.isEquivalentForDeterminingIfInstalled(toInstalled: installedXcode.version) &&
version.isEquivalent(to: installedXcode.version) &&
version.buildMetadataIdentifiers.isEmpty
}) {
allAvailableXcodeVersions[index] = installedXcode.version
@ -388,10 +388,7 @@ class AppState: ObservableObject {
.sorted(by: { $0.0 > $1.0 })
.map { availableXcodeVersion, availableXcode in
let installedXcode = installedXcodes.first(where: { installedXcode in
// Checking equality for Xcode Releases version
availableXcodeVersion == installedXcode.version ||
// Check more carefully for Apple version
availableXcodeVersion.isEquivalentForDeterminingIfInstalled(toInstalled: installedXcode.version)
availableXcodeVersion.isEquivalent(to: installedXcode.version)
})
// If the existing install state is "installing", keep it

View file

@ -1,41 +1,24 @@
import Version
public extension Version {
func isEqualWithoutBuildMetadataIdentifiers(to other: Version) -> Bool {
return major == other.major &&
minor == other.minor &&
patch == other.patch &&
prereleaseIdentifiers == other.prereleaseIdentifiers
}
/// If release versions, don't compare build metadata because that's not provided in the /downloads/more list
/// if beta versions, compare build metadata because it's available in versions.plist
func isEquivalentForDeterminingIfInstalled(toInstalled installed: Version) -> Bool {
let isBeta = !prereleaseIdentifiers.isEmpty
let otherIsBeta = !installed.prereleaseIdentifiers.isEmpty
if isBeta && otherIsBeta {
if buildMetadataIdentifiers.isEmpty {
return major == installed.major &&
minor == installed.minor &&
patch == installed.patch &&
prereleaseIdentifiers.map { $0.lowercased() } == installed.prereleaseIdentifiers.map { $0.lowercased() }
}
else {
return major == installed.major &&
minor == installed.minor &&
patch == installed.patch &&
prereleaseIdentifiers.map { $0.lowercased() } == installed.prereleaseIdentifiers.map { $0.lowercased() } &&
buildMetadataIdentifiers.map { $0.lowercased() } == installed.buildMetadataIdentifiers.map { $0.lowercased() }
}
/// Determines if two Xcode versions should be treated equivalently. This is not the same as equality.
///
/// We need a way to determine if two Xcode versions are the same without always having full information, and supporting different data sources.
/// For example, the Apple data source often doesn't have build metadata identifiers.
func isEquivalent(to other: Version) -> Bool {
// If we don't have build metadata identifiers for both Versions, compare major, minor, patch and prerelease identifiers.
if buildMetadataIdentifiers.isEmpty || other.buildMetadataIdentifiers.isEmpty {
return major == other.major &&
minor == other.minor &&
patch == other.patch &&
prereleaseIdentifiers.map { $0.lowercased() } == other.prereleaseIdentifiers.map { $0.lowercased() }
// If we have build metadata identifiers for both, we can ignore the prerelease identifiers.
} else {
return major == other.major &&
minor == other.minor &&
patch == other.patch &&
buildMetadataIdentifiers.map { $0.lowercased() } == other.buildMetadataIdentifiers.map { $0.lowercased() }
}
else if !isBeta && !otherIsBeta {
return major == installed.major &&
minor == installed.minor &&
patch == installed.patch
}
return false
}
var descriptionWithoutBuildMetadata: String {