gitbay/Repos/RepoCreateViewModel.swift
55 lines · 1985 bytes
1import Foundation
2import Observation
3
4/// `repo create <owner/name> [--private]` and
5/// `repo import <owner/name> --from <url> [--private] [--token-stdin]`.
6@Observable
7@MainActor
8final class RepoCreateViewModel {
9
10 private(set) var working = false
11 private(set) var errorMessage: String?
12
13 private let client: GitbayClient
14
15 init(client: GitbayClient) {
16 self.client = client
17 }
18
19 nonisolated private struct Created: Decodable, Sendable {
20 let path: String
21 }
22
23 /// Returns the created repo's path, or nil with `errorMessage` set.
24 func create(path: String, isPrivate: Bool) async -> String? {
25 var argv = ["repo", "create", path.trimmingCharacters(in: .whitespaces)]
26 if isPrivate { argv.append("--private") }
27 return await run(argv, stdin: nil)
28 }
29
30 /// The token, when there is one, travels on stdin and never in the
31 /// URL: the server refuses credentials embedded in `--from`.
32 func importRepo(path: String, from: String, isPrivate: Bool, token: String) async -> String? {
33 var argv = ["repo", "import", path.trimmingCharacters(in: .whitespaces),
34 "--from", from.trimmingCharacters(in: .whitespaces)]
35 if isPrivate { argv.append("--private") }
36 let token = token.trimmingCharacters(in: .whitespacesAndNewlines)
37 if !token.isEmpty { argv.append("--token-stdin") }
38 return await run(argv, stdin: token.isEmpty ? nil : token + "\n")
39 }
40
41 private func run(_ argv: [String], stdin: String?) async -> String? {
42 working = true
43 errorMessage = nil
44 defer { working = false }
45 do {
46 let created = try await client.run(argv, stdin: stdin, as: Created.self)
47 return created?.path
48 } catch let error as GitbayError {
49 errorMessage = error.userFacingMessage
50 } catch {
51 errorMessage = GitbayError.transport(error).userFacingMessage
52 }
53 return nil
54 }
55}