swift-argument-parser
swift-argument-parser copied to clipboard
Return codes for custom error types
I have a custom error type that I'm throwing from my command:
enum Error: LocalizedError {
case foo
...
var localizedDescription: String {
switch self {
case .foo: return "foo error"
...
}
}
}
These all surface with code 1 in the shell. I'd like at least some of them to report specific codes so I can more easily respond to them.
I've seen there's ExitCode
which I could throw instead but then I don't have a single Error
tracking all error states anymore and I'd need to make the error printing local to the throwing location because ExitCode does not do any error logging:
print("error bar")
throw ExitCode(2)
Currently I can just throw and tweak all the error messages in my Error
's localizedDescription
.
I tried making my Error
adopt RawRepresentable
in the hopes it might pick up the raw values from there but it didn't.
Is there another way to attach specific codes to custom error types? Would it be possible to pick up error codes from RawRepresentable
or some other error code protocol?
Hi there!
You can always conform to a CustomNSError
protocol.
enum MyError: Error, LocalizedError, CustomNSError {
case firstCase
case secondCase
var errorDescription: String? {
switch self {
case .firstCase:
return "Here is an error description for the first case"
case .secondCase:
return "Here is an error description for the second case"
}
}
var errorCode: Int {
switch self {
case .firstCase:
return 101
case .secondCase:
return 102
}
}
}
Just opened #243, since @SergeyPetrachkov's suggestion isn't actually supported yet.
Please do not (ab)use the errorCode
of any general-purpose Error type/protocol as the exit status -- it leads to issues like #269.
A better way to do this is to introduce a mixin Error protocol with an explicit exitCode
property requirement, and/or careful runtime validation to prevent exiting with a truncated status.
On reflection, FWIW, I don't think the exit status is the best place to differentiate between the myriad possible error cases -- the output sent to stderr already does a better job at that.
Whether the process failed due to POSIXError.ENOENT
or CocoaError.fileReadNoPermission
is in the vast majority of cases irrelevant to the parent process; and trying to map all these errors into a tiny range of possible integers is an impossible problem in the first place.
Authors of command line tools ought to be in complete control of the exit status of their program. A CustomExitStatus
protocol is one way to do this; but allowing them to set a custom closure (or implement a ParsableCommand method) that maps an Error value to an exit code might be even better in the long run. (After all, in most cases, tools would just want to be able to pick a particular value for all errors.)
One issue with the protocol approach is that library authors would likely start "helpfully" conforming their error types to CustomExitStatus
, inappropriately taking control out of the hand of the tool author.
Another issue is that we'd still need to come up with a way to customize the exit status for errors that don't implement CustomExitStatus
. Defaulting to, say, 1 (like the package does now) isn't going to work in cases like grep
where the tool needs to reserve the 1 exit code for non-erroneous failures.