Skip to content

Commit

Permalink
+ First steps in rewriting of package loading func
Browse files Browse the repository at this point in the history
  • Loading branch information
buresdv committed Nov 10, 2024
1 parent ad904d7 commit 9f02136
Show file tree
Hide file tree
Showing 12 changed files with 186 additions and 42 deletions.
8 changes: 8 additions & 0 deletions Cork/ContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,11 @@ struct ContentView: View, Sendable
}
}
.task(priority: .high)
{
await brewData.loadInstalledPackages(packageTypeToLoad: .formula, appState: appState)
await brewData.loadInstalledPackages(packageTypeToLoad: .cask, appState: appState)
}
.task(priority: .high)
{
AppConstants.shared.logger.info("Started Package Load startup action at \(Date())")

Expand Down Expand Up @@ -418,6 +423,9 @@ struct ContentView: View, Sendable
{ error in
switch error
{
case .couldNotGetContentsOfPackageFolder(let failureReason):
EmptyView()

case .uninstallationNotPossibleDueToDependency:
EmptyView()

Expand Down
2 changes: 1 addition & 1 deletion Cork/Enums/Alerts/Main Window/Displayable Alerts.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import SwiftUI

enum DisplayableAlert: LocalizedError
{
case couldNotLoadAnyPackages(LocalizedError), couldNotLoadCertainPackage(String, URL, failureReason: String)
case couldNotGetContentsOfPackageFolder(String), couldNotLoadAnyPackages(LocalizedError), couldNotLoadCertainPackage(String, URL, failureReason: String)
case licenseCheckingFailedDueToAuthorizationComplexNotBeingEncodedProperly, licenseCheckingFailedDueToNoInternet, licenseCheckingFailedDueToTimeout, licenseCheckingFailedForOtherReason(localizedDescription: String)
case customBrewExcutableGotDeleted
case couldNotFindPackageUUIDInList
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ extension DisplayableAlert
{
switch self
{
case .couldNotGetContentsOfPackageFolder:
return String(localized: "alert.could-not-get-contents-of-package-folder.title")
case .couldNotLoadAnyPackages(let error):
return String(localized: "alert.fatal.could-not-load-any-packages-\(error.localizedDescription).title")
case .couldNotLoadCertainPackage(let offendingPackage, _, _):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ extension DisplayableAlert
{
switch self
{
case .couldNotGetContentsOfPackageFolder(let localizedError):
return String(localized: "alert.could-not-get-contents-of-package-folder.message-\(localizedError)")
case .couldNotLoadAnyPackages:
return String(localized: "alert.restart-or-reinstall")
case .couldNotLoadCertainPackage(_, _, let failureReason):
Expand Down
13 changes: 13 additions & 0 deletions Cork/Enums/Packages/Package Types.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@
import AppIntents
import Charts
import Foundation
import CorkShared

enum PackageType: String, CustomStringConvertible, Plottable, AppEntity, Codable
{
case formula
case cask

/// User-readable description of the package type
var description: String
{
switch self
Expand All @@ -24,6 +26,17 @@ enum PackageType: String, CustomStringConvertible, Plottable, AppEntity, Codable
return String(localized: "package-details.type.cask")
}
}

/// Parent folder for this package type
var parentFolder: URL
{
switch self {
case .formula:
return AppConstants.shared.brewCellarPath
case .cask:
return AppConstants.shared.brewCaskPath
}
}

static let typeDisplayRepresentation: TypeDisplayRepresentation = .init(name: "package-details.type")

Expand Down
42 changes: 42 additions & 0 deletions Cork/Errors/Package Loading Error.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
//
// Package Loading Error.swift
// Cork
//
// Created by David Bureš on 10.11.2024.
//

import Foundation

enum PackageLoadingError: LocalizedError
{
/// When attempting to get the list of raw URLs from the folder containing the packages, the function for loading packages returned nil, therefore, an error occured
case couldNotReadContentsOfParentFolder(failureReason: String)
case failedWhileLoadingPackages(failureReason: String?)
case failedWhileLoadingCertainPackage(String, URL, failureReason: String)
case packageDoesNotHaveAnyVersionsInstalled(String)
case packageIsNotAFolder(String, URL)

var errorDescription: String?
{
switch self
{
case .couldNotReadContentsOfParentFolder(let failureReason):
return String(localized: "error.package-loading.could-not-read-contents-of-parent-folder.\(failureReason)")
case .failedWhileLoadingPackages(let failureReason):
if let failureReason
{
return String(localized: "error.package-loading.could-not-load-packages.\(failureReason)")
}
else
{
return String(localized: "error.package-loading.could-not-load-packages")
}
case .failedWhileLoadingCertainPackage(let string, let uRL, let failureReason):
return String(localized: "error.package-loading.could-not-load-\(string)-at-\(uRL.absoluteString)-because-\(failureReason)", comment: "Couldn't load package (package name) at (package URL) because (failure reason)")
case .packageDoesNotHaveAnyVersionsInstalled(let string):
return String(localized: "error.package-loading.\(string)-does-not-have-any-versions-installed")
case .packageIsNotAFolder(let string, _):
return String(localized: "error.package-loading.\(string)-not-a-folder", comment: "Package folder in this context means a folder that encloses package versions. Every package has its own folder, and this error occurs when the provided URL does not point to a folder that encloses package versions")
}
}
}
9 changes: 9 additions & 0 deletions Cork/Localizable.xcstrings
Original file line number Diff line number Diff line change
Expand Up @@ -11644,6 +11644,12 @@
}
}
}
},
"alert.could-not-get-contents-of-package-folder.message-%@" : {

},
"alert.could-not-get-contents-of-package-folder.title" : {

},
"alert.could-not-get-new-packages-in-scheduled-update-system.message" : {
"extractionState" : "manual",
Expand Down Expand Up @@ -16496,6 +16502,9 @@
}
}
}
},
"error.package-loading.could-not-read-contents-of-parent-folder.%@" : {

},
"error.package-loading.last-path-component-of-checked-package-url-is-folder-containing-packages-itself.casks" : {
"localizations" : {
Expand Down
41 changes: 6 additions & 35 deletions Cork/Logic/File System/File Browser/Get Contents of Folder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,33 +9,6 @@ import Foundation
import SwiftUI
import CorkShared

enum PackageLoadingError: LocalizedError
{
case failedWhileLoadingPackages(failureReason: String?), failedWhileLoadingCertainPackage(String, URL, failureReason: String), packageDoesNotHaveAnyVersionsInstalled(String), packageIsNotAFolder(String, URL)

var errorDescription: String?
{
switch self
{
case .failedWhileLoadingPackages(let failureReason):
if let failureReason
{
return String(localized: "error.package-loading.could-not-load-packages.\(failureReason)")
}
else
{
return String(localized: "error.package-loading.could-not-load-packages")
}
case .failedWhileLoadingCertainPackage(let string, let uRL, let failureReason):
return String(localized: "error.package-loading.could-not-load-\(string)-at-\(uRL.absoluteString)-because-\(failureReason)", comment: "Couldn't load package (package name) at (package URL) because (failure reason)")
case .packageDoesNotHaveAnyVersionsInstalled(let string):
return String(localized: "error.package-loading.\(string)-does-not-have-any-versions-installed")
case .packageIsNotAFolder(let string, _):
return String(localized: "error.package-loading.\(string)-not-a-folder", comment: "Package folder in this context means a folder that encloses package versions. Every package has its own folder, and this error occurs when the provided URL does not point to a folder that encloses package versions")
}
}
}

func getContentsOfFolder(targetFolder: URL) async throws -> Set<BrewPackage>
{
do
Expand Down Expand Up @@ -288,25 +261,23 @@ extension [URL]

// MARK: - Getting list of URLs in folder

func getContentsOfFolder(targetFolder: URL, options: FileManager.DirectoryEnumerationOptions? = nil) -> [URL]
func getContentsOfFolder(targetFolder: URL, options: FileManager.DirectoryEnumerationOptions? = nil) throws -> [URL]
{
var contentsOfFolder: [URL] = .init()

do
{
if let options
{
contentsOfFolder = try FileManager.default.contentsOfDirectory(at: targetFolder, includingPropertiesForKeys: nil, options: options)
return try FileManager.default.contentsOfDirectory(at: targetFolder, includingPropertiesForKeys: nil, options: options)
}
else
{
contentsOfFolder = try FileManager.default.contentsOfDirectory(at: targetFolder, includingPropertiesForKeys: nil)
return try FileManager.default.contentsOfDirectory(at: targetFolder, includingPropertiesForKeys: nil)
}
}
catch let folderReadingError as NSError
catch let folderReadingError
{
AppConstants.shared.logger.error("\(folderReadingError.localizedDescription)")

throw folderReadingError
}

return contentsOfFolder
}
4 changes: 2 additions & 2 deletions Cork/Logic/Load up Tapped Taps.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,15 @@ func loadUpTappedTaps() async -> [BrewTap]
{
var finalAvailableTaps: [BrewTap] = .init()

let contentsOfTapFolder: [URL] = getContentsOfFolder(targetFolder: AppConstants.shared.tapPath, options: .skipsHiddenFiles)
let contentsOfTapFolder: [URL] = try! getContentsOfFolder(targetFolder: AppConstants.shared.tapPath, options: .skipsHiddenFiles)

AppConstants.shared.logger.debug("Contents of tap folder: \(contentsOfTapFolder)")

for tapRepoParentURL in contentsOfTapFolder
{
AppConstants.shared.logger.debug("Tap repo: \(tapRepoParentURL)")

let contentsOfTapRepoParent: [URL] = getContentsOfFolder(targetFolder: tapRepoParentURL, options: .skipsHiddenFiles)
let contentsOfTapRepoParent: [URL] = try! getContentsOfFolder(targetFolder: tapRepoParentURL, options: .skipsHiddenFiles)

for repoURL in contentsOfTapRepoParent
{
Expand Down
6 changes: 3 additions & 3 deletions Cork/Logic/Maintenance/Steps/Delete Cached Downloads.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import CorkShared
func deleteCachedDownloads()
{
/// This folder has the symlinks, so we have do **delete ONLY THE SYMLINKS**
for url in getContentsOfFolder(targetFolder: AppConstants.shared.brewCachedFormulaeDownloadsPath)
for url in try! getContentsOfFolder(targetFolder: AppConstants.shared.brewCachedFormulaeDownloadsPath)
{
if let isSymlink = url.isSymlink()
{
Expand All @@ -31,7 +31,7 @@ func deleteCachedDownloads()
}

/// This folder has the symlinks, so we have to **delete ONLY THE SYMLINKS**
for url in getContentsOfFolder(targetFolder: AppConstants.shared.brewCachedCasksDownloadsPath)
for url in try! getContentsOfFolder(targetFolder: AppConstants.shared.brewCachedCasksDownloadsPath)
{
if let isSymlink = url.isSymlink()
{
Expand All @@ -51,7 +51,7 @@ func deleteCachedDownloads()
}

/// This folder has the downloads themselves, so we have do **DELETE EVERYTHING THAT IS NOT A SYMLINK**
for url in getContentsOfFolder(targetFolder: AppConstants.shared.brewCachedDownloadsPath)
for url in try! getContentsOfFolder(targetFolder: AppConstants.shared.brewCachedDownloadsPath)
{
if let isSymlink = url.isSymlink()
{
Expand Down
95 changes: 95 additions & 0 deletions Cork/Logic/Package Loading/Load Up Installed Packages.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
//
// Load Up Installed Packages.swift
// Cork
//
// Created by David Bureš on 10.11.2024.
//

import CorkShared
import Foundation

extension BrewDataStorage
{
/// Parent function for loading installed packages from disk
/// Abstracts away the function ``loadInstalledPackagesFromFolder(packageTypeToLoad:)``, transforming errors thrown by ``loadInstalledPackagesFromFolder(packageTypeToLoad:)`` into displayable errors
/// - Parameters:
/// - packageTypeToLoad: Which ``PackageType`` to load
/// - appState: ``AppState`` used to display loading errors
/// - Returns: A set of loaded ``BrewPackage``s for the specified ``PackageType``
func loadInstalledPackages(
packageTypeToLoad: PackageType, appState: AppState
) async -> Set<BrewPackage>?
{
/// Start tracking when loading started
let timeLoadingStarted: Date = .now
AppConstants.shared.logger.debug(
"Started \(packageTypeToLoad.rawValue, privacy: .public) loading task at \(timeLoadingStarted, privacy: .public)"
)

/// Calculate how long loading took
defer
{
AppConstants.shared.logger.debug("Finished \(packageTypeToLoad.rawValue, privacy: .public). Took \(timeLoadingStarted.timeIntervalSince(.now), privacy: .public)")
}

do
{
return try await self.loadInstalledPackagesFromFolder(
packageTypeToLoad: packageTypeToLoad)
}
catch let packageLoadingError
{
switch packageLoadingError
{
case .couldNotReadContentsOfParentFolder(let loadingError):
appState.showAlert(errorToShow: .couldNotGetContentsOfPackageFolder(loadingError))
case .failedWhileLoadingPackages:
appState.showAlert(
errorToShow: .couldNotLoadAnyPackages(packageLoadingError))
case .failedWhileLoadingCertainPackage(
let offendingPackage, let offendingPackageURL, let failureReason
):
appState.showAlert(
errorToShow: .couldNotLoadCertainPackage(
offendingPackage, offendingPackageURL,
failureReason: failureReason
))
case .packageDoesNotHaveAnyVersionsInstalled(let offendingPackage):
appState.showAlert(
errorToShow: .installedPackageHasNoVersions(
corruptedPackageName: offendingPackage))
case .packageIsNotAFolder(let offendingFile, let offendingFileURL):
appState.showAlert(
errorToShow: .installedPackageIsNotAFolder(
itemName: offendingFile, itemURL: offendingFileURL
))
}

return nil
}
}
}

private extension BrewDataStorage
{
/// Load packages from disk, and convert them into ``BrewPackage``s
func loadInstalledPackagesFromFolder(
packageTypeToLoad: PackageType
) async throws(PackageLoadingError) -> Set<BrewPackage>?
{
do
{
let urlsInParentFolder: [URL] = try getContentsOfFolder(targetFolder: packageTypeToLoad.parentFolder, options: [.skipsHiddenFiles])

AppConstants.shared.logger.debug("Loaded contents of folder: \(urlsInParentFolder)")

return nil
}
catch let parentFolderReadingError
{
AppConstants.shared.logger.error("Couldn't get contents of folder \(packageTypeToLoad.parentFolder, privacy: .public)")

throw .couldNotReadContentsOfParentFolder(failureReason: parentFolderReadingError.localizedDescription)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
// Created by David Bureš on 11.02.2023.
//

import Foundation
import CorkShared
import Foundation

@MainActor
func loadUpPackages(whatToLoad: PackageType, appState: AppState) async -> Set<BrewPackage>
Expand All @@ -29,6 +29,8 @@ func loadUpPackages(whatToLoad: PackageType, appState: AppState) async -> Set<Br
{
switch packageLoadingError
{
case .couldNotReadContentsOfParentFolder(let failureReason):
appState.showAlert(errorToShow: .couldNotGetContentsOfPackageFolder(failureReason))
case .failedWhileLoadingPackages:
appState.showAlert(errorToShow: .couldNotLoadAnyPackages(packageLoadingError))
case .failedWhileLoadingCertainPackage(let offendingPackage, let offendingPackageURL, let failureReason):
Expand Down

0 comments on commit 9f02136

Please sign in to comment.