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
41 changes: 2 additions & 39 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,42 +7,5 @@ on:
branches: [ "main" ]

jobs:
unit_tests:
strategy:
fail-fast: false
matrix:
os: [macos-latest, macos-15-intel, ubuntu-latest, ubuntu-24.04-arm]
traits: ["none", "Foundation"]
runs-on: ${{ matrix.os }}

steps:
- uses: actions/checkout@v6
- uses: SwiftyLab/setup-swift@latest
- name: Determine traits flags
id: traits
run: |
if [[ ${{ matrix.traits }} = 'all' ]]; then
echo "flags=--enable-all-traits" >> $GITHUB_OUTPUT
elif [[ ${{ matrix.traits }} = 'none' ]]; then
echo "flags=--disable-default-traits" >> $GITHUB_OUTPUT
else
echo "flags=--traits ${{ matrix.traits }}" >> $GITHUB_OUTPUT
fi
- name: Build
run: swift build --build-tests --enable-code-coverage -v ${{ steps.traits.outputs.flags }}
- name: Run tests
run: swift test --enable-code-coverage -v ${{ steps.traits.outputs.flags }} --xunit-output testresults.xml
- name: Collect artifacts
if: always()
run: |
mkdir -p artifacts
cp testresults*.xml artifacts/ 2>/dev/null || true
find .build -path "*/codecov/*.json" | xargs -I{} cp {} artifacts/ 2>/dev/null || true

- name: Upload test results
uses: actions/upload-artifact@v7
if: always()
with:
name: test-results-${{ matrix.os }}-${{ matrix.traits }}
path: artifacts/

run-ci:
uses: CharlesJS/shared-workflows/.github/workflows/ci.yml@main
27 changes: 18 additions & 9 deletions Sources/CSErrors/Error+CSErrors.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,9 @@ extension Error {
return true
}

#if Foundation && canImport(Darwin)
if let err = (self as NSError).toCSErrorProtocol(), err.isFileNotFoundError {
if let err = self.toCSErrorProtocol(), err.isFileNotFoundError {
return true
}
#endif

return false
}
Expand All @@ -74,11 +72,9 @@ extension Error {
return true
}

#if Foundation && canImport(Darwin)
if let err = (self as NSError).toCSErrorProtocol(), err.isPermissionError {
if let err = self.toCSErrorProtocol(), err.isPermissionError {
return true
}
#endif

return false
}
Expand All @@ -103,12 +99,25 @@ extension Error {
return true
}

#if Foundation && canImport(Darwin)
if let err = (self as NSError).toCSErrorProtocol(), err.isCancelledError {
if let err = self.toCSErrorProtocol(), err.isCancelledError {
return true
}
#endif

return false
}

private func toCSErrorProtocol() -> (any CSErrorProtocol)? {
#if Foundation
#if canImport(Darwin)
(self as NSError).nserrorToCSErrorProtocol()
#else
switch self._domain {
case cocoaErrorDomain: CocoaError(CocoaError.Code(rawValue: self._code))
default: nil
}
#endif
#else
nil
#endif
}
}
11 changes: 2 additions & 9 deletions Sources/CSErrors/NSError+CSErrors.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,11 @@
// Created by Charles Srstka on 11/11/23.
//

#if Foundation && canImport(Darwin)

#if canImport(FoundationEssentials)
import FoundationEssentials
#else
#if Foundation && !canImport(FoundationEssentials)
import Foundation
#endif

import System

