gitbay/Snippets/SnippetDetailViewModel.swift

v1.8.0
gitbay-ios/gitbay/Snippets/SnippetDetailViewModel.swift history · blame · raw

85 lines · 2734 bytes

 1import Foundation
 2import Observation
 3
 4/// `snippet show <id>` with every file's content, plus the writes an
 5/// owner has: `snippet edit`, `snippet file set`, `snippet file remove`
 6/// and `snippet delete`.
 7@Observable
 8@MainActor
 9final class SnippetDetailViewModel {
10
11    private(set) var state: LoadState<Snippet> = .loading
12    private(set) var actionError: String?
13    private(set) var working = false
14
15    private let client: GitbayClient
16    let id: String
17
18    init(client: GitbayClient, id: String) {
19        self.client = client
20        self.id = id
21    }
22
23    func load() async {
24        do {
25            state = .loaded(try await client.read(["snippet", "show", id], as: Snippet.self))
26        } catch {
27            state = .from(error)
28        }
29    }
30
31    /// `snippet edit <id> [--description <d>] [--visibility <v>]`: a nil
32    /// field is left as it is. An empty description clears it.
33    func edit(description: String?, visibility: String?) async {
34        var argv = ["snippet", "edit", id]
35        if let description { argv += ["--description", description] }
36        if let visibility { argv += ["--visibility", visibility] }
37        await perform(argv)
38    }
39
40    /// `snippet file set <id> <filename> < file`: adds the file or
41    /// replaces its content.
42    func setFile(_ name: String, content: String) async {
43        await perform(["snippet", "file", "set", id, name.trimmingCharacters(in: .whitespaces)], stdin: content)
44    }
45
46    func removeFile(_ name: String) async {
47        await perform(["snippet", "file", "remove", id, name])
48    }
49
50    /// `snippet delete <id>`. There is nothing to reload afterwards; the
51    /// screen pops on true.
52    func delete() async -> Bool {
53        working = true
54        actionError = nil
55        defer { working = false }
56        do {
57            try await client.run(["snippet", "delete", id])
58            return true
59        } catch let error as GitbayError {
60            actionError = error.userFacingMessage
61            return false
62        } catch {
63            actionError = GitbayError.transport(error).userFacingMessage
64            return false
65        }
66    }
67
68    private func perform(_ argv: [String], stdin: String? = nil) async {
69        working = true
70        actionError = nil
71        defer { working = false }
72        do {
73            try await client.run(argv, stdin: stdin)
74            await load()
75        } catch let error as GitbayError {
76            if case .usage(let message) = error, !message.isEmpty {
77                actionError = message
78            } else {
79                actionError = error.userFacingMessage
80            }
81        } catch {
82            actionError = GitbayError.transport(error).userFacingMessage
83        }
84    }
85}