diff --git a/Snippets/NIOFileSystemTour.swift b/Snippets/NIOFileSystemTour.swift new file mode 100644 index 0000000000..7fe5947e35 --- /dev/null +++ b/Snippets/NIOFileSystemTour.swift @@ -0,0 +1,97 @@ +// snippet.hide +import _NIOFileSystem +import NIOCore +// snippet.show + +// NIOFileSystem provides access to the local file system via the FileSystem +// type which is available as a global shared instance. +let fileSystem = FileSystem.shared + +// Files can be inspected by using 'info': +if let info = try await fileSystem.info(forFileAt: "/Users/hal9000/demise-of-dave.txt") { + print("demise-of-dave.txt has type '\(info.type)'") +} else { + print("demise-of-dave.txt doesn't exist") +} + +// Let's find out what's in that file. +do { + // Reading a whole file requires a limit. If the file is larger than the limit + // then an error is thrown. This avoids accidentally consuming too much memory + // if the file is larger than expected. + let plan = try await ByteBuffer( + contentsOf: "/Users/hal9000/demise-of-dave.txt", + maximumSizeAllowed: .mebibytes(1) + ) + print("Plan for Dave's demise:", String(decoding: plan.readableBytesView, as: UTF8.self)) +} catch let error as FileSystemError where error.code == .notFound { + // All errors thrown by the module have type FileSystemError (or + // Swift.CancellationError). It looks like the file doesn't exist. Let's + // create it now. + // + // The code above for reading the file is shorthand for opening the file in + // read-only mode and then reading its contents. The FileSystemProtocol + // has a few different 'withFileHandle' methods for opening a file in different + // modes. Let's open a file for writing, creating it at the same time. + try await fileSystem.withFileHandle( + forWritingAt: "/Users/hal9000/demise-of-dave.txt", + options: .newFile(replaceExisting: false) + ) { file in + let plan = ByteBuffer(string: "TODO...") + try await file.write(contentsOf: plan.readableBytesView, toAbsoluteOffset: 0) + } +} + +// Directories can be opened like regular files but they cannot be read from or +// written to. However, their contents can be listed: +let path: FilePath? = try await fileSystem.withDirectoryHandle(atPath: "/Users/hal9000/Music") { directory in + for try await entry in directory.listContents() { + if entry.name.extension == "mp3", entry.name.stem.contains("daisy") { + // Found it! + return entry.path + } + } + // No luck. + return nil +} + +if let path = path { + print("Found file at '\(path)'") +} + +// The file system can also be used to perform the following operations on files +// and directories: +// - copy, +// - remove, +// - rename, and +// - replace. +// +// Here's an example of copying a directory: +try await fileSystem.copyItem(at: "/Users/hal9000/Music", to: "/Volumes/Tardis/Music") + +// Symbolic links can also be created (and read with 'destinationOfSymbolicLink(at:)'). +try await fileSystem.createSymbolicLink(at: "/Users/hal9000/Backup", withDestination: "/Volumes/Tardis") + +// Opening a symbolic link opens its destination so in most cases there's no +// need to read the destination of a symbolic link: +try await fileSystem.withDirectoryHandle(atPath: "/Users/hal9000/Backup") { directory in + // Beyond listing the contents of a directory, the directory handle provides a + // number of other functions, many of which are also available on regular file + // handles. + // + // This includes getting information about a file, such as its permissions, last access time, + // and last modification time: + let info = try await directory.info() + print("The directory has permissions '\(info.permissions)'") + + // Where supported, the extended attributes of a file can also be accessed, read, and modified: + for attribute in try await directory.attributeNames() { + let value = try await directory.valueForAttribute(attribute) + print("Extended attribute '\(attribute)' has value '\(value)'") + } + + // Once this closure returns the file system will close the directory handle freeing + // any resources required to access it such as file descriptors. Handles can also be opened + // with the 'openFile' and 'openDirectory' APIs but that places the onus you to close the + // handle at an appropriate time to avoid leaking resources. +} diff --git a/Sources/NIOFileSystem/Docs.docc/NIOFileSystem.md b/Sources/NIOFileSystem/Docs.docc/NIOFileSystem.md index 1803cac5ad..4fbf466541 100644 --- a/Sources/NIOFileSystem/Docs.docc/NIOFileSystem.md +++ b/Sources/NIOFileSystem/Docs.docc/NIOFileSystem.md @@ -23,102 +23,7 @@ to a set of protocols for creating other file system implementations. The following sample code demonstrates a number of the APIs offered by this module: -```swift -import _NIOFileSystem - -// NIOFileSystem provides access to the local file system via the FileSystem -// type which is available as a global shared instance. -let fileSystem = FileSystem.shared - -// Files can be inspected by using 'info': -if let info = try await fileSystem.info(forFileAt: "/Users/hal9000/demise-of-dave.txt") { - print("demise-of-dave.txt has type '\(info.type)'") -} else { - print("demise-of-dave.txt doesn't exist") -} - -// Let's find out what's in that file. -do { - // Reading a whole file requires a limit. If the file is larger than the limit - // then an error is thrown. This avoids accidentally consuming too much memory - // if the file is larger than expected. - let plan = try await ByteBuffer( - contentsOf: "/Users/hal9000/demise-of-dave.txt", - maximumSizeAllowed: .mebibytes(1) - ) - print("Plan for Dave's demise:", String(decoding: plan, as: UTF8.self)) -} catch let error as FileSystemError where error.code == .notFound { - // All errors thrown by the module have type FileSystemError (or - // Swift.CancellationError). It looks like the file doesn't exist. Let's - // create it now. - // - // The code above for reading the file is shorthand for opening the file in - // read-only mode and then reading its contents. The FileSystemProtocol - // has a few different 'withFileHandle' methods for opening a file in different - // modes. Let's open a file for writing, creating it at the same time. - try await fileSystem.withFileHandle( - forWritingAt: "/Users/hal9000/demise-of-dave.txt", - options: .newFile(replaceExisting: false) - ) { file in - let plan = ByteBuffer(string: "TODO...") - try await file.write(contentsOf: plan.readableBytesView, toAbsoluteOffset: 0) - } -} - -// Directories can be opened like regular files but they cannot be read from or -// written to. However, their contents can be listed: -let path: FilePath? = try await fileSystem.withDirectoryHandle(atPath: "/Users/hal9000/Music") { directory in - for try await entry in directory.listContents() { - if entry.name.extension == "mp3", entry.name.stem.contains("daisy") { - // Found it! - return entry.path - } - } - // No luck. - return nil -} - -if let path = path { - print("Found file at '\(path)'") -} - -// The file system can also be used to perform the following operations on files -// and directories: -// - copy, -// - remove, -// - rename, and -// - replace. -// -// Here's an example of copying a directory: -try await fileSystem.copyItem(at: "/Users/hal9000/Music", to: "/Volumes/Tardis/Music") - -// Symbolic links can also be created (and read with 'destinationOfSymbolicLink(at:)'). -try await fileSystem.createSymbolicLink(at: "/Users/hal9000/Backup", withDestination: "/Volumes/Tardis") - -// Opening a symbolic link opens its destination so in most cases there's no -// need to read the destination of a symbolic link: -try await fileSystem.withDirectoryHandle(atPath: "/Users/hal9000/Backup") { directory in - // Beyond listing the contents of a directory, the directory handle provides a - // number of other functions, many of which are also available on regular file - // handles. - // - // This includes getting information about a file, such as its permissions, last access time, - // and last modification time: - let info = try await directory.info() - print("The directory has permissions '\(info.permissions)'") - - // Where supported, the extended attributes of a file can also be accessed, read, and modified: - for attribute in try await directory.attributeNames() { - let value = try await directory.valueForAttribute(attribute) - print("Extended attribute '\(attribute)' has value '\(value)'") - } - - // Once this closure returns the file system will close the directory handle freeing - // any resources required to access it such as file descriptors. Handles can also be opened - // with the 'openFile' and 'openDirectory' APIs but that places the onus you to close the - // handle at an appropriate time to avoid leaking resources. -} -``` +@Snippet(path: "swift-nio/Snippets/NIOFileSystemTour") In depth documentation can be found in the following sections. diff --git a/scripts/soundness.sh b/scripts/soundness.sh index b66cf420a6..1e6c2ca67b 100755 --- a/scripts/soundness.sh +++ b/scripts/soundness.sh @@ -51,7 +51,7 @@ for language in swift-or-c bash dtrace python; do matching_files=( -name '*' ) case "$language" in swift-or-c) - exceptions=( -name c_nio_llhttp.c -o -name c_nio_api.c -o -name c_nio_http.c -o -name c_nio_llhttp.h -o -name cpp_magic.h -o -name Package.swift -o -name 'Package@*.swift' -o -name CNIOSHA1.h -o -name c_nio_sha1.c -o -name ifaddrs-android.c -o -name ifaddrs-android.h) + exceptions=( -name c_nio_llhttp.c -o -name c_nio_api.c -o -name c_nio_http.c -o -name c_nio_llhttp.h -o -name cpp_magic.h -o -name Package.swift -o -name 'Package@*.swift' -o -path './Snippets/*' -o -name CNIOSHA1.h -o -name c_nio_sha1.c -o -name ifaddrs-android.c -o -name ifaddrs-android.h) matching_files=( -name '*.swift' -o -name '*.c' -o -name '*.h' ) cat > "$tmp" <<"EOF" //===----------------------------------------------------------------------===//