From fa2d2105c5de91e6eff4f629671c60bf478417af Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Tue, 1 Nov 2022 14:11:34 +0100 Subject: [PATCH] =?UTF-8?q?=F0=9F=91=8C=20Removed=20remaining=20`FileManag?= =?UTF-8?q?er.default`=20usage?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Filesystem/FileSystemProtocol.swift | 10 ++++- phpmon/Common/Filesystem/RealFileSystem.swift | 21 +++++++++- phpmon/Common/PHP/PHP Version/PhpHelper.swift | 2 +- .../Common/Testables/TestableFileSystem.swift | 38 ++++++++++++++++++- phpmon/Domain/App/Startup.swift | 4 +- .../Sites/SiteScanner/ValetSiteScanner.swift | 29 ++++---------- .../Integrations/Valet/Sites/ValetSite.swift | 2 +- phpmon/Domain/Integrations/Valet/Valet.swift | 7 +--- phpmon/Domain/Warnings/WarningManager.swift | 2 +- phpmon/Domain/Watcher/PhpConfigWatcher.swift | 4 ++ 10 files changed, 82 insertions(+), 37 deletions(-) diff --git a/phpmon/Common/Filesystem/FileSystemProtocol.swift b/phpmon/Common/Filesystem/FileSystemProtocol.swift index e1e0fb5..f0892c7 100644 --- a/phpmon/Common/Filesystem/FileSystemProtocol.swift +++ b/phpmon/Common/Filesystem/FileSystemProtocol.swift @@ -16,7 +16,11 @@ protocol FileSystemProtocol { func writeAtomicallyToFile(_ path: String, content: String) throws - func readStringFromFile(_ path: String) throws -> String + func getStringFromFile(_ path: String) throws -> String + + func getContentsOfDirectory(_ path: String) throws -> [String] + + func getDestinationOfSymlink(_ path: String) throws -> String // MARK: - Move & Delete Files @@ -40,5 +44,7 @@ protocol FileSystemProtocol { func directoryExists(_ path: String) -> Bool - func fileIsSymlink(_ path: String) -> Bool + func isSymlink(_ path: String) -> Bool + + func isDirectory(_ path: String) -> Bool } diff --git a/phpmon/Common/Filesystem/RealFileSystem.swift b/phpmon/Common/Filesystem/RealFileSystem.swift index 6d6262f..f54c861 100644 --- a/phpmon/Common/Filesystem/RealFileSystem.swift +++ b/phpmon/Common/Filesystem/RealFileSystem.swift @@ -33,13 +33,21 @@ class RealFileSystem: FileSystemProtocol { ) } - func readStringFromFile(_ path: String) throws -> String { + func getStringFromFile(_ path: String) throws -> String { return try String( contentsOf: URL(fileURLWithPath: path.replacingTildeWithHomeDirectory), encoding: .utf8 ) } + func getContentsOfDirectory(_ path: String) throws -> [String] { + // TODO + } + + func getDestinationOfSymlink(_ path: String) throws -> String { + // TODO + } + // MARK: - Move & Delete Files func move(from path: String, to newPath: String) throws { @@ -96,7 +104,7 @@ class RealFileSystem: FileSystemProtocol { return exists && isDirectory.boolValue } - func fileIsSymlink(_ path: String) -> Bool { + func isSymlink(_ path: String) -> Bool { do { let attribs = try FileManager.default.attributesOfItem(atPath: path) return attribs[.type] as! FileAttributeType == FileAttributeType.typeSymbolicLink @@ -104,4 +112,13 @@ class RealFileSystem: FileSystemProtocol { return false } } + + func isDirectory(_ path: String) -> Bool { + do { + let attribs = try FileManager.default.attributesOfItem(atPath: path) + return attribs[.type] as! FileAttributeType == FileAttributeType.typeDirectory + } catch { + return false + } + } } diff --git a/phpmon/Common/PHP/PHP Version/PhpHelper.swift b/phpmon/Common/PHP/PHP Version/PhpHelper.swift index 4e1ce7b..1ed99e1 100644 --- a/phpmon/Common/PHP/PHP Version/PhpHelper.swift +++ b/phpmon/Common/PHP/PHP Version/PhpHelper.swift @@ -93,7 +93,7 @@ class PhpHelper { return } - if !FileSystem.fileIsSymlink(destination) { + if !FileSystem.isSymlink(destination) { Log.info("Overwriting existing file with new symlink: \(destination)") await Shell.quiet("ln -fs \(source) \(destination)") return diff --git a/phpmon/Common/Testables/TestableFileSystem.swift b/phpmon/Common/Testables/TestableFileSystem.swift index b3f425d..4f2b1b9 100644 --- a/phpmon/Common/Testables/TestableFileSystem.swift +++ b/phpmon/Common/Testables/TestableFileSystem.swift @@ -33,7 +33,7 @@ class TestableFileSystem: FileSystemProtocol { self.files[path] = .fake(.text, content) } - func readStringFromFile(_ path: String) throws -> String { + func getStringFromFile(_ path: String) throws -> String { guard let file = files[path] else { throw TestableFileSystemError.fileMissing } @@ -41,6 +41,30 @@ class TestableFileSystem: FileSystemProtocol { return file.content ?? "" } + func getContentsOfDirectory(_ path: String) throws -> [String] { + // TODO + } + + func getDestinationOfSymlink(_ path: String) throws -> String { + guard let file = files[path] else { + throw TestableFileSystemError.fileMissing + } + + if file.type != .symlink { + throw TestableFileSystemError.notSymlink + } + + guard let pathToSymlink = file.content else { + throw TestableFileSystemError.invalidSymlink + } + + if !files.keys.contains(pathToSymlink) { + throw TestableFileSystemError.invalidSymlink + } + + return pathToSymlink + } + // MARK: - Move & Delete Files func move(from path: String, to newPath: String) throws { @@ -99,13 +123,21 @@ class TestableFileSystem: FileSystemProtocol { return [.directory].contains(file.type) } - func fileIsSymlink(_ path: String) -> Bool { + func isSymlink(_ path: String) -> Bool { guard let file = files[path] else { return false } return file.type == .symlink } + + func isDirectory(_ path: String) -> Bool { + guard let file = files[path] else { + return false + } + + return file.type == .directory + } } enum FakeFileType: Codable { @@ -139,4 +171,6 @@ class FakeFile: Codable { enum TestableFileSystemError: Error { case fileMissing case alreadyExists + case notSymlink + case invalidSymlink } diff --git a/phpmon/Domain/App/Startup.swift b/phpmon/Domain/App/Startup.swift index 428ad26..be840db 100644 --- a/phpmon/Domain/App/Startup.swift +++ b/phpmon/Domain/App/Startup.swift @@ -86,7 +86,7 @@ class Startup { // The Homebrew binary must exist. // ================================================================================= EnvironmentCheck( - command: { return !FileManager.default.fileExists(atPath: Paths.brew) }, + command: { return !FileSystem.fileExists(Paths.brew) }, name: "`\(Paths.brew)` exists", titleText: "alert.homebrew_missing.title".localized, subtitleText: "alert.homebrew_missing.subtitle".localized, @@ -205,7 +205,7 @@ class Startup { command: { let nodePath = await Shell.pipe("which node").out return App.architecture == "x86_64" - && FileManager.default.fileExists(atPath: "/usr/local/bin/which") + && FileSystem.fileExists("/usr/local/bin/which") && nodePath.contains("env: node: No such file or directory") }, name: "`env: node` issue does not apply", diff --git a/phpmon/Domain/Integrations/Valet/Sites/SiteScanner/ValetSiteScanner.swift b/phpmon/Domain/Integrations/Valet/Sites/SiteScanner/ValetSiteScanner.swift index 82f27e3..801f89d 100644 --- a/phpmon/Domain/Integrations/Valet/Sites/SiteScanner/ValetSiteScanner.swift +++ b/phpmon/Domain/Integrations/Valet/Sites/SiteScanner/ValetSiteScanner.swift @@ -12,8 +12,8 @@ class ValetSiteScanner: SiteScanner { func resolveSiteCount(paths: [String]) -> Int { return paths.map { path in - let entries = try! FileManager.default - .contentsOfDirectory(atPath: path) + let entries = try! FileSystem + .getContentsOfDirectory(path) return entries .map { self.isSite($0, forPath: path) } @@ -27,8 +27,8 @@ class ValetSiteScanner: SiteScanner { var sites: [ValetSite] = [] paths.forEach { path in - let entries = try! FileManager.default - .contentsOfDirectory(atPath: path) + let entries = try! FileSystem + .getContentsOfDirectory(path) return entries.forEach { if let site = self.resolveSite(path: "\(path)/\($0)") { @@ -48,24 +48,19 @@ class ValetSiteScanner: SiteScanner { // Get the TLD from the global Valet object let tld = Valet.shared.config.tld - // See if the file is a symlink, if so, resolve it - guard let attrs = try? FileManager.default.attributesOfItem(atPath: path) else { + if !FileSystem.anyExists(path) { Log.warn("Could not parse the site: \(path), skipping!") - return nil } - // We can also determine whether the thing at the path is a directory, too - let type = attrs[FileAttributeKey.type] as! FileAttributeType - // We should also check that we can interpret the path correctly if URL(fileURLWithPath: path).lastPathComponent == "" { Log.warn("Could not parse the site: \(path), skipping!") return nil } - if type == FileAttributeType.typeSymbolicLink { + if FileSystem.isSymlink(path) { return ValetSite(aliasPath: path, tld: tld) - } else if type == FileAttributeType.typeDirectory { + } else if FileSystem.isDirectory(path) { return ValetSite(absolutePath: path, tld: tld) } @@ -79,14 +74,6 @@ class ValetSiteScanner: SiteScanner { private func isSite(_ entry: String, forPath path: String) -> Bool { let siteDir = path + "/" + entry - let attrs = try! FileManager.default.attributesOfItem(atPath: siteDir) - - let type = attrs[FileAttributeKey.type] as! FileAttributeType - - if type == FileAttributeType.typeSymbolicLink || type == FileAttributeType.typeDirectory { - return true - } - - return false + return (FileSystem.isDirectory(siteDir) || FileSystem.isSymlink(siteDir)) } } diff --git a/phpmon/Domain/Integrations/Valet/Sites/ValetSite.swift b/phpmon/Domain/Integrations/Valet/Sites/ValetSite.swift index 33212f0..7502996 100644 --- a/phpmon/Domain/Integrations/Valet/Sites/ValetSite.swift +++ b/phpmon/Domain/Integrations/Valet/Sites/ValetSite.swift @@ -94,7 +94,7 @@ class ValetSite: DomainListable { convenience init(aliasPath: String, tld: String) { let name = URL(fileURLWithPath: aliasPath).lastPathComponent - let absolutePath = try! FileManager.default.destinationOfSymbolicLink(atPath: aliasPath) + let absolutePath = try! FileSystem.getDestinationOfSymlink(aliasPath) self.init(name: name, tld: tld, absolutePath: absolutePath, aliasPath: aliasPath) } diff --git a/phpmon/Domain/Integrations/Valet/Valet.swift b/phpmon/Domain/Integrations/Valet/Valet.swift index 2bdc6fe..9a44bbe 100644 --- a/phpmon/Domain/Integrations/Valet/Valet.swift +++ b/phpmon/Domain/Integrations/Valet/Valet.swift @@ -101,7 +101,7 @@ class Valet { do { config = try JSONDecoder().decode( Valet.Configuration.self, - from: FileSystem.readStringFromFile("~/.config/valet/config.json").data(using: .utf8)! + from: FileSystem.getStringFromFile("~/.config/valet/config.json").data(using: .utf8)! ) } catch { Log.err(error) @@ -206,10 +206,7 @@ class Valet { proxies = ValetScanners.proxyScanner .resolveProxies( - directoryPath: FileManager.default - .homeDirectoryForCurrentUser - .appendingPathComponent(".config/valet/Nginx") - .path + directoryPath: "~/.config/valet/Nginx".replacingTildeWithHomeDirectory ) if let defaultPath = Valet.shared.config.defaultSite, diff --git a/phpmon/Domain/Warnings/WarningManager.swift b/phpmon/Domain/Warnings/WarningManager.swift index 516e0e7..ff2c3d5 100644 --- a/phpmon/Domain/Warnings/WarningManager.swift +++ b/phpmon/Domain/Warnings/WarningManager.swift @@ -33,7 +33,7 @@ class WarningManager { Warning( command: { return !Shell.PATH.contains("\(Paths.homePath)/.config/phpmon/bin") && - !FileManager.default.isWritableFile(atPath: "/usr/local/bin/") + !FileSystem.isWriteableFile("/usr/local/bin/") }, name: "Helpers cannot be symlinked and not in PATH", title: "warnings.helper_permissions.title", diff --git a/phpmon/Domain/Watcher/PhpConfigWatcher.swift b/phpmon/Domain/Watcher/PhpConfigWatcher.swift index fc00cc3..62ce21a 100644 --- a/phpmon/Domain/Watcher/PhpConfigWatcher.swift +++ b/phpmon/Domain/Watcher/PhpConfigWatcher.swift @@ -21,6 +21,10 @@ class PhpConfigWatcher { var watchers: [FSWatcher] = [] init(for url: URL) { + if FileSystem is TestableFileSystem { + fatalError("PhpConfigWatcher is not compatible with testable FS! You are not allowed to instantiate these while using a testable FS.") + } + self.url = url // Add a watcher for php.ini