mirror of
https://github.com/exituser/Pearcleaner.git
synced 2026-09-17 04:58:56 +00:00
132 lines
4.0 KiB
Swift
132 lines
4.0 KiB
Swift
//
|
|
// ReversePathsFetch.swift
|
|
// Pearcleaner
|
|
//
|
|
// Created by Alin Lupascu on 3/20/24.
|
|
//
|
|
|
|
import Foundation
|
|
import AppKit
|
|
import AlinFoundation
|
|
|
|
class ReversePathsSearcher {
|
|
private let appState: AppState
|
|
private let locations: Locations
|
|
private let fsm: FolderSettingsManager
|
|
private let fileManager = FileManager.default
|
|
private var collection: [URL] = []
|
|
private var fileSize: [URL: Int64] = [:]
|
|
private var fileSizeLogical: [URL: Int64] = [:]
|
|
private var fileIcon: [URL: NSImage?] = [:]
|
|
private let dispatchGroup = DispatchGroup()
|
|
private let sortedApps: [AppInfo]
|
|
|
|
init(appState: AppState, locations: Locations, fsm: FolderSettingsManager, sortedApps: [AppInfo]) {
|
|
self.appState = appState
|
|
self.locations = locations
|
|
self.fsm = fsm
|
|
self.sortedApps = sortedApps
|
|
}
|
|
|
|
func reversePathsSearch(completion: @escaping () -> Void = {}) {
|
|
Task(priority: .high) {
|
|
self.processLocations()
|
|
self.calculateFileDetails()
|
|
self.updateAppState()
|
|
completion()
|
|
}
|
|
}
|
|
|
|
private func processLocations() {
|
|
|
|
for location in locations.reverse.paths where fileManager.fileExists(atPath: location) {
|
|
|
|
dispatchGroup.enter()
|
|
processLocation(location)
|
|
dispatchGroup.leave()
|
|
|
|
}
|
|
}
|
|
|
|
private func processLocation(_ location: String) {
|
|
|
|
do {
|
|
let contents = try fileManager.contentsOfDirectory(atPath: location)
|
|
contents.forEach { itemName in
|
|
let itemURL = URL(fileURLWithPath: location).appendingPathComponent(itemName)
|
|
processItem(itemName, itemURL: itemURL)
|
|
}
|
|
} catch {
|
|
printOS("Error processing location: \(location), error: \(error)")
|
|
}
|
|
}
|
|
|
|
private func processItem(_ itemName: String, itemURL: URL) {
|
|
let itemPath = itemURL.path.pearFormat()
|
|
let exclusionList = fsm.fileFolderPathsZ.map { $0.pearFormat() }
|
|
|
|
if exclusionList.contains(itemPath) || itemPath.contains("dsstore") || itemPath.contains("daemonnameoridentifierhere") {
|
|
return
|
|
}
|
|
|
|
guard !skipReverse.contains(where: { itemName.pearFormat().contains($0) }),
|
|
isSupportedFileType(at: itemURL.path),
|
|
!isRelatedToInstalledApp(itemPath: itemPath),
|
|
!isExcludedByConditions(itemPath: itemPath) else {
|
|
|
|
return
|
|
}
|
|
|
|
collection.append(itemURL)
|
|
}
|
|
|
|
private func isRelatedToInstalledApp(itemPath: String) -> Bool {
|
|
for app in sortedApps {
|
|
if itemPath.contains(app.bundleIdentifier.pearFormat()) || itemPath.contains(app.appName.pearFormat()) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
private func isExcludedByConditions(itemPath: String) -> Bool {
|
|
|
|
for condition in conditions {
|
|
// Include keywords
|
|
if condition.include.contains(where: { itemPath.contains($0.pearFormat()) }) {
|
|
return true
|
|
}
|
|
// Include force
|
|
if let includeForce = condition.includeForce,
|
|
includeForce.contains(where: { itemPath.contains($0.path.pearFormat()) }) {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
private func calculateFileDetails() {
|
|
collection.forEach { path in
|
|
let size = totalSizeOnDisk(for: path)
|
|
fileSize[path] = size.real
|
|
fileSizeLogical[path] = size.logical
|
|
fileIcon[path] = getIconForFileOrFolderNS(atPath: path)
|
|
}
|
|
}
|
|
|
|
private func updateAppState() {
|
|
dispatchGroup.notify(queue: .main) {
|
|
var updatedZombieFile = ZombieFile.empty
|
|
updatedZombieFile.fileSize = self.fileSize
|
|
updatedZombieFile.fileSizeLogical = self.fileSizeLogical
|
|
updatedZombieFile.fileIcon = self.fileIcon
|
|
self.appState.zombieFile = updatedZombieFile
|
|
self.appState.showProgress = false
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|