Skip to content

Commit badc95c

Browse files
committed
Introduce a GenericError struct + improve documentation & messages
1 parent 388cdf8 commit badc95c

File tree

9 files changed

+725
-36
lines changed

9 files changed

+725
-36
lines changed

Sources/ErrorKit/BuiltInErrors/DatabaseError.swift

Lines changed: 85 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,103 @@
11
import Foundation
22

33
/// Represents errors that occur during database operations.
4+
///
5+
/// # Examples of Use
6+
///
7+
/// ## Handling Database Connections
8+
/// ```swift
9+
/// struct DatabaseConnection {
10+
/// func connect() throws(DatabaseError) {
11+
/// guard let socket = openNetworkSocket() else {
12+
/// throw .connectionFailed
13+
/// }
14+
/// // Successful connection logic
15+
/// }
16+
/// }
17+
/// ```
18+
///
19+
/// ## Managing Record Operations
20+
/// ```swift
21+
/// struct UserRepository {
22+
/// func findUser(byId id: String) throws(DatabaseError) -> User {
23+
/// guard let user = database.findUser(id: id) else {
24+
/// throw .recordNotFound(entity: "User", identifier: id)
25+
/// }
26+
/// return user
27+
/// }
28+
///
29+
/// func updateUser(_ user: User) throws(DatabaseError) {
30+
/// guard hasValidPermissions(for: user) else {
31+
/// throw .operationFailed(context: "Updating user profile")
32+
/// }
33+
/// // Update logic
34+
/// }
35+
/// }
36+
/// ```
437
public enum DatabaseError: Throwable {
538
/// The database connection failed.
39+
///
40+
/// # Example
41+
/// ```swift
42+
/// struct AuthenticationService {
43+
/// func authenticate() throws(DatabaseError) {
44+
/// guard let connection = attemptDatabaseConnection() else {
45+
/// throw .connectionFailed
46+
/// }
47+
/// // Proceed with authentication
48+
/// }
49+
/// }
50+
/// ```
651
case connectionFailed
752

853
/// The database query failed to execute.
54+
///
55+
/// # Example
56+
/// ```swift
57+
/// struct AnalyticsRepository {
58+
/// func generateReport(for period: DateInterval) throws(DatabaseError) -> Report {
59+
/// guard period.duration <= maximumReportPeriod else {
60+
/// throw .operationFailed(context: "Generating analytics report")
61+
/// }
62+
/// // Report generation logic
63+
/// }
64+
/// }
65+
/// ```
966
/// - Parameters:
1067
/// - context: A description of the operation or entity being queried.
1168
case operationFailed(context: String)
1269

1370
/// A requested record was not found in the database.
71+
///
72+
/// # Example
73+
/// ```swift
74+
/// struct ProductInventory {
75+
/// func fetchProduct(sku: String) throws(DatabaseError) -> Product {
76+
/// guard let product = database.findProduct(bySKU: sku) else {
77+
/// throw .recordNotFound(entity: "Product", identifier: sku)
78+
/// }
79+
/// return product
80+
/// }
81+
/// }
82+
/// ```
1483
/// - Parameters:
1584
/// - entity: The name of the entity or record type.
1685
/// - identifier: A unique identifier for the missing entity.
1786
case recordNotFound(entity: String, identifier: String?)
1887

1988
/// Generic error message if the existing cases don't provide the required details.
89+
///
90+
/// # Example
91+
/// ```swift
92+
/// struct DataMigrationService {
93+
/// func migrate() throws(DatabaseError) {
94+
/// guard canPerformMigration() else {
95+
/// throw .generic(userFriendlyMessage: "Migration cannot be performed")
96+
/// }
97+
/// // Migration logic
98+
/// }
99+
/// }
100+
/// ```
20101
case generic(userFriendlyMessage: String)
21102

22103
/// A user-friendly error message suitable for display to end users.
@@ -25,20 +106,20 @@ public enum DatabaseError: Throwable {
25106
case .connectionFailed:
26107
return String(
27108
localized: "BuiltInErrors.DatabaseError.connectionFailed",
28-
defaultValue: "Failed to connect to the database. Please try again later.",
109+
defaultValue: "Unable to establish a connection to the database. Check your network settings and try again.",
29110
bundle: .module
30111
)
31112
case .operationFailed(let context):
32113
return String(
33114
localized: "BuiltInErrors.DatabaseError.operationFailed",
34-
defaultValue: "An error occurred while performing the operation: \(context). Please try again.",
115+
defaultValue: "The database operation for \(context) could not be completed. Please retry the action.",
35116
bundle: .module
36117
)
37118
case .recordNotFound(let entity, let identifier):
38-
let idMessage = identifier.map { " Identifier: \($0)." } ?? ""
119+
let idMessage = identifier.map { " with ID \($0)" } ?? ""
39120
return String(
40121
localized: "BuiltInErrors.DatabaseError.recordNotFound",
41-
defaultValue: "The \(entity) record could not be found.\(idMessage) Please check and try again.",
122+
defaultValue: "The \(entity) record\(idMessage) was not found in the database. Verify the details and try again.",
42123
bundle: .module
43124
)
44125
case .generic(let userFriendlyMessage):

Sources/ErrorKit/BuiltInErrors/FileError.swift

Lines changed: 84 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,98 @@
11
import Foundation
22

33
/// Represents errors that occur during file operations.
4+
///
5+
/// # Examples of Use
6+
///
7+
/// ## Handling File Retrieval
8+
/// ```swift
9+
/// struct DocumentManager {
10+
/// func loadDocument(named name: String) throws(FileError) -> Document {
11+
/// guard let fileURL = findFile(named: name) else {
12+
/// throw .fileNotFound(fileName: name)
13+
/// }
14+
/// // Document loading logic
15+
/// }
16+
/// }
17+
/// ```
18+
///
19+
/// ## Managing File Operations
20+
/// ```swift
21+
/// struct FileProcessor {
22+
/// func processFile(at path: String) throws(FileError) {
23+
/// guard canWrite(to: path) else {
24+
/// throw .writeFailed(fileName: path)
25+
/// }
26+
/// // File processing logic
27+
/// }
28+
///
29+
/// func readConfiguration() throws(FileError) -> Configuration {
30+
/// guard let data = attemptFileRead() else {
31+
/// throw .readFailed(fileName: "config.json")
32+
/// }
33+
/// // Configuration parsing logic
34+
/// }
35+
/// }
36+
/// ```
437
public enum FileError: Throwable {
538
/// The file could not be found.
39+
///
40+
/// # Example
41+
/// ```swift
42+
/// struct AssetManager {
43+
/// func loadImage(named name: String) throws(FileError) -> Image {
44+
/// guard let imagePath = searchForImage(name) else {
45+
/// throw .fileNotFound(fileName: name)
46+
/// }
47+
/// // Image loading logic
48+
/// }
49+
/// }
50+
/// ```
651
case fileNotFound(fileName: String)
752

853
/// There was an issue reading the file.
54+
///
55+
/// # Example
56+
/// ```swift
57+
/// struct LogReader {
58+
/// func readLatestLog() throws(FileError) -> String {
59+
/// guard let logContents = attemptFileRead() else {
60+
/// throw .readFailed(fileName: "application.log")
61+
/// }
62+
/// return logContents
63+
/// }
64+
/// }
65+
/// ```
966
case readFailed(fileName: String)
1067

1168
/// There was an issue writing to the file.
69+
///
70+
/// # Example
71+
/// ```swift
72+
/// struct DataBackup {
73+
/// func backup(data: Data) throws(FileError) {
74+
/// guard canWriteToBackupLocation() else {
75+
/// throw .writeFailed(fileName: "backup.dat")
76+
/// }
77+
/// // Backup writing logic
78+
/// }
79+
/// }
80+
/// ```
1281
case writeFailed(fileName: String)
1382

1483
/// Generic error message if the existing cases don't provide the required details.
84+
///
85+
/// # Example
86+
/// ```swift
87+
/// struct FileIntegrityChecker {
88+
/// func validateFile() throws(FileError) {
89+
/// guard passes(integrityCheck) else {
90+
/// throw .generic(userFriendlyMessage: "File integrity compromised")
91+
/// }
92+
/// // Validation logic
93+
/// }
94+
/// }
95+
/// ```
1596
case generic(userFriendlyMessage: String)
1697

1798
/// A user-friendly error message suitable for display to end users.
@@ -20,19 +101,19 @@ public enum FileError: Throwable {
20101
case .fileNotFound(let fileName):
21102
return String(
22103
localized: "BuiltInErrors.FileError.fileNotFound",
23-
defaultValue: "The file \(fileName) could not be found. Please check the file path.",
104+
defaultValue: "The file \(fileName) could not be located. Please verify the file path and try again.",
24105
bundle: .module
25106
)
26107
case .readFailed(let fileName):
27108
return String(
28109
localized: "BuiltInErrors.FileError.readError",
29-
defaultValue: "There was an issue reading the file \(fileName). Please try again.",
110+
defaultValue: "An error occurred while attempting to read the file \(fileName). Please check file permissions and try again.",
30111
bundle: .module
31112
)
32113
case .writeFailed(let fileName):
33114
return String(
34115
localized: "BuiltInErrors.FileError.writeError",
35-
defaultValue: "There was an issue writing to the file \(fileName). Please try again.",
116+
defaultValue: "Unable to write to the file \(fileName). Ensure you have the necessary permissions and try again.",
36117
bundle: .module
37118
)
38119
case .generic(let userFriendlyMessage):
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import Foundation
2+
3+
/// Represents a generic error with a custom user-friendly message.
4+
/// Use this when the built-in error types don't match your specific error case
5+
/// or when you need a simple way to throw custom error messages.
6+
///
7+
/// # Examples of Use
8+
///
9+
/// ## Custom Business Logic Validation
10+
/// ```swift
11+
/// struct BusinessRuleValidator {
12+
/// func validateComplexRule(data: BusinessData) throws(GenericError) {
13+
/// guard meetsCustomCriteria(data) else {
14+
/// throw GenericError(
15+
/// userFriendlyMessage: String(localized: "The business data doesn't meet required criteria")
16+
/// )
17+
/// }
18+
/// // Continue with business logic
19+
/// }
20+
/// }
21+
/// ```
22+
///
23+
/// ## Application-Specific Errors
24+
/// ```swift
25+
/// struct CustomProcessor {
26+
/// func processSpecialCase() throws(GenericError) {
27+
/// guard canHandleSpecialCase() else {
28+
/// throw GenericError(
29+
/// userFriendlyMessage: String(localized: "Unable to process this special case")
30+
/// )
31+
/// }
32+
/// // Special case handling
33+
/// }
34+
/// }
35+
/// ```
36+
public struct GenericError: Throwable {
37+
/// A user-friendly message describing the error.
38+
public let userFriendlyMessage: String
39+
40+
/// Creates a new generic error with a custom user-friendly message.
41+
///
42+
/// # Example
43+
/// ```swift
44+
/// struct CustomValidator {
45+
/// func validateSpecialRequirement() throws(GenericError) {
46+
/// guard meetsRequirement() else {
47+
/// throw GenericError(
48+
/// userFriendlyMessage: String(localized: "The requirement was not met. Please try again.")
49+
/// )
50+
/// }
51+
/// // Validation logic
52+
/// }
53+
/// }
54+
/// ```
55+
/// - Parameter userFriendlyMessage: A clear, actionable message that will be shown to the user.
56+
public init(userFriendlyMessage: String) {
57+
self.userFriendlyMessage = userFriendlyMessage
58+
}
59+
}

0 commit comments

Comments
 (0)