gitbay/Repos/SymbolSearchViewModel.swift

v1.14.1
gitbay-ios/gitbay/Repos/SymbolSearchViewModel.swift history · blame · raw

49 lines · 1637 bytes

 1import Foundation
 2import Observation
 3
 4/// `repo symbols <owner/name> [--kind <k>] <query>`: where names are
 5/// defined, from the default branch's symbol index (gitbay v1.39.0).
 6@Observable
 7@MainActor
 8final class SymbolSearchViewModel {
 9
10    nonisolated struct Symbol: Decodable, Sendable, Hashable, Identifiable {
11        let name: String
12        let kind: String
13        let path: String
14        let line: Int
15        var id: String { "\(path):\(line):\(kind):\(name)" }
16    }
17
18    /// The kinds `--kind` takes, as the command's help lists them.
19    static let kinds = ["function", "method", "class", "struct", "enum", "interface",
20                        "type", "const", "var", "module", "macro", "section"]
21
22    /// nil until the first search; distinct from an empty result.
23    private(set) var state: LoadState<[Symbol]>?
24    /// nil searches every kind.
25    var kind: String?
26
27    private let client: GitbayClient
28    let repoPath: String
29
30    init(client: GitbayClient, repoPath: String) {
31        self.client = client
32        self.repoPath = repoPath
33    }
34
35    func search(_ query: String) async {
36        let trimmed = query.trimmingCharacters(in: .whitespaces)
37        guard !trimmed.isEmpty else { return }
38        state = .loading
39        var argv = ["repo", "symbols", repoPath]
40        if let kind { argv += ["--kind", kind] }
41        argv.append(trimmed)
42        do {
43            let symbols = try await client.readList(argv, of: Symbol.self)
44            state = symbols.isEmpty ? .empty("No definitions matching \"\(trimmed)\".") : .loaded(symbols)
45        } catch {
46            state = .from(error)
47        }
48    }
49}