extension NSError {
internal func toCSErrorProtocol() -> (any CSErrorProtocol)? {
internal func nserrorToCSErrorProtocol() -> (any CSErrorProtocol)? {
switch self.domain {
case NSCocoaErrorDomain:
return self as? CocoaError
Expand Down
99 changes: 98 additions & 1 deletion Sources/CSErrors/OSStatus Errors.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ import System
#if canImport(Darwin)
import Darwin
#else
#if canImport(Glibc)
import Glibc
#endif
public typealias OSStatus = Int32
#endif

Expand Down Expand Up @@ -273,7 +276,101 @@ public func osStatusError(

internal func translateOSStatusToPOSIX(_ code: some BinaryInteger) -> Int32? {
let osStatus = OSStatus(code)


#if !canImport(Darwin)
// kPOSIXError* codes assume Darwin sys/errno.h, which can vary on other platforms

switch osStatus {
case 100001: return EPERM
case 100002: return ENOENT
case 100003: return ESRCH
case 100004: return EINTR
case 100005: return EIO
case 100006: return ENXIO
case 100007: return E2BIG
case 100008: return ENOEXEC
case 100009: return EBADF
case 100010: return ECHILD
case 100011: return EDEADLK
case 100012: return ENOMEM
case 100013: return EACCES
case 100014: return EFAULT
case 100015: return ENOTBLK
case 100016: return EBUSY
case 100017: return EEXIST
case 100018: return EXDEV
case 100019: return ENODEV
case 100020: return ENOTDIR
case 100021: return EISDIR
case 100022: return EINVAL
case 100023: return ENFILE
case 100024: return EMFILE
case 100025: return ENOTTY
case 100026: return ETXTBSY
case 100027: return EFBIG
case 100028: return ENOSPC
case 100029: return ESPIPE
case 100030: return EROFS
case 100031: return EMLINK
case 100032: return EPIPE
case 100033: return EDOM
case 100034: return ERANGE
case 100035: return EAGAIN
case 100036: return EINPROGRESS
case 100037: return EALREADY
case 100038: return ENOTSOCK
case 100039: return EDESTADDRREQ
case 100040: return EMSGSIZE
case 100041: return EPROTOTYPE
case 100042: return ENOPROTOOPT
case 100043: return EPROTONOSUPPORT
case 100044: return ESOCKTNOSUPPORT
case 100045: return ENOTSUP
case 100046: return EPFNOSUPPORT
case 100047: return EAFNOSUPPORT
case 100048: return EADDRINUSE
case 100049: return EADDRNOTAVAIL
case 100050: return ENETDOWN
case 100051: return ENETUNREACH
case 100052: return ENETRESET
case 100053: return ECONNABORTED
case 100054: return ECONNRESET
case 100055: return ENOBUFS
case 100056: return EISCONN
case 100057: return ENOTCONN
case 100058: return ESHUTDOWN
case 100059: return ETOOMANYREFS
case 100060: return ETIMEDOUT
case 100061: return ECONNREFUSED
case 100062: return ELOOP
case 100063: return ENAMETOOLONG
case 100064: return EHOSTDOWN
case 100065: return EHOSTUNREACH
case 100066: return ENOTEMPTY
case 100068: return EUSERS
case 100069: return EDQUOT
case 100070: return ESTALE
case 100071: return EREMOTE
case 100077: return ENOLCK
case 100078: return ENOSYS
case 100084: return EOVERFLOW
case 100089: return ECANCELED
case 100090: return EIDRM
case 100091: return ENOMSG
case 100092: return EILSEQ
case 100094: return EBADMSG
case 100095: return EMULTIHOP
case 100096: return ENODATA
case 100097: return ENOLINK
case 100098: return ENOSR
case 100099: return ENOSTR
case 100100: return EPROTO
case 100101: return ETIME
case 100102: return EOPNOTSUPP
default: break
}
#endif

if ((OSStatusError.Codes.kPOSIXErrorBase + 1)..<(OSStatusError.Codes.kPOSIXErrorBase + 1000)).contains(osStatus) {
return osStatus - OSStatusError.Codes.kPOSIXErrorBase
}
Expand Down
10 changes: 9 additions & 1 deletion Tests/CSErrorsTests/NSErrorTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
// Created by Charles Srstka on 11/7/23.
//

#if Foundation && canImport(Darwin)
#if Foundation

import Foundation
import Testing
Expand All @@ -16,11 +16,15 @@ struct NSErrorTests {
func testFileNotFound() {
#expect(NSError(domain: NSCocoaErrorDomain, code: NSFileNoSuchFileError).isFileNotFoundError)
#expect(NSError(domain: NSCocoaErrorDomain, code: NSFileReadNoSuchFileError).isFileNotFoundError)
#if canImport(Darwin)
#expect(NSError(domain: NSCocoaErrorDomain, code: NSUbiquitousFileUnavailableError).isFileNotFoundError)
#endif
#expect(!NSError(domain: NSCocoaErrorDomain, code: NSFileWriteNoPermissionError).isFileNotFoundError)

#if canImport(Darwin)
#expect(NSError(domain: NSURLErrorDomain, code: NSURLErrorFileDoesNotExist).isFileNotFoundError)
#expect(!NSError(domain: NSURLErrorDomain, code: NSURLErrorNoPermissionsToReadFile).isFileNotFoundError)
#endif

#expect(NSError(domain: NSOSStatusErrorDomain, code: -43 /* fnfErr */).isFileNotFoundError)
#expect(!NSError(domain: NSOSStatusErrorDomain, code: -36 /* ioErr */).isFileNotFoundError)
Expand All @@ -41,8 +45,10 @@ struct NSErrorTests {
#expect(NSError(domain: NSCocoaErrorDomain, code: NSFileWriteNoPermissionError).isPermissionError)
#expect(!NSError(domain: NSCocoaErrorDomain, code: NSFileNoSuchFileError).isPermissionError)

#if canImport(Darwin)
#expect(NSError(domain: NSURLErrorDomain, code: NSURLErrorNoPermissionsToReadFile).isPermissionError)
#expect(!NSError(domain: NSURLErrorDomain, code: NSURLErrorUnsupportedURL).isPermissionError)
#endif

#expect(NSError(domain: NSOSStatusErrorDomain, code: -5000 /* afpAccessDenied */).isPermissionError)
#expect(!NSError(domain: NSOSStatusErrorDomain, code: -43 /* fnfErr */).isPermissionError)
Expand All @@ -63,9 +69,11 @@ struct NSErrorTests {
#expect(NSError(domain: NSCocoaErrorDomain, code: NSUserCancelledError).isCancelledError)
#expect(!NSError(domain: NSCocoaErrorDomain, code: NSFileNoSuchFileError).isCancelledError)

#if canImport(Darwin)
#expect(NSError(domain: NSURLErrorDomain, code: NSURLErrorCancelled).isCancelledError)
#expect(NSError(domain: NSURLErrorDomain, code: NSURLErrorUserCancelledAuthentication).isCancelledError)
#expect(!NSError(domain: NSURLErrorDomain, code: NSURLErrorTimedOut).isCancelledError)
#endif

#expect(NSError(domain: NSOSStatusErrorDomain, code: -128 /* userCanceledErr */).isCancelledError)
#expect(NSError(domain: NSOSStatusErrorDomain, code: -1711 /* errAEWaitCanceled */).isCancelledError)
Expand Down
Loading