Skip to content

Commit b6524d8

Browse files
committed
Update File Feautre
1 parent 3ec0bd9 commit b6524d8

File tree

7 files changed

+138
-0
lines changed

7 files changed

+138
-0
lines changed

Sources/File/Child.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
import Foundation
2+
import Logging
3+
4+
private let logger = Logger(label: "Child")
25

36
// MARK: - Child
47
public extension Folder {

Sources/File/File.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
import Foundation
2+
import Logging
3+
4+
private let logger = Logger(label: "File")
25

36
/// File functionality in PLFile
47
public struct File: FileSystem {
@@ -66,6 +69,14 @@ public extension File {
6669
}
6770
}
6871

72+
// MARK: - Existence
73+
public extension File {
74+
/// Checks if the file actually exists in the file system.
75+
func exists() -> Bool {
76+
return FileManager.default.fileExists(atPath: store.path.rawValue)
77+
}
78+
}
79+
6980
#if canImport(AppKit) && !targetEnvironment(macCatalyst)
7081
import AppKit
7182

Sources/File/FileError.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
import Foundation
2+
import Logging
3+
4+
private let logger = Logger(label: "FileError")
25

36
/// Error can be thrown by PLFile
47
public enum FileError: Error {

Sources/File/FileSystem.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
import Foundation
2+
import Logging
3+
4+
private let logger = Logger(label: "FileSystem")
25

36
/// Can use the functions of the file system as a key feature of PLFile.
47
public protocol FileSystem: Equatable, CustomStringConvertible {

Sources/File/Folder.swift

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
import Foundation
2+
import Logging
3+
4+
private let logger = Logger(label: "Folder")
25

36
/// Folder functionality in PLFile
47
public struct Folder: FileSystem {
@@ -78,3 +81,36 @@ extension Folder {
7881
try subfolders.delete()
7982
}
8083
}
84+
85+
// MARK: - Existence
86+
public extension Folder {
87+
/// Checks if the folder actually exists in the file system.
88+
func exists() -> Bool {
89+
var isDir: ObjCBool = false
90+
let exists = FileManager.default.fileExists(atPath: store.path.rawValue, isDirectory: &isDir)
91+
return exists && isDir.boolValue
92+
}
93+
}
94+
95+
// MARK: - All Files/Folders
96+
public extension Folder {
97+
/// Returns all files in the folder (supports recursive search).
98+
func allFiles(recursive: Bool = false) -> [File] {
99+
var result: [File] = []
100+
let sequence = recursive ? self.files.recursiveStatus : self.files
101+
for file in sequence {
102+
result.append(file)
103+
}
104+
return result
105+
}
106+
107+
/// Returns all subfolders in the folder (supports recursive search).
108+
func allFolders(recursive: Bool = false) -> [Folder] {
109+
var result: [Folder] = []
110+
let sequence = recursive ? self.subfolders.recursiveStatus : self.subfolders
111+
for folder in sequence {
112+
result.append(folder)
113+
}
114+
return result
115+
}
116+
}

Sources/File/Path.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import Foundation
2+
import Logging
3+
private let logger = Logger(label: "Path")
24

35
/// Manage the path of the PLFile.
46
public struct Path {

Sources/File/Store.swift

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import Foundation
2+
import Dispatch
3+
import Logging
24

35
/// A common part of the File and Folder functionality that allows you to set up the required paths and FileManager that the file system should use.
46
public final class Store<fileSystem: FileSystem> {
@@ -176,3 +178,81 @@ extension Store where fileSystem == Folder {
176178
return File(store: store)
177179
}
178180
}
181+
182+
// MARK: - Permission
183+
public extension Store {
184+
/// Returns the file or folder permissions as a POSIX mode_t (Int).
185+
func getPermissions() -> Int? {
186+
let attributes = try? fileManager.attributesOfItem(atPath: path.rawValue)
187+
return attributes?[.posixPermissions] as? Int
188+
}
189+
190+
/// Sets the file or folder permissions using a POSIX mode_t (Int).
191+
func setPermissions(_ permissions: Int) throws {
192+
do {
193+
try fileManager.setAttributes([.posixPermissions: permissions], ofItemAtPath: path.rawValue)
194+
} catch {
195+
throw FileError.writeFailed(path: path, error: error)
196+
}
197+
}
198+
}
199+
200+
// MARK: - Symbolic Link
201+
public extension Store {
202+
/// Checks if the path is a symbolic link.
203+
func isSymbolicLink() -> Bool {
204+
let attributes = try? fileManager.attributesOfItem(atPath: path.rawValue)
205+
let fileType = attributes?[.type] as? FileAttributeType
206+
return fileType == .typeSymbolicLink
207+
}
208+
209+
/// Creates a symbolic link at this path pointing to the destination path.
210+
func createSymbolicLink(to destinationPath: Path) throws {
211+
do {
212+
try fileManager.createSymbolicLink(atPath: path.rawValue, withDestinationPath: destinationPath.rawValue)
213+
} catch {
214+
throw FileError.writeFailed(path: path, error: error)
215+
}
216+
}
217+
218+
/// Returns the destination path of the symbolic link, if this is a symbolic link.
219+
func destinationOfSymbolicLink() -> Path? {
220+
do {
221+
let dest = try fileManager.destinationOfSymbolicLink(atPath: path.rawValue)
222+
return Path(dest)
223+
} catch {
224+
return nil
225+
}
226+
}
227+
}
228+
229+
// MARK: - File/Folder Change Watch
230+
public extension Store {
231+
/// Starts watching for changes to the file or folder at this path.
232+
/// - Parameters:
233+
/// - eventHandler: Called when a change is detected.
234+
/// - Returns: A DispatchSourceFileSystemObject? (macOS, iOS), or nil if not supported. Uses swift-log for logging.
235+
@discardableResult
236+
func watch(eventHandler: @escaping () -> Void) -> Any? {
237+
#if os(macOS) || os(iOS)
238+
let fd = open(path.rawValue, O_EVTONLY)
239+
guard fd != -1 else { return nil }
240+
let source = DispatchSource.makeFileSystemObjectSource(fileDescriptor: fd, eventMask: .all, queue: .main)
241+
source.setEventHandler(handler: eventHandler)
242+
source.setCancelHandler {
243+
close(fd)
244+
}
245+
source.resume()
246+
return source
247+
#elseif os(Linux)
248+
// Linux: inotify-based implementation needed (not implemented here)
249+
let logger = Logger(label: "Store.Watch")
250+
logger.warning("Watch is not implemented for Linux in this version.")
251+
return nil
252+
#else
253+
let logger = Logger(label: "Store.Watch")
254+
logger.warning("Watch is not supported on this platform.")
255+
return nil
256+
#endif
257+
}
258+
}

0 commit comments

Comments
 (0)