Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Swift
name: SwiftWasm
on:
push:
branches: ["wasm32-wasi-release/6.0"]
Expand Down Expand Up @@ -39,7 +39,7 @@ jobs:
- uses: actions/checkout@v4
- uses: bytecodealliance/actions/wasmtime/setup@v1
with:
version: "25.0.1"
version: "25.0.2"
- run: swift --version
- run: wasmtime -V
- run: swift sdk install $SWIFT_SDK_URL --checksum $SWIFT_SDK_CHECKSUM
Expand Down
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# kkebo/swift-format

The WebAssembly (WASI) version of `swift-format`.
The WebAssembly (WASI) version of `swift-format`. This project's goal is to be merged into [swiftlang/swift-format](https://github.com/swiftlang/swift-format).

## Download

Expand All @@ -17,8 +17,9 @@ The checked items are currently supported. The others are planned to be supporte

- [x] [a-Shell (`wasm` and `wasm3`)](https://github.com/holzschu/a-shell?tab=readme-ov-file) (iOS, iPadOS)
- [x] [Wasmtime](https://wasmtime.dev)
- [ ] [Wasmer](https://wasmer.io)
- [ ] [WasmKit](https://github.com/swiftwasm/WasmKit)
- [x] [Wasmer](https://wasmer.io)
- [x] [WasmKit](https://github.com/swiftwasm/WasmKit)
- [x] [WasmEdge](https://wasmedge.org)

## Restrictions

Expand Down
134 changes: 113 additions & 21 deletions Sources/WASIHelpers/FileManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,27 +11,49 @@
//===----------------------------------------------------------------------===//

import Foundation
import _FoundationCShims

#if os(WASI)
extension FileManager {
public func enumeratorWASI(
at url: URL,
includingPropertiesForKeys keys: [URLResourceKey]?,
options mask: FileManager.DirectoryEnumerationOptions = [],
errorHandler handler: (/* @escaping */ (URL, Error) -> Bool)? = nil
) -> FileManager.WASIDirectoryEnumerator? {
// TODO: Use arguments
.init(at: url)
errorHandler handler: ( /* @escaping */(URL, Error) -> Bool)? = nil
) -> DirectoryEnumerator? {
NSURLDirectoryEnumerator(url: url, options: mask, errorHandler: handler)
}
}

extension FileManager {
/// Thread-unsafe directory enumerator.
public class WASIDirectoryEnumerator: Sequence, IteratorProtocol {
private var dpStack = [(URL, OpaquePointer)]()
class NSURLDirectoryEnumerator: DirectoryEnumerator {
private(set) var dpStack = [(URL, OpaquePointer)]()
var url: URL
var options: FileManager.DirectoryEnumerationOptions
var errorHandler: ((URL, any Error) -> Bool)?
var rootError: (any Error)? = nil

fileprivate init(at url: URL) {
appendDirectoryPointer(of: url)
init(
url: URL,
options: FileManager.DirectoryEnumerationOptions,
errorHandler: ( /* @escaping */(URL, Error) -> Bool)?
) {
self.url = url
self.options = options
self.errorHandler = errorHandler
super.init()

let fm = FileManager.default
do {
guard fm.fileExists(atPath: url.path) else { throw _NSErrorWithErrno(ENOENT, reading: true, url: url) }
guard let dp = fm.withFileSystemRepresentation(for: url.path, opendir) else {
throw _NSErrorWithErrno(errno, reading: true, url: url)
}
dpStack.append((url, dp))
} catch {
rootError = error
}
}

deinit {
Expand All @@ -45,33 +67,103 @@ extension FileManager {
dpStack.append((url, dp))
}

public func next() -> Any? {
override func nextObject() -> Any? {
func match(filename: String, to options: DirectoryEnumerationOptions, isDir: Bool) -> (Bool, Bool) {
var showFile = true
var skipDescendants = false

if isDir {
if options.contains(.skipsSubdirectoryDescendants) {
skipDescendants = true
}
// Ignore .skipsPackageDescendants
}
if options.contains(.skipsHiddenFiles) && filename.hasPrefix(".") {
showFile = false
skipDescendants = true
}

return (showFile, skipDescendants)
}

while let (url, dp) = dpStack.last {
while let ep = readdir(dp) {
let filename = withUnsafeBytes(of: &ep.pointee.d_type) { rawPtr in
// UnsafeRawPointer of d_name
let d_namePtr = rawPtr.baseAddress! + MemoryLayout<UInt8>.stride
return String(cString: d_namePtr.assumingMemoryBound(to: CChar.self))
}
guard ep.pointee.d_ino != 0 else { continue }
let filename = String(cString: _platform_shims_dirent_d_name(ep))
guard filename != "." && filename != ".." else { continue }
let child = url.appendingPathComponent(filename)
var status = stat()
if child.withUnsafeFileSystemRepresentation({ stat($0, &status) }) == 0, (status.st_mode & S_IFMT) == S_IFDIR {
appendDirectoryPointer(of: child)
return child
var isDirectory = false
if ep.pointee.d_type == _platform_shims_DT_DIR() {
isDirectory = true
} else if ep.pointee.d_type == _platform_shims_DT_UNKNOWN() {
var status = stat()
if stat(child.path, &status) == 0, (status.st_mode & S_IFMT) == S_IFDIR {
isDirectory = true
}
}
if isDirectory {
let (showFile, skipDescendants) = match(filename: filename, to: options, isDir: true)
if !skipDescendants {
appendDirectoryPointer(of: child)
}
if showFile {
return child
}
} else {
return child
let (showFile, _) = match(filename: filename, to: options, isDir: false)
if showFile {
return child
}
}
}
closedir(dp)
dpStack.removeLast()
}
if let error = rootError, let handler = errorHandler {
let _ = handler(url, error)
}
return nil
}
}
}

public func nextObject() -> Any? {
next()
func _NSErrorWithErrno(
_ posixErrno: Int32,
reading: Bool,
path: String? = nil,
url: URL? = nil,
extraUserInfo: [String: Any]? = nil
) -> NSError {
var cocoaError: CocoaError.Code
if reading {
switch posixErrno {
case EFBIG: cocoaError = .fileReadTooLarge
case ENOENT: cocoaError = .fileReadNoSuchFile
case EPERM, EACCES: cocoaError = .fileReadNoPermission
case ENAMETOOLONG: cocoaError = .fileReadUnknown
default: cocoaError = .fileReadUnknown
}
} else {
switch posixErrno {
case ENOENT: cocoaError = .fileNoSuchFile
case EPERM, EACCES: cocoaError = .fileWriteNoPermission
case ENAMETOOLONG: cocoaError = .fileWriteInvalidFileName
case EDQUOT, ENOSPC: cocoaError = .fileWriteOutOfSpace
case EROFS: cocoaError = .fileWriteVolumeReadOnly
case EEXIST: cocoaError = .fileWriteFileExists
default: cocoaError = .fileWriteUnknown
}
}

var userInfo = extraUserInfo ?? [String: Any]()
if let path = path {
userInfo[NSFilePathErrorKey] = path
} else if let url = url {
userInfo[NSURLErrorKey] = url
}

userInfo[NSUnderlyingErrorKey] = NSError(domain: NSPOSIXErrorDomain, code: Int(posixErrno))

return NSError(domain: NSCocoaErrorDomain, code: cocoaError.rawValue, userInfo: userInfo)
}
#endif
8 changes: 1 addition & 7 deletions Sources/swift-format/Utilities/FileIterator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,6 @@ import Foundation
import WASIHelpers
#endif

#if !os(WASI)
private typealias DirectoryEnumerator = FileManager.DirectoryEnumerator
#else
private typealias DirectoryEnumerator = FileManager.WASIDirectoryEnumerator
#endif

/// Iterator for looping over lists of files and directories. Directories are automatically
/// traversed recursively, and we check for files with a ".swift" extension.
@_spi(Testing)
Expand All @@ -37,7 +31,7 @@ public struct FileIterator: Sequence, IteratorProtocol {
private var urlIterator: Array<URL>.Iterator

/// Iterator for recursing through directories.
private var dirIterator: DirectoryEnumerator? = nil
private var dirIterator: FileManager.DirectoryEnumerator? = nil

/// The current working directory of the process, which is used to relativize URLs of files found
/// during iteration.
Expand Down