gitbay/Snippets/SnippetDetailViewModel.swift
85 lines · 2734 bytes
7 symbols in this file
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}