import Foundation import Observation /// `repo create [--private]` and /// `repo import --from [--private] [--token-stdin]`. @Observable @MainActor final class RepoCreateViewModel { private(set) var working = false private(set) var errorMessage: String? private let client: GitbayClient init(client: GitbayClient) { self.client = client } nonisolated private struct Created: Decodable, Sendable { let path: String } /// Returns the created repo's path, or nil with `errorMessage` set. func create(path: String, isPrivate: Bool) async -> String? { var argv = ["repo", "create", path.trimmingCharacters(in: .whitespaces)] if isPrivate { argv.append("--private") } return await run(argv, stdin: nil) } /// The token, when there is one, travels on stdin and never in the /// URL: the server refuses credentials embedded in `--from`. func importRepo(path: String, from: String, isPrivate: Bool, token: String) async -> String? { var argv = ["repo", "import", path.trimmingCharacters(in: .whitespaces), "--from", from.trimmingCharacters(in: .whitespaces)] if isPrivate { argv.append("--private") } let token = token.trimmingCharacters(in: .whitespacesAndNewlines) if !token.isEmpty { argv.append("--token-stdin") } return await run(argv, stdin: token.isEmpty ? nil : token + "\n") } private func run(_ argv: [String], stdin: String?) async -> String? { working = true errorMessage = nil defer { working = false } do { let created = try await client.run(argv, stdin: stdin, as: Created.self) return created?.path } catch let error as GitbayError { errorMessage = error.userFacingMessage } catch { errorMessage = GitbayError.transport(error).userFacingMessage } return nil } }