krz/keycask

Password manager: Swift core library, CLI for macOS/Linux/Windows, iOS/macOS app. cli password-manager swift

Commit 48eb4d9dbf

48eb4d9dbf5408636b96f05510ffc13be8e84fdc

parent: fe4dc49efe

Verified · cmc

cmc <hello@cleberg.net> · 2026-09-17 15:03 UTC

Add KeycaskError with exit codes

Layout: unified · split

Sources/KeycaskCore/KeycaskCore.swift deleted −3
@@ -1,3 +0,0 @@
1public enum KeycaskCore {
2 public static let name = "keycask"
3}
Sources/KeycaskCore/KeycaskError.swift added +38
@@ -0,0 +1,38 @@
1public enum KeycaskError: Error, Equatable, Sendable {
2 case notFound(String)
3 case ambiguous(name: String, candidates: [String])
4 case cannotDecrypt
5 case corrupt(String)
6 case vaultExists(String)
7 case noVault(String)
8 case duplicateID(String)
9 case io(String)
10 case usage(String)
11 case failure(String)
12
13 public var exitCode: Int32 {
14 switch self {
15 case .failure, .io, .corrupt, .vaultExists, .duplicateID: 1
16 case .usage: 2
17 case .notFound, .noVault: 3
18 case .cannotDecrypt: 4
19 case .ambiguous: 5
20 }
21 }
22
23 public var message: String {
24 switch self {
25 case .notFound(let what): "\(what): not found"
26 case .ambiguous(let name, let candidates):
27 (["\(name): ambiguous, use an id:"] + candidates).joined(separator: "\n")
28 case .cannotDecrypt: "cannot decrypt: wrong passphrase or damaged vault"
29 case .corrupt(let why): "vault is corrupt: \(why)"
30 case .vaultExists(let path): "vault \(path) already exists"
31 case .noVault(let path): "vault \(path) not found (run `keycask init`)"
32 case .duplicateID(let id): "duplicate id \(id)"
33 case .io(let why): why
34 case .usage(let why): why
35 case .failure(let why): why
36 }
37 }
38}
Sources/keycask/main.swift +1 −1
@@ -1,3 +1,3 @@
11import KeycaskCore
22
3print(KeycaskCore.name)
3print("keycask")
Tests/KeycaskCoreTests/KeycaskErrorTests.swift added +28
@@ -0,0 +1,28 @@
1import Testing
2
3@testable import KeycaskCore
4
5@Suite struct KeycaskErrorTests {
6 @Test func exitCodesFollowTheSpec() {
7 #expect(KeycaskError.failure("x").exitCode == 1)
8 #expect(KeycaskError.io("x").exitCode == 1)
9 #expect(KeycaskError.corrupt("x").exitCode == 1)
10 #expect(KeycaskError.vaultExists("x").exitCode == 1)
11 #expect(KeycaskError.duplicateID("abcd2345").exitCode == 1)
12 #expect(KeycaskError.usage("x").exitCode == 2)
13 #expect(KeycaskError.notFound("x").exitCode == 3)
14 #expect(KeycaskError.noVault("/p").exitCode == 3)
15 #expect(KeycaskError.cannotDecrypt.exitCode == 4)
16 #expect(KeycaskError.ambiguous(name: "gh", candidates: []).exitCode == 5)
17 }
18
19 @Test func messagesNameTheSubject() {
20 #expect(KeycaskError.notFound("gh").message == "gh: not found")
21 #expect(KeycaskError.noVault("/v").message == "vault /v not found (run `keycask init`)")
22 #expect(KeycaskError.vaultExists("/v").message == "vault /v already exists")
23 #expect(
24 KeycaskError.cannotDecrypt.message
25 == "cannot decrypt: wrong passphrase or damaged vault")
26 #expect(KeycaskError.corrupt("bad json").message == "vault is corrupt: bad json")
27 }
28}
Tests/KeycaskCoreTests/SmokeTests.swift deleted −7
@@ -1,7 +0,0 @@
1import Testing
2
3@testable import KeycaskCore
4
5@Test func packageBuilds() {
6 #expect(KeycaskCore.name == "keycask")
7}