diff --git a/.gitignore b/.gitignore
index d32735f..a0090b4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,67 +1,2 @@
-# Xcode
-#
-# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
-
-## Build generated
-build/
-DerivedData/
-
-## Various settings
-*.pbxuser
-!default.pbxuser
-*.mode1v3
-!default.mode1v3
-*.mode2v3
-!default.mode2v3
-*.perspectivev3
-!default.perspectivev3
-xcuserdata/
-
-## Other
-*.moved-aside
-*.xcuserstate
-*.xcscmblueprint
-
-## Obj-C/Swift specific
-*.hmap
-*.ipa
-*.dSYM.zip
-*.dSYM
-
-## Playgrounds
-timeline.xctimeline
-playground.xcworkspace
-
-# Swift Package Manager
-#
-# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
-# Packages/
-.build/
-.swiftpm/
-
-# CocoaPods
-#
-# We recommend against adding the Pods directory to your .gitignore. However
-# you should judge for yourself, the pros and cons are mentioned at:
-# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
-#
-# Pods/
-
-# Carthage
-#
-# Add this line if you want to avoid checking in source code from Carthage dependencies.
-# Carthage/Checkouts
-
-Carthage/Build
-
-# fastlane
-#
-# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
-# screenshots whenever they are needed.
-# For more information about the recommended setup visit:
-# https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md
-
-fastlane/report.xml
-fastlane/Preview.html
-fastlane/screenshots
-fastlane/test_output
+# macOS
+.DS_Store
diff --git a/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata b/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000..919434a
--- /dev/null
+++ b/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/.swiftpm/xcode/package.xcworkspace/xcuserdata/andrew.xcuserdatad/UserInterfaceState.xcuserstate b/.swiftpm/xcode/package.xcworkspace/xcuserdata/andrew.xcuserdatad/UserInterfaceState.xcuserstate
new file mode 100644
index 0000000..8a8324b
Binary files /dev/null and b/.swiftpm/xcode/package.xcworkspace/xcuserdata/andrew.xcuserdatad/UserInterfaceState.xcuserstate differ
diff --git a/.swiftpm/xcode/xcuserdata/andrew.xcuserdatad/xcschemes/xcschememanagement.plist b/.swiftpm/xcode/xcuserdata/andrew.xcuserdatad/xcschemes/xcschememanagement.plist
new file mode 100644
index 0000000..8b91339
--- /dev/null
+++ b/.swiftpm/xcode/xcuserdata/andrew.xcuserdatad/xcschemes/xcschememanagement.plist
@@ -0,0 +1,14 @@
+
+
+
+
+ SchemeUserState
+
+ Files.xcscheme_^#shared#^_
+
+ orderHint
+ 0
+
+
+
+
diff --git a/Package.swift b/Package.swift
index cc7dc34..0557dbb 100644
--- a/Package.swift
+++ b/Package.swift
@@ -1,4 +1,4 @@
-// swift-tools-version:5.0
+// swift-tools-version:6.2
/**
* Files
diff --git a/Sources/Files.swift b/Sources/Files.swift
index aede19e..1b94cf2 100644
--- a/Sources/Files.swift
+++ b/Sources/Files.swift
@@ -47,6 +47,17 @@ public protocol Location: Equatable, CustomStringConvertible {
init(storage: Storage)
}
+/// Protocol adopted by locations that represent files.
+public protocol FileLocation: Location {}
+
+public extension FileLocation {
+ /// The file size, in bytes. Returns `0` if the size couldn't be determined.
+ var size: UInt64 {
+ let number = storage.attributes[.size] as? NSNumber
+ return number?.uint64Value ?? 0
+ }
+}
+
public extension Location {
static func ==(lhs: Self, rhs: Self) -> Bool {
return lhs.storage.path == rhs.storage.path
@@ -79,6 +90,19 @@ public extension Location {
return components.dropLast().joined()
}
+ /// Disco: The name of the location, excluding its `extension`.
+ var baseName: String {
+ get {
+ let components = name.split(separator: ".")
+ guard components.count > 1 else { return name }
+ return components.dropLast().joined()
+ }
+ set {
+ try? rename(to: newValue, keepExtension: true)
+ }
+ }
+
+
/// The file extension of the item at the location.
var `extension`: String? {
let components = name.split(separator: ".")
@@ -364,7 +388,7 @@ private extension Storage where LocationType == Folder {
/// Type that represents a file on disk. You can either reference an existing
/// file by initializing an instance with a `path`, or you can create new files
/// using the various `createFile...` APIs available on `Folder`.
-public struct File: Location {
+public struct File: Location, FileLocation {
public let storage: Storage
public init(storage: Storage) {
@@ -406,9 +430,15 @@ public extension File {
func append(_ data: Data) throws {
do {
let handle = try FileHandle(forWritingTo: url)
- handle.seekToEndOfFile()
- handle.write(data)
- handle.closeFile()
+ if #available(macOS 10.15.4, *) {
+ _ = try handle.seekToEnd()
+ try handle.write(contentsOf: data)
+ try handle.close()
+ } else {
+ handle.seekToEndOfFile()
+ handle.write(data)
+ handle.closeFile()
+ }
} catch {
throw WriteError(path: path, reason: .writeFailed(error))
}
@@ -466,7 +496,7 @@ import AppKit
public extension File {
/// Open the file.
func open() {
- NSWorkspace.shared.openFile(path)
+ NSWorkspace.shared.open(url)
}
}
diff --git a/Tests/FilesTests/FilesTests.swift b/Tests/FilesTests/FilesTests.swift
index 040f60e..b32ae73 100644
--- a/Tests/FilesTests/FilesTests.swift
+++ b/Tests/FilesTests/FilesTests.swift
@@ -197,6 +197,14 @@ class FilesTests: XCTestCase {
try assert(nonIntFile.readAsInt(), throwsErrorOfType: ReadError.self)
}
}
+
+ func testFileSize() {
+ performTest {
+ let data = Data("Hello".utf8)
+ let file = try folder.createFile(named: "size.txt", contents: data)
+ XCTAssertEqual(file.size, UInt64(data.count))
+ }
+ }
func testRenamingFile() {
performTest {