├── .github
├── FUNDING.yml
└── workflows
│ ├── checks.yml
│ └── ci.yml
├── .gitignore
├── .swiftpm
└── xcode
│ └── package.xcworkspace
│ └── contents.xcworkspacedata
├── LICENSE
├── Package.swift
├── Package@swift-5.5.swift
├── README.md
├── Sources
└── LegibleError.swift
├── Tests
├── LegibleErrorTests
│ ├── ClassTests.swift
│ ├── Edgecases.swift
│ ├── EnumTests.swift
│ ├── StructTests.swift
│ ├── XCTestManifests.swift
│ └── etc.swift
└── LinuxMain.swift
└── tea.yaml
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | github: mxcl
2 |
--------------------------------------------------------------------------------
/.github/workflows/checks.yml:
--------------------------------------------------------------------------------
1 | on:
2 | push:
3 | branches:
4 | - master
5 | jobs:
6 | macOS:
7 | runs-on: ubuntu-latest
8 | steps:
9 | - uses: actions/checkout@v2
10 | - run: swift --version
11 | - run: swift test --parallel
12 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | on:
2 | pull_request:
3 | paths:
4 | - Sources/**
5 | - Tests/**
6 | - .github/workflows/ci.yml
7 | schedule:
8 | - cron: '3 3 * * 2' # 3:03 AM, every Tuesday
9 | jobs:
10 | macOS:
11 | runs-on: ${{ matrix.os }}
12 | strategy:
13 | matrix:
14 | swift:
15 | - ~5.0
16 | - ~5.1
17 | - ~5.2
18 | - ~5.3
19 | os:
20 | - macos-10.15
21 | include:
22 | - swift: 5.4
23 | os: macos-11
24 | - swift: 5.5
25 | os: macos-11
26 | steps:
27 | - uses: actions/checkout@v2
28 | - uses: mxcl/xcodebuild@v1
29 | with:
30 | swift: ${{ matrix.swift }}
31 | warnings-as-errors: true
32 | - run: |
33 | swift test --generate-linuxmain
34 | git diff --exit-code
35 | if: ${{ matrix.swift == '5.3' }}
36 | linux:
37 | runs-on: ubuntu-latest
38 | strategy:
39 | matrix:
40 | swift:
41 | - swift:4.2
42 | - swift:5.0
43 | - swift:5.1
44 | - swift:5.2
45 | - swift:5.3
46 | - swift:5.4
47 | - swiftlang/swift:nightly-5.5
48 | container:
49 | image: ${{ matrix.swift }}
50 | steps:
51 | - uses: actions/checkout@v2
52 | - run: swift test --parallel -Xswiftc -warnings-as-errors
53 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /*.xcodeproj
2 | /.build
3 |
--------------------------------------------------------------------------------
/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Unlicense (Public Domain)
2 | ============================
3 |
4 | This is free and unencumbered software released into the public domain.
5 |
6 | Anyone is free to copy, modify, publish, use, compile, sell, or
7 | distribute this software, either in source code form or as a compiled
8 | binary, for any purpose, commercial or non-commercial, and by any
9 | means.
10 |
11 | In jurisdictions that recognize copyright laws, the author or authors
12 | of this software dedicate any and all copyright interest in the
13 | software to the public domain. We make this dedication for the benefit
14 | of the public at large and to the detriment of our heirs and
15 | successors. We intend this dedication to be an overt act of
16 | relinquishment in perpetuity of all present and future rights to this
17 | software under copyright law.
18 |
19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
22 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
23 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
24 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
25 | OTHER DEALINGS IN THE SOFTWARE.
26 |
27 | For more information, please refer to <>
28 |
--------------------------------------------------------------------------------
/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version:4.2
2 | import PackageDescription
3 |
4 | let name = "LegibleError"
5 |
6 | let package = Package(
7 | name: name,
8 | products: [
9 | .library(name: name, targets: [name]),
10 | ],
11 | targets: [
12 | .target(name: name, path: "Sources"),
13 | .testTarget(name: "LegibleErrorTests", dependencies: [.init(stringLiteral: name)]),
14 | ],
15 | swiftLanguageVersions: [.v4, .v4_2, .version("5")]
16 | )
17 |
--------------------------------------------------------------------------------
/Package@swift-5.5.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version:5.0
2 | import PackageDescription
3 |
4 | let name = "LegibleError"
5 |
6 | let package = Package(
7 | name: name,
8 | products: [
9 | .library(name: name, targets: [name]),
10 | ],
11 | targets: [
12 | .target(name: name, path: "Sources"),
13 | .testTarget(name: "LegibleErrorTests", dependencies: [.init(stringLiteral: name)]),
14 | ]
15 | )
16 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## LegibleError ![badge-platforms][] ![badge-languages][] [![badge-ci][]][gha] [![badge-codecov][]][codecov] [![badge-version][]][cocoapods]
2 |
3 | LegibleError’s goal is to prevent you showing the user a string like this:
4 |
5 | > The operation couldn’t be completed. (ThirdPartyModule.(unknown context at 0xx10d6b4a44).SomeError error 0.)
6 |
7 | That string is the default `localizedDescription` for a Swift `Error`. Instead
8 | use LegibleError and you’ll get something more like this:
9 |
10 | > The operation couldn’t be completed. (ThirdPartyModule.SomeError.networkFailure(http: 503))
11 |
12 | ## `Error.legibleLocalizedDescription`
13 |
14 | If you have an `Error` like this:
15 |
16 | ```swift
17 | enum SystemError: Error {
18 | case databaseFailure(internalCode: Int)
19 | }
20 |
21 | let error = SystemError.databaseFailure
22 | // ^^ obviously you’d get this from a callback or `catch` in the real-world
23 |
24 | let alert = UIAlertController(…)
25 | alert.message = error.localizedDescription
26 | present(alert)
27 | ```
28 |
29 | The alert will show:
30 |
31 | > The operation couldn’t be completed. (MyModule.(unknown context at 0xx10d6b4a44).SystemError error 0.)
32 |
33 | But if we were to use `.legibleLocalizedDescription`:
34 |
35 | ```swift
36 | import LegibleError
37 |
38 | let alert = UIAlertController(…)
39 | alert.message = error.legibleLocalizedDescription
40 | present(alert)
41 | ```
42 |
43 | The alert will show:
44 |
45 | > The operation couldn’t be completed. (SystemError.databaseFailure(internalCode: 34))
46 |
47 | Still not great, but way more useful in a bug report.
48 |
49 | If you want a great message, implement `LocalizedError` this will make both
50 | `localizedDescription` **and** `legibleLocalizedDescription` return the string
51 | you specify:
52 |
53 | ```swift
54 | enum SystemError: LocalizedError {
55 | case databaseFailure
56 |
57 | var errorDescription: String? {
58 | switch self {
59 | case databaseFailure(let code):
60 | return "A serious database failure occurred. Contact support. (#\(code))"
61 | }
62 | }
63 | }
64 | ```
65 |
66 | The alert will show:
67 |
68 | > A serious database failure occurred. Contact support. (#34)
69 |
70 | ---
71 |
72 | LegibleError exists because:
73 |
74 | 1. You have no control over third parties and cannot force them to implement
75 | `LocalizedError`
76 | 2. Some Errors in your codebase are very unlikely and thus “localizing” them is
77 | not a good maintenance burden.
78 | 3. When logging errors you want the full information without any risk that the
79 | localized version has “fallen behind”, get the compiler to do the work, in
80 | such cases use `legibleDescription` (see the next section).
81 |
82 | ## Loggable Error Descriptions
83 |
84 | This:
85 |
86 | ```swift
87 | let msg = "There was an error (\(error))"
88 | ```
89 |
90 | Will give you this:
91 |
92 | > There was an error (databaseFailure)
93 |
94 | Which loses the context of the enum’s type; use `legibleDescription`:
95 |
96 | ```swift
97 | let msg = "There was an error! \(error.legibleDescription)"
98 | ```
99 |
100 | > There was an error (SystemError.databaseFailure(internalCode: 34))
101 |
102 | `legibleDescription` is to `description` where `legibleLocalizedDescription` is
103 | to `localizedDescription`. `legibleDescription` is always appropriate for
104 | communicating to *you*, the developer, which error happened. Use it in logs and
105 | to supplement a good message for the user.
106 |
107 | ## Way better descriptions on Linux
108 |
109 | Linux is a little behind, usually you only get `The operation could not be
110 | completed` on Linux. We fully support Linux.
111 |
112 | # Sponsorship
113 |
114 | If you or your company depend on this project, please consider [sponsorship] so
115 | I have justification for maintenance .
116 |
117 | ## Installation
118 |
119 | SwiftPM:
120 |
121 | ```swift
122 | package.append(.package(url: "https://github.com/mxcl/LegibleError.git", from: "1.0.0"))
123 | ```
124 |
125 | CocoaPods:
126 |
127 | ```ruby
128 | pod 'LegibleError', '~> 1.0'
129 | ```
130 |
131 | Carthage:
132 |
133 | > Waiting on: [@Carthage#1945].
134 |
135 |
136 | [badge-platforms]: https://img.shields.io/badge/platforms-macOS%20%7C%20Linux%20%7C%20iOS%20%7C%20tvOS%20%7C%20watchOS-lightgrey.svg
137 | [badge-languages]: https://img.shields.io/badge/swift-4.2%20%7C%205.x-orange.svg
138 | [badge-codecov]: https://codecov.io/gh/mxcl/LegibleError/branch/master/graph/badge.svg
139 | [badge-version]: https://img.shields.io/cocoapods/v/LegibleError.svg?label=version
140 | [badge-ci]: https://github.com/mxcl/LegibleError/workflows/Checks/badge.svg
141 |
142 | [gha]: https://github.com/mxcl/LegibleError/actions
143 | [codecov]: https://codecov.io/gh/mxcl/LegibleError
144 | [cocoapods]: https://cocoapods.org/pods/LegibleError
145 |
146 | [`LocalizedError`]: https://developer.apple.com/documentation/foundation/localizederror
147 | [@Carthage#1945]: https://github.com/Carthage/Carthage/pull/1945
148 | [sponsorship]: https://github.com/sponsors/mxcl
149 |
--------------------------------------------------------------------------------
/Sources/LegibleError.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | #if os(Linux)
4 | #if swift(>=5.1)
5 | let theOperationCouldNotBeCompleted = "The operation could not be completed."
6 | #else
7 | let theOperationCouldNotBeCompleted = "The operation could not be completed"
8 | #endif
9 | #else
10 | let theOperationCouldNotBeCompleted = "The operation couldn’t be completed."
11 | #endif
12 |
13 | extension Error {
14 | /// - Returns: A fully qualified representation of this error.
15 | public var legibleDescription: String {
16 | switch errorType {
17 | case .swiftError(.enum?), .swiftLocalizedError(_, .enum?):
18 | return "\(type(of: self)).\(self)"
19 | case .swiftError(.class?), .swiftLocalizedError(_, .class?):
20 | //TODO better
21 | return "\(type(of: self))"
22 | case .swiftError, .swiftLocalizedError:
23 | return String(describing: self)
24 | case let .nsError(nsError, domain, code):
25 | if let underlyingError = nsError.userInfo[NSUnderlyingErrorKey] as? NSError {
26 | return "\(domain)(\(code), \(underlyingError.domain)(\(underlyingError.code)))"
27 | } else {
28 | return "\(domain)(\(code))"
29 | }
30 | }
31 | }
32 |
33 | /// - Returns: A fully qualified, user-visible representation of this error.
34 | public var legibleLocalizedDescription: String {
35 | switch errorType {
36 | case .swiftError:
37 | return "\(theOperationCouldNotBeCompleted) (\(legibleDescription))"
38 | case .swiftLocalizedError(let msg, _):
39 | return msg
40 | case .nsError(_, "kCLErrorDomain", 0):
41 | return "The location could not be determined."
42 | // ^^ Apple don’t provide a localized description for this
43 | case let .nsError(nsError, domain, code):
44 | if !localizedDescription.hasPrefix(theOperationCouldNotBeCompleted) {
45 | return localizedDescription
46 | //FIXME ^^ for non-EN
47 | } else if let underlyingError = nsError.userInfo[NSUnderlyingErrorKey] as? Error {
48 | return underlyingError.legibleLocalizedDescription
49 | } else {
50 | // usually better than the localizedDescription, but not pretty
51 | return "\(theOperationCouldNotBeCompleted) (\(domain).\(code))"
52 | }
53 | }
54 | }
55 |
56 | private var errorType: ErrorType {
57 | #if os(Linux)
58 | // without the `as Any` noop this generates an (incorrect) warning
59 | let isNSError = self as Any is NSError
60 | #else
61 | let foo: Any = self
62 | let nativeClassNames = ["_SwiftNativeNSError", "__SwiftNativeNSError"]
63 | let selfClassName = String(cString: object_getClassName(self))
64 | let isNSError = !nativeClassNames.contains(selfClassName) && foo is NSObject
65 | // ^^ ∵ otherwise implicit bridging implicitly casts as for other tests
66 | #endif
67 |
68 | if isNSError {
69 | let nserr = self as NSError
70 | return .nsError(nserr, domain: nserr.domain, code: nserr.code)
71 | } else if let err = self as? LocalizedError, let msg = err.errorDescription {
72 | return .swiftLocalizedError(msg, Mirror(reflecting: self).displayStyle)
73 | } else {
74 | return .swiftError(Mirror(reflecting: self).displayStyle)
75 | }
76 | }
77 | }
78 |
79 | private enum ErrorType {
80 | case nsError(NSError, domain: String, code: Int)
81 | case swiftLocalizedError(String, Mirror.DisplayStyle?)
82 | case swiftError(Mirror.DisplayStyle?)
83 | }
84 |
--------------------------------------------------------------------------------
/Tests/LegibleErrorTests/ClassTests.swift:
--------------------------------------------------------------------------------
1 | @testable import LegibleError
2 | import XCTest
3 |
4 | public class PublicFoo: Error {
5 | let a = "a"
6 | }
7 |
8 | class InternalFoo: Error {
9 | let a = "a"
10 | }
11 |
12 | class PrivateFoo: Error {
13 | let a = "a"
14 | }
15 |
16 | class ClassTests: XCTestCase {
17 | func test_local_Swift_class() {
18 |
19 | //FIXME!
20 |
21 | class Foo: Error {
22 | let a = "a"
23 | }
24 |
25 | let foo = Foo()
26 |
27 | #if os(macOS) && swift(>=5.0)
28 | XCTAssertMatches(
29 | foo.legibleDescription,
30 | "^LegibleErrorTests\\.ClassTests\\.\\(unknown context at \\$.+\\)\\.\\(unknown context at \\$.+\\)\\.Foo\\(1\\)$")
31 | XCTAssertMatches(
32 | foo.legibleLocalizedDescription,
33 | "^\(theOperationCouldNotBeCompleted) \\(LegibleErrorTests\\.ClassTests\\.\\(unknown context at \\$.+\\)\\.\\(unknown context at \\$.+\\)\\.Foo\\.1\\)$")
34 | #else
35 | XCTAssertEqual(foo.legibleDescription, "Foo")
36 | XCTAssertEqual(foo.legibleLocalizedDescription, "\(theOperationCouldNotBeCompleted) (Foo)")
37 | #endif
38 | }
39 |
40 | func test_public_Swift_class() {
41 | let foo = PublicFoo()
42 |
43 | #if os(macOS)
44 | #if swift(>=5.0)
45 | XCTAssertEqual(foo.legibleDescription, "LegibleErrorTests.PublicFoo(1)")
46 | XCTAssertEqual(foo.legibleLocalizedDescription, "\(theOperationCouldNotBeCompleted) (LegibleErrorTests.PublicFoo.1)")
47 | #else
48 | XCTAssertEqual(foo.legibleDescription, "PublicFoo")
49 | XCTAssertEqual(foo.legibleLocalizedDescription, "\(theOperationCouldNotBeCompleted) (PublicFoo)")
50 | #endif
51 | #else
52 | //FIXME
53 | XCTAssertEqual(foo.legibleDescription, "PublicFoo")
54 | XCTAssertEqual(foo.legibleLocalizedDescription, "\(theOperationCouldNotBeCompleted) (PublicFoo)")
55 | #endif
56 | }
57 |
58 | func test_internal_Swift_class() {
59 | let foo = InternalFoo()
60 |
61 | #if os(macOS)
62 | #if swift(>=5.0)
63 | XCTAssertEqual(foo.legibleDescription, "LegibleErrorTests.InternalFoo(1)")
64 | XCTAssertEqual(foo.legibleLocalizedDescription, "\(theOperationCouldNotBeCompleted) (LegibleErrorTests.InternalFoo.1)")
65 | #else
66 | XCTAssertEqual(foo.legibleDescription, "InternalFoo")
67 | XCTAssertEqual(foo.legibleLocalizedDescription, "\(theOperationCouldNotBeCompleted) (InternalFoo)")
68 | #endif
69 | #else
70 | XCTAssertEqual(foo.legibleDescription, "InternalFoo")
71 | XCTAssertEqual(foo.legibleLocalizedDescription, "\(theOperationCouldNotBeCompleted) (InternalFoo)")
72 | #endif
73 | }
74 |
75 | func test_private_Swift_class() {
76 | let foo = PrivateFoo()
77 |
78 | #if os(macOS)
79 | #if swift(>=5.0)
80 | XCTAssertEqual(foo.legibleDescription, "LegibleErrorTests.PrivateFoo(1)")
81 | XCTAssertEqual(foo.legibleLocalizedDescription, "\(theOperationCouldNotBeCompleted) (LegibleErrorTests.PrivateFoo.1)")
82 | #else
83 | XCTAssertEqual(foo.legibleDescription, "PrivateFoo")
84 | XCTAssertEqual(foo.legibleLocalizedDescription, "\(theOperationCouldNotBeCompleted) (PrivateFoo)")
85 | #endif
86 | #else
87 | XCTAssertEqual(foo.legibleDescription, "PrivateFoo")
88 | XCTAssertEqual(foo.legibleLocalizedDescription, "\(theOperationCouldNotBeCompleted) (PrivateFoo)")
89 | #endif
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/Tests/LegibleErrorTests/Edgecases.swift:
--------------------------------------------------------------------------------
1 | @testable import LegibleError
2 | import XCTest
3 |
4 | class Edgcases: XCTestCase {
5 | func test_vanilla_NSError() {
6 | let Foo = { NSError(domain: "a", code: 1) }
7 | XCTAssertEqual(Foo().legibleDescription, "a(1)")
8 | XCTAssertEqual(Foo().legibleLocalizedDescription, "\(theOperationCouldNotBeCompleted) (a.1)")
9 | #if !os(Linux) || swift(>=5.1)
10 | XCTAssertEqual(Foo().localizedDescription, "\(theOperationCouldNotBeCompleted) (a error 1.)")
11 | #else
12 | XCTAssertEqual(Foo().localizedDescription, theOperationCouldNotBeCompleted)
13 | #endif
14 | }
15 |
16 | func test_derived_NSError() {
17 | class Foo: NSError {
18 | convenience init() {
19 | self.init(domain: "a", code: 1)
20 | }
21 | }
22 |
23 | XCTAssertEqual(Foo().legibleDescription, "a(1)")
24 | XCTAssertEqual(Foo().legibleLocalizedDescription, "\(theOperationCouldNotBeCompleted) (a.1)")
25 | #if !os(Linux) || swift(>=5.1)
26 | XCTAssertEqual(Foo().localizedDescription, "\(theOperationCouldNotBeCompleted) (a error 1.)")
27 | #else
28 | XCTAssertEqual(Foo().localizedDescription, theOperationCouldNotBeCompleted)
29 | #endif
30 | }
31 |
32 | func test_derived_annotated_NSError() {
33 | class Foo: NSError {
34 | convenience init() {
35 | self.init(domain: "a", code: 1, userInfo: [
36 | NSLocalizedDescriptionKey: "Foobar"
37 | ])
38 | }
39 | }
40 |
41 | XCTAssertEqual(Foo().legibleDescription, "a(1)")
42 | XCTAssertEqual(Foo().legibleLocalizedDescription, "Foobar")
43 | XCTAssertEqual(Foo().localizedDescription, "Foobar")
44 | }
45 |
46 | func test_derived_underlying_NSError() {
47 | class Foo: NSError {
48 | convenience init() {
49 | self.init(domain: "a", code: 1, userInfo: [
50 | NSUnderlyingErrorKey: Foo(domain: "b", code: 2, userInfo: [
51 | NSLocalizedDescriptionKey: "Foobar"
52 | ])
53 | ])
54 | }
55 | }
56 |
57 | XCTAssertEqual(Foo().legibleDescription, "a(1, b(2))")
58 | XCTAssertEqual(Foo().legibleLocalizedDescription, "Foobar")
59 | #if !os(Linux) || swift(>=5.1)
60 | XCTAssertEqual(Foo().localizedDescription, "\(theOperationCouldNotBeCompleted) (a error 1.)")
61 | #else
62 | XCTAssertEqual(Foo().localizedDescription, theOperationCouldNotBeCompleted)
63 | #endif
64 | }
65 |
66 | func test_CocoaError() {
67 | let err = CocoaError.error(.coderInvalidValue)
68 | let msg = "The data couldn’t be written because it isn’t in the correct format."
69 |
70 | XCTAssertEqual(err.legibleDescription, "NSCocoaErrorDomain(4866)")
71 | #if !os(Linux)
72 | XCTAssertEqual(err.legibleLocalizedDescription, msg)
73 | XCTAssertEqual(err.localizedDescription, msg)
74 | #elseif swift(>=5.1)
75 | XCTAssertEqual(err.legibleLocalizedDescription, "\(theOperationCouldNotBeCompleted) (NSCocoaErrorDomain.4866)")
76 | XCTAssertEqual(err.localizedDescription, "\(theOperationCouldNotBeCompleted) The data isn’t in the correct format.")
77 | #else
78 | XCTAssertEqual(err.legibleLocalizedDescription, "\(theOperationCouldNotBeCompleted) (NSCocoaErrorDomain.4866)")
79 | XCTAssertEqual(err.localizedDescription, theOperationCouldNotBeCompleted)
80 | #endif
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/Tests/LegibleErrorTests/EnumTests.swift:
--------------------------------------------------------------------------------
1 | @testable import LegibleError
2 | import XCTest
3 |
4 | public enum PublicEnum: Error {
5 | case a
6 | }
7 |
8 | enum InternalEnum: Error {
9 | case a
10 | }
11 |
12 | private enum PrivateEnum: Error {
13 | case a
14 | }
15 |
16 | class EnumTests: XCTestCase {
17 | func test_public_Swift_enum() {
18 | XCTAssertEqual(PublicEnum.a.legibleDescription, "PublicEnum.a")
19 | XCTAssertEqual(PublicEnum.a.legibleLocalizedDescription, "\(theOperationCouldNotBeCompleted) (PublicEnum.a)")
20 | }
21 |
22 | func test_internal_Swift_enum() {
23 | XCTAssertEqual(InternalEnum.a.legibleDescription, "InternalEnum.a")
24 | XCTAssertEqual(InternalEnum.a.legibleLocalizedDescription, "\(theOperationCouldNotBeCompleted) (InternalEnum.a)")
25 | }
26 |
27 | func test_private_Swift_enum() {
28 | XCTAssertEqual(PrivateEnum.a.legibleDescription, "PrivateEnum.a")
29 | XCTAssertEqual(PrivateEnum.a.legibleLocalizedDescription, "\(theOperationCouldNotBeCompleted) (PrivateEnum.a)")
30 | }
31 |
32 | func test_local_Swift_enum() {
33 | enum Enum: Error {
34 | case a
35 | }
36 | XCTAssertEqual(Enum.a.legibleDescription, "Enum.a")
37 | XCTAssertEqual(Enum.a.legibleLocalizedDescription, "\(theOperationCouldNotBeCompleted) (Enum.a)")
38 | }
39 |
40 | func test_Swift_LocalizedError_enum_without_errorDescription() {
41 | enum Foo: LocalizedError {
42 | case a
43 | }
44 |
45 | XCTAssertEqual(Foo.a.legibleDescription, "Foo.a")
46 | XCTAssertEqual(Foo.a.legibleLocalizedDescription, "\(theOperationCouldNotBeCompleted) (Foo.a)")
47 | }
48 |
49 | func test_Swift_LocalizedError_enum_with_errorDescription() {
50 | enum Foo: LocalizedError {
51 | case a
52 |
53 | var errorDescription: String? {
54 | return "Foobar"
55 | }
56 | }
57 |
58 | XCTAssertEqual(Foo.a.legibleLocalizedDescription, "Foobar")
59 | XCTAssertEqual(Foo.a.legibleDescription, "Foo.a")
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/Tests/LegibleErrorTests/StructTests.swift:
--------------------------------------------------------------------------------
1 | @testable import LegibleError
2 | import XCTest
3 |
4 | public struct PublicStruct: Error {
5 | let a = "a"
6 | }
7 |
8 | struct InternalStruct: Error {
9 | let a = "a"
10 | }
11 |
12 | private struct PrivateStruct: Error {
13 | let a = "a"
14 | }
15 |
16 |
17 | class StructTest: XCTestCase {
18 | func test_public_Swift_struct() {
19 | XCTAssertEqual(PublicStruct().legibleDescription, "PublicStruct(a: \"a\")")
20 | XCTAssertEqual(PublicStruct().legibleLocalizedDescription, "\(theOperationCouldNotBeCompleted) (PublicStruct(a: \"a\"))")
21 | }
22 |
23 | func test_internal_Swift_struct() {
24 | XCTAssertEqual(InternalStruct().legibleDescription, "InternalStruct(a: \"a\")")
25 | XCTAssertEqual(InternalStruct().legibleLocalizedDescription, "\(theOperationCouldNotBeCompleted) (InternalStruct(a: \"a\"))")
26 | }
27 |
28 | func test_private_Swift_enum() {
29 | XCTAssertEqual(PrivateStruct().legibleDescription, "PrivateStruct(a: \"a\")")
30 | XCTAssertEqual(PrivateStruct().legibleLocalizedDescription, "\(theOperationCouldNotBeCompleted) (PrivateStruct(a: \"a\"))")
31 | }
32 |
33 | func test_local_Swift_struct() {
34 | struct Struct: Error {
35 | let a = "a"
36 | }
37 |
38 | XCTAssertEqual(Struct().legibleDescription, "Struct(a: \"a\")")
39 | XCTAssertEqual(Struct().legibleLocalizedDescription, "\(theOperationCouldNotBeCompleted) (Struct(a: \"a\"))")
40 | }
41 |
42 | func test_Swift_LocalizedError_struct_without_errorDescription() {
43 | struct Foo: LocalizedError {
44 | let a = "a"
45 | }
46 |
47 | XCTAssertEqual(Foo().legibleDescription, "Foo(a: \"a\")")
48 | XCTAssertEqual(Foo().legibleLocalizedDescription, "\(theOperationCouldNotBeCompleted) (Foo(a: \"a\"))")
49 | }
50 |
51 | func test_Swift_LocalizedError_struct_with_errorDescription() {
52 | struct Foo: LocalizedError {
53 | let a = "a"
54 |
55 | var errorDescription: String? {
56 | return "Foobar"
57 | }
58 | }
59 |
60 | XCTAssertEqual(Foo().legibleDescription, "Foo(a: \"a\")")
61 | XCTAssertEqual(Foo().legibleLocalizedDescription, "Foobar")
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/Tests/LegibleErrorTests/XCTestManifests.swift:
--------------------------------------------------------------------------------
1 | #if !canImport(ObjectiveC)
2 | import XCTest
3 |
4 | extension ClassTests {
5 | // DO NOT MODIFY: This is autogenerated, use:
6 | // `swift test --generate-linuxmain`
7 | // to regenerate.
8 | static let __allTests__ClassTests = [
9 | ("test_internal_Swift_class", test_internal_Swift_class),
10 | ("test_local_Swift_class", test_local_Swift_class),
11 | ("test_private_Swift_class", test_private_Swift_class),
12 | ("test_public_Swift_class", test_public_Swift_class),
13 | ]
14 | }
15 |
16 | extension Edgcases {
17 | // DO NOT MODIFY: This is autogenerated, use:
18 | // `swift test --generate-linuxmain`
19 | // to regenerate.
20 | static let __allTests__Edgcases = [
21 | ("test_CocoaError", test_CocoaError),
22 | ("test_derived_annotated_NSError", test_derived_annotated_NSError),
23 | ("test_derived_NSError", test_derived_NSError),
24 | ("test_derived_underlying_NSError", test_derived_underlying_NSError),
25 | ("test_vanilla_NSError", test_vanilla_NSError),
26 | ]
27 | }
28 |
29 | extension EnumTests {
30 | // DO NOT MODIFY: This is autogenerated, use:
31 | // `swift test --generate-linuxmain`
32 | // to regenerate.
33 | static let __allTests__EnumTests = [
34 | ("test_internal_Swift_enum", test_internal_Swift_enum),
35 | ("test_local_Swift_enum", test_local_Swift_enum),
36 | ("test_private_Swift_enum", test_private_Swift_enum),
37 | ("test_public_Swift_enum", test_public_Swift_enum),
38 | ("test_Swift_LocalizedError_enum_with_errorDescription", test_Swift_LocalizedError_enum_with_errorDescription),
39 | ("test_Swift_LocalizedError_enum_without_errorDescription", test_Swift_LocalizedError_enum_without_errorDescription),
40 | ]
41 | }
42 |
43 | extension StructTest {
44 | // DO NOT MODIFY: This is autogenerated, use:
45 | // `swift test --generate-linuxmain`
46 | // to regenerate.
47 | static let __allTests__StructTest = [
48 | ("test_internal_Swift_struct", test_internal_Swift_struct),
49 | ("test_local_Swift_struct", test_local_Swift_struct),
50 | ("test_private_Swift_enum", test_private_Swift_enum),
51 | ("test_public_Swift_struct", test_public_Swift_struct),
52 | ("test_Swift_LocalizedError_struct_with_errorDescription", test_Swift_LocalizedError_struct_with_errorDescription),
53 | ("test_Swift_LocalizedError_struct_without_errorDescription", test_Swift_LocalizedError_struct_without_errorDescription),
54 | ]
55 | }
56 |
57 | public func __allTests() -> [XCTestCaseEntry] {
58 | return [
59 | testCase(ClassTests.__allTests__ClassTests),
60 | testCase(Edgcases.__allTests__Edgcases),
61 | testCase(EnumTests.__allTests__EnumTests),
62 | testCase(StructTest.__allTests__StructTest),
63 | ]
64 | }
65 | #endif
66 |
--------------------------------------------------------------------------------
/Tests/LegibleErrorTests/etc.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | import XCTest
3 |
4 | func XCTAssertMatches(_ input: String, _ pattern: String, file: StaticString = #file, line: UInt = #line) {
5 | do {
6 | let rx = try NSRegularExpression(pattern: pattern)
7 | let range = NSRange(location: 0, length: input.utf16.count)
8 | if rx.firstMatch(in: input, range: range) == nil {
9 | XCTFail("\(input) did not match the provided pattern", file: file, line: line)
10 | }
11 | } catch {
12 | XCTFail("Pattern failed to compile", file: file, line: line)
13 | }
14 | }
15 |
16 | #if !os(macOS) && !os(Linux)
17 | import XCTest
18 |
19 | // SwiftPM generates code that is improperly escaped thus we require this to
20 | // compile on iOS & tvOS.
21 | public typealias XCTestCaseEntry = (testCaseClass: XCTestCase.Type, allTests: [(String, (XCTestCase) throws -> Void)])
22 |
23 | public func testCase(_ allTests: [(String, (T) -> () throws -> Void)]) -> XCTestCaseEntry {
24 | fatalError()
25 | }
26 |
27 | public func testCase(_ allTests: [(String, (T) -> () -> Void)]) -> XCTestCaseEntry {
28 | fatalError()
29 | }
30 | #endif
31 |
32 | #if swift(>=5)
33 | let addressPattern = "0x.?+"
34 | #else
35 | let addressPattern = "\\$.?+"
36 | #endif
37 |
--------------------------------------------------------------------------------
/Tests/LinuxMain.swift:
--------------------------------------------------------------------------------
1 | import XCTest
2 |
3 | import LegibleErrorTests
4 |
5 | var tests = [XCTestCaseEntry]()
6 | tests += LegibleErrorTests.__allTests()
7 |
8 | XCTMain(tests)
9 |
--------------------------------------------------------------------------------
/tea.yaml:
--------------------------------------------------------------------------------
1 | # https://tea.xyz/what-is-this-file
2 | # created with https://mash.pkgx.sh/mxcl/tea-register
3 | ---
4 | version: 1.0.0
5 | codeOwners:
6 | - '0x5E2DE4A68df811AAAD32d71fb065e6946fA5C8d9' # mxcl
7 | quorum: 1
8 |
--------------------------------------------------------------------------------