summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Hutch/Models/Git.swift19
-rw-r--r--Hutch/Views/Repositories/FileTreeView.swift2
-rw-r--r--Hutch/Views/Repositories/FileTreeViewModel.swift24
-rw-r--r--HutchTests/GitObjectTests.swift40
4 files changed, 66 insertions, 19 deletions
diff --git a/Hutch/Models/Git.swift b/Hutch/Models/Git.swift
index 19ec31b..905822e 100644
--- a/Hutch/Models/Git.swift
+++ b/Hutch/Models/Git.swift
@@ -113,7 +113,7 @@ struct GitTree: Codable, Sendable {
struct GitTextBlob: Codable, Sendable {
let id: String?
let shortId: String?
- let text: String
+ let text: String?
let size: Int?
}
@@ -134,14 +134,16 @@ struct GitTreeEntryPage: Codable, Sendable {
extension GitObject: Codable {
private enum CodingKeys: String, CodingKey {
case type, id, shortId, entries, text, size, content
+ case typename = "__typename"
}
init(from decoder: any Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let type = try container.decodeIfPresent(String.self, forKey: .type)
+ let typename = try container.decodeIfPresent(String.self, forKey: .typename)
- switch type {
- case "TREE":
+ switch type ?? typename {
+ case "TREE", "Tree":
let tree = GitTree(
id: try container.decodeIfPresent(String.self, forKey: .id),
shortId: try container.decodeIfPresent(String.self, forKey: .shortId),
@@ -149,13 +151,12 @@ extension GitObject: Codable {
)
self = .tree(tree)
- case "BLOB":
- // TextBlob has a "text" key; BinaryBlob does not
- if container.contains(.text) {
+ case "BLOB", "TextBlob", "BinaryBlob":
+ if typename == "TextBlob" || container.contains(.text) {
let blob = GitTextBlob(
id: try container.decodeIfPresent(String.self, forKey: .id),
shortId: try container.decodeIfPresent(String.self, forKey: .shortId),
- text: try container.decode(String.self, forKey: .text),
+ text: try container.decodeIfPresent(String.self, forKey: .text),
size: try container.decodeIfPresent(Int.self, forKey: .size)
)
self = .textBlob(blob)
@@ -184,12 +185,14 @@ extension GitObject: Codable {
try container.encodeIfPresent(tree.entries, forKey: .entries)
case .textBlob(let blob):
try container.encode("BLOB", forKey: .type)
+ try container.encode("TextBlob", forKey: .typename)
try container.encodeIfPresent(blob.id, forKey: .id)
try container.encodeIfPresent(blob.shortId, forKey: .shortId)
- try container.encode(blob.text, forKey: .text)
+ try container.encodeIfPresent(blob.text, forKey: .text)
try container.encodeIfPresent(blob.size, forKey: .size)
case .binaryBlob(let blob):
try container.encode("BLOB", forKey: .type)
+ try container.encode("BinaryBlob", forKey: .typename)
try container.encodeIfPresent(blob.id, forKey: .id)
try container.encodeIfPresent(blob.shortId, forKey: .shortId)
try container.encodeIfPresent(blob.size, forKey: .size)
diff --git a/Hutch/Views/Repositories/FileTreeView.swift b/Hutch/Views/Repositories/FileTreeView.swift
index 3750adb..45fe5b6 100644
--- a/Hutch/Views/Repositories/FileTreeView.swift
+++ b/Hutch/Views/Repositories/FileTreeView.swift
@@ -235,7 +235,7 @@ private struct FileTreeContentView: View {
GeometryReader { geometry in
ScrollView([.vertical, .horizontal]) {
- Text(blob.text)
+ Text(blob.text ?? "")
.font(.system(.body, design: .monospaced))
.multilineTextAlignment(.leading)
.fixedSize(horizontal: true, vertical: false)
diff --git a/Hutch/Views/Repositories/FileTreeViewModel.swift b/Hutch/Views/Repositories/FileTreeViewModel.swift
index f270779..a9c86b4 100644
--- a/Hutch/Views/Repositories/FileTreeViewModel.swift
+++ b/Hutch/Views/Repositories/FileTreeViewModel.swift
@@ -96,6 +96,7 @@ final class FileTreeViewModel {
mode
object {
type
+ __typename
id
shortId
... on Tree {
@@ -110,12 +111,10 @@ final class FileTreeViewModel {
}
}
... on TextBlob {
- text
size
}
... on BinaryBlob {
size
- content
}
}
}
@@ -142,6 +141,7 @@ final class FileTreeViewModel {
mode
object {
type
+ __typename
id
shortId
... on Tree {
@@ -156,12 +156,10 @@ final class FileTreeViewModel {
}
}
... on TextBlob {
- text
size
}
... on BinaryBlob {
size
- content
}
}
}
@@ -187,6 +185,7 @@ final class FileTreeViewModel {
mode
object {
type
+ __typename
id
shortId
... on Tree {
@@ -201,12 +200,10 @@ final class FileTreeViewModel {
}
}
... on TextBlob {
- text
size
}
... on BinaryBlob {
size
- content
}
}
}
@@ -321,16 +318,23 @@ final class FileTreeViewModel {
// Otherwise fetch the subtree (handles pagination)
await loadSubtree(name: entry.name, treeId: objectSHA)
- case .textBlob:
- viewingEntry = entry
- viewingObject = object
+ case .textBlob(let blob):
+ if blob.text != nil {
+ viewingEntry = entry
+ viewingObject = object
+ } else if let blobId = blob.id {
+ await loadBlob(entry: entry, blobId: blobId)
+ }
case .binaryBlob(let blob):
- if blob.content != nil || blob.size != nil {
+ if blob.content != nil {
viewingEntry = entry
viewingObject = object
} else if let blobId = blob.id {
await loadBlob(entry: entry, blobId: blobId)
+ } else {
+ viewingEntry = entry
+ viewingObject = object
}
case .unknown:
diff --git a/HutchTests/GitObjectTests.swift b/HutchTests/GitObjectTests.swift
new file mode 100644
index 0000000..547c6f3
--- /dev/null
+++ b/HutchTests/GitObjectTests.swift
@@ -0,0 +1,40 @@
+import Foundation
+import Testing
+@testable import Hutch
+
+struct GitObjectTests {
+
+ @Test
+ @MainActor
+ func decodesMetadataOnlyTextBlobUsingTypename() throws {
+ let data = Data(#"{"type":"BLOB","__typename":"TextBlob","id":"blob123","shortId":"blob123","size":42}"#.utf8)
+
+ let blob = try JSONDecoder().decode(GitObject.self, from: data)
+
+ guard case .textBlob(let textBlob) = blob else {
+ Issue.record("Expected metadata-only blob to decode as text blob.")
+ return
+ }
+
+ #expect(textBlob.id == "blob123")
+ #expect(textBlob.size == 42)
+ #expect(textBlob.text == nil)
+ }
+
+ @Test
+ @MainActor
+ func decodesMetadataOnlyBinaryBlobUsingTypename() throws {
+ let data = Data(#"{"type":"BLOB","__typename":"BinaryBlob","id":"blob456","shortId":"blob456","size":64}"#.utf8)
+
+ let blob = try JSONDecoder().decode(GitObject.self, from: data)
+
+ guard case .binaryBlob(let binaryBlob) = blob else {
+ Issue.record("Expected metadata-only blob to decode as binary blob.")
+ return
+ }
+
+ #expect(binaryBlob.id == "blob456")
+ #expect(binaryBlob.size == 64)
+ #expect(binaryBlob.content == nil)
+ }
+}