Skip to content

Commit b7667f3

Browse files
authored
add isReadable and isWritable APIs to FileSystem (#286)
motivation: users often need to check if a path is readable and / or writable changes: * add isReadable and isWritable APIs to FileSystem * implement in relevant FileSystem implementations * add tests
1 parent 6017b48 commit b7667f3

File tree

2 files changed

+98
-0
lines changed

2 files changed

+98
-0
lines changed

Diff for: Sources/TSCBasic/FileSystem.swift

+30
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,12 @@ public protocol FileSystem: AnyObject {
162162
/// Check whether the given path is accessible and is a symbolic link.
163163
func isSymlink(_ path: AbsolutePath) -> Bool
164164

165+
/// Check whether the given path is accessible and readable.
166+
func isReadable(_ path: AbsolutePath) -> Bool
167+
168+
/// Check whether the given path is accessible and writable.
169+
func isWritable(_ path: AbsolutePath) -> Bool
170+
165171
// FIXME: Actual file system interfaces will allow more efficient access to
166172
// more data than just the name here.
167173
//
@@ -321,6 +327,14 @@ private class LocalFileSystem: FileSystem {
321327
return attrs?[.type] as? FileAttributeType == .typeSymbolicLink
322328
}
323329

330+
func isReadable(_ path: AbsolutePath) -> Bool {
331+
FileManager.default.isReadableFile(atPath: path.pathString)
332+
}
333+
334+
func isWritable(_ path: AbsolutePath) -> Bool {
335+
FileManager.default.isWritableFile(atPath: path.pathString)
336+
}
337+
324338
func getFileInfo(_ path: AbsolutePath) throws -> FileInfo {
325339
let attrs = try FileManager.default.attributesOfItem(atPath: path.pathString)
326340
return FileInfo(attrs)
@@ -715,6 +729,14 @@ public class InMemoryFileSystem: FileSystem {
715729
}
716730
}
717731

732+
public func isReadable(_ path: AbsolutePath) -> Bool {
733+
self.exists(path)
734+
}
735+
736+
public func isWritable(_ path: AbsolutePath) -> Bool {
737+
self.exists(path)
738+
}
739+
718740
public func isExecutableFile(_ path: AbsolutePath) -> Bool {
719741
// FIXME: Always return false until in-memory implementation
720742
// gets permission semantics.
@@ -1028,6 +1050,14 @@ public class RerootedFileSystemView: FileSystem {
10281050
return underlyingFileSystem.isSymlink(formUnderlyingPath(path))
10291051
}
10301052

1053+
public func isReadable(_ path: AbsolutePath) -> Bool {
1054+
return underlyingFileSystem.isReadable(formUnderlyingPath(path))
1055+
}
1056+
1057+
public func isWritable(_ path: AbsolutePath) -> Bool {
1058+
return underlyingFileSystem.isWritable(formUnderlyingPath(path))
1059+
}
1060+
10311061
public func isExecutableFile(_ path: AbsolutePath) -> Bool {
10321062
return underlyingFileSystem.isExecutableFile(formUnderlyingPath(path))
10331063
}

Diff for: Tests/TSCBasicTests/FileSystemTests.swift

+68
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,74 @@ class FileSystemTests: XCTestCase {
183183
}
184184
}
185185

186+
func testLocalReadableWritable() throws {
187+
try testWithTemporaryDirectory { tmpdir in
188+
let fs = localFileSystem
189+
190+
// directory
191+
192+
do {
193+
let directory = tmpdir.appending(component: "directory")
194+
try fs.createDirectory(directory, recursive: true)
195+
196+
// should be readable and writable by default
197+
XCTAssertTrue(fs.isReadable(directory))
198+
XCTAssertTrue(fs.isWritable(directory))
199+
200+
// set to non-readable non-writable.
201+
_ = try Process.popen(args: "chmod", "-r-w", directory.pathString)
202+
XCTAssertFalse(fs.isReadable(directory))
203+
XCTAssertFalse(fs.isWritable(directory))
204+
205+
// set to readable non-writable.
206+
_ = try Process.popen(args: "chmod", "+r-w", directory.pathString)
207+
XCTAssertTrue(fs.isReadable(directory))
208+
XCTAssertFalse(fs.isWritable(directory))
209+
210+
// set to non-readable writable.
211+
_ = try Process.popen(args: "chmod", "-r+w", directory.pathString)
212+
XCTAssertFalse(fs.isReadable(directory))
213+
XCTAssertTrue(fs.isWritable(directory))
214+
215+
// set to readable and writable.
216+
_ = try Process.popen(args: "chmod", "+r+w", directory.pathString)
217+
XCTAssertTrue(fs.isReadable(directory))
218+
XCTAssertTrue(fs.isWritable(directory))
219+
}
220+
221+
// file
222+
223+
do {
224+
let file = tmpdir.appending(component: "file")
225+
try fs.writeFileContents(file, bytes: "")
226+
227+
// should be readable and writable by default
228+
XCTAssertTrue(fs.isReadable(file))
229+
XCTAssertTrue(fs.isWritable(file))
230+
231+
// set to non-readable non-writable.
232+
_ = try Process.popen(args: "chmod", "-r-w", file.pathString)
233+
XCTAssertFalse(fs.isReadable(file))
234+
XCTAssertFalse(fs.isWritable(file))
235+
236+
// set to readable non-writable.
237+
_ = try Process.popen(args: "chmod", "+r-w", file.pathString)
238+
XCTAssertTrue(fs.isReadable(file))
239+
XCTAssertFalse(fs.isWritable(file))
240+
241+
// set to non-readable writable.
242+
_ = try Process.popen(args: "chmod", "-r+w", file.pathString)
243+
XCTAssertFalse(fs.isReadable(file))
244+
XCTAssertTrue(fs.isWritable(file))
245+
246+
// set to readable and writable.
247+
_ = try Process.popen(args: "chmod", "+r+w", file.pathString)
248+
XCTAssertTrue(fs.isReadable(file))
249+
XCTAssertTrue(fs.isWritable(file))
250+
}
251+
}
252+
}
253+
186254
func testLocalCreateDirectory() throws {
187255
let fs = TSCBasic.localFileSystem
188256

0 commit comments

Comments
 (0)