Skip to content

Commit 8900b91

Browse files
authored
incrementally adding tests to work the parsing from exemplar archives (#3)
* incrementally adding tests to work the parsing from exemplar archives * fixing path references for Linux * adding in references to code, and looking at OpenAPI spec 3.1 for nullable defaults * walking the renderIndex * updating punchlist * added exploration notes
1 parent 78700b5 commit 8900b91

19 files changed

Lines changed: 1247 additions & 932 deletions

Package.swift

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import PackageDescription
55
let package = Package(
66
name: "DocCArchive",
77
platforms: [
8-
.macOS(.v10_15)
8+
.macOS(.v13)
99
],
1010
products: [
1111
.library(
@@ -16,11 +16,15 @@ let package = Package(
1616
dependencies: [
1717
.package(url: "https://github.com/apple/swift-openapi-generator", from: "1.10.0"),
1818
.package(url: "https://github.com/apple/swift-openapi-runtime", from: "1.8.0"),
19+
.package(url: "https://github.com/apple/swift-nio", from: "2.0.0"),
1920
],
2021
targets: [
2122
.target(
2223
name: "DocCArchive",
23-
dependencies: ["VendoredDocC"]
24+
dependencies: [
25+
"VendoredDocC",
26+
.product(name: "_NIOFileSystem", package: "swift-nio"),
27+
]
2428
),
2529
.target(
2630
name: "VendoredDocC",
@@ -37,7 +41,7 @@ let package = Package(
3741
"Diagnostics.json",
3842
"Metadata.json",
3943
"ThemeSettings.spec.json",
40-
"Benchmark.json"
44+
"Benchmark.json",
4145
]
4246
),
4347
.testTarget(

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,7 @@ rm -rf .build
2727
container run -it -c 4 -m 8g -v "$(pwd):/src" -w src/ swift:6.2 swift test
2828
```
2929

30+
31+
### Random Notes
32+
33+
- I'm trying NIOFilesystem as an alternative to Foundation's Bundle structure for navigating and interacting with the filesystem, but I think that I'd like to bind that behind a package trait down the road.

Scripts/format.sh

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#!/bin/bash
2+
##===----------------------------------------------------------------------===##
3+
##
4+
## This source file is part of the Swift Argument Parser open source project
5+
##
6+
## Copyright (c) 2025 Apple Inc. and the Swift project authors
7+
## Licensed under Apache License v2.0 with Runtime Library Exception
8+
##
9+
## See https://swift.org/LICENSE.txt for license information
10+
##
11+
##===----------------------------------------------------------------------===##
12+
13+
# Move to the project root
14+
cd "$(dirname "$0")" || exit
15+
cd ..
16+
echo "Formatting Swift sources in $(pwd)"
17+
18+
# Run the format / lint commands
19+
git ls-files -z '*.swift' | xargs -0 swift format format --parallel --in-place
20+
#git ls-files -z '*.swift' | xargs -0 swift format lint --strict --parallel

Sources/DocCArchive/DocCArchive.swift

Lines changed: 89 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,94 @@
1-
// The Swift Programming Language
2-
// https://docs.swift.org/swift-book
1+
import Foundation
2+
internal import VendoredDocC
33

4-
import VendoredDocC
4+
public struct Archive {
5+
/// File path to the DocC Archive
6+
public let path: String
57

6-
let metadata = Components.Schemas.Metadata(
7-
bundleDisplayName: "fred", bundleIdentifier: "org.swift",
8-
schemaVersion: .init(major: 1, minor: 0, patch: 0))
8+
public init(path: String) {
9+
self.path = path
10+
}
11+
12+
// ExampleDocs.doccarchive
13+
// ├── assets.json
14+
// ├── data
15+
// │   └── documentation
16+
// │   ├── exampledocs
17+
// │   │   └── examplearticle.json
18+
// │   └── exampledocs.json
19+
// ├── diagnostics.json ✅ (need an fixture that includes diagnostics)
20+
// ├── index
21+
// │   └── index.json ✅ (includes title, icon, and path in hierarchical tree of nodes)
22+
// │   (The index directory also contains a multi-segment LMDB database, but that doesn't
23+
// │   appear to be used by the DocC Render single-page application. It seems to focus entirely
24+
// │   on the index.json in this directory, flattening the tree structure encoded and using the
25+
// │   `path` property to identify and reference the relevant JSON files to load (RenderNode.spec.json)
26+
// ├── indexing-records.json ✅ (full text search content within a flat list of IndexingRecord)
27+
// ├── linkable-entities.json
28+
// └── metadata.json ✅
29+
30+
let decoder = JSONDecoder()
31+
32+
func parseMetadata() throws -> Components.Schemas.Metadata {
33+
let metadataURL = URL(filePath: path).appending(component: "metadata").appendingPathExtension(
34+
"json")
35+
// print("metadata URL calculated at \(metadataURL.path)")
36+
37+
let metadataBytes = try Data(contentsOf: metadataURL)
38+
let metadata = try decoder.decode(Components.Schemas.Metadata.self, from: metadataBytes)
39+
return metadata
40+
}
41+
42+
func parseDiagnostics() throws -> Components.Schemas.Diagnostics {
43+
let diagnosticsURL = URL(filePath: path).appending(component: "diagnostics")
44+
.appendingPathExtension("json")
45+
46+
let diagnosticsBytes = try Data(contentsOf: diagnosticsURL)
47+
let diagnostics = try decoder.decode(
48+
Components.Schemas.Diagnostics.self, from: diagnosticsBytes)
49+
return diagnostics
50+
}
51+
52+
func parseIndexingRecords() throws -> Components.Schemas.IndexingRecords {
53+
let indexingRecordsURL = URL(filePath: path).appending(component: "indexing-records")
54+
.appendingPathExtension("json")
55+
56+
let indexingRecordsBytes = try Data(contentsOf: indexingRecordsURL)
57+
let indexingRecords = try decoder.decode(
58+
Components.Schemas.IndexingRecords.self, from: indexingRecordsBytes)
59+
return indexingRecords
60+
}
61+
62+
func parseIndex() throws -> Components.Schemas.RenderIndex {
63+
let indexURL = URL(filePath: path).appending(component: "index").appending(component: "index")
64+
.appendingPathExtension("json")
65+
66+
let indexBytes = try Data(contentsOf: indexURL)
67+
let index = try decoder.decode(Components.Schemas.RenderIndex.self, from: indexBytes)
68+
return index
69+
}
70+
71+
// recursive depth-first walk of tree of Nodes through the list provided, doing the
72+
// function stuff on each node (visitor pattern)
73+
func walkRenderIndexNodes(
74+
nodes: [Components.Schemas.Node], doing: (Components.Schemas.Node, Int) -> Void
75+
) {
76+
for node in nodes {
77+
walkRenderIndexNodes(node: node, level: 0, doing: doing)
78+
}
79+
}
80+
81+
func walkRenderIndexNodes(
82+
node: Components.Schemas.Node, level: Int, doing: (Components.Schemas.Node, Int) -> Void
83+
) {
84+
doing(node, level)
85+
if let childNodes = node.children {
86+
for n in childNodes {
87+
walkRenderIndexNodes(node: n, level: level + 1, doing: doing)
88+
}
89+
}
90+
}
91+
}
992

1093
// JSON files to parse within a DocC Archive:
1194
//

Sources/VendoredDocC/Benchmark.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
"components": {
1010
"schemas": {
1111
"Benchmark": {
12+
"description": "maps to https://github.com/swiftlang/swift-docc/blob/main/Sources/SwiftDocC/Benchmark/BenchmarkResults.swift#L14",
1213
"type": "object",
1314
"required": [
1415
"doccArguments",

Sources/VendoredDocC/IndexingRecords.spec.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
}
1616
},
1717
"IndexingRecord": {
18+
description: "Maps to https://github.com/swiftlang/swift-docc/blob/main/Sources/SwiftDocC/Indexing/IndexingRecord.swift#L14",
1819
"type": "object",
1920
"required": [
2021
"kind",

Sources/VendoredDocC/LinkableEntities.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
}
1616
},
1717
"LinkDestinationSummary": {
18+
"description": "maps to https://github.com/swiftlang/swift-docc/blob/main/Sources/SwiftDocC/LinkTargets/LinkDestinationSummary.swift#L76",
1819
"type": "object",
1920
"additionalProperties": false,
2021
"required": [

Sources/VendoredDocC/Metadata.json

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,17 @@
1010
"schemas": {
1111
"Metadata": {
1212
"type": "object",
13+
"description": "maps to Maps to https://github.com/swiftlang/swift-docc/blob/main/Sources/SwiftDocC/Model/BuildMetadata.swift#L14",
1314
"required": [
1415
"bundleDisplayName",
15-
"bundleIdentifier",
16+
"bundleID",
1617
"schemaVersion"
1718
],
1819
"properties": {
1920
"bundleDisplayName": {
2021
"type": "string"
2122
},
22-
"bundleIdentifier": {
23+
"bundleID": {
2324
"type": "string"
2425
},
2526
"schemaVersion": {
@@ -28,6 +29,7 @@
2829
}
2930
},
3031
"SchemaVersion": {
32+
"description": "A version that follows the [Semantic Versioning](https://semver.org) specification. Maps to https: https://github.com/swiftlang/swift-docc/blob/main/Sources/SwiftDocC/Model/Rendering/SemanticVersion.swift#L14",
3133
"type": "object",
3234
"required": [
3335
"major",
@@ -43,6 +45,14 @@
4345
},
4446
"patch": {
4547
"type": "integer"
48+
},
49+
"prerelease": {
50+
"type": "string",
51+
"description": "The optional prerelease version component, which may contain non-numeric characters."
52+
},
53+
"buildMetadata": {
54+
"type": "string",
55+
"description": "Optional additional build metadata."
4656
}
4757
}
4858
}

Sources/VendoredDocC/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ OpenAPI generator accepts a single OpenAPI spec file, so this directory merges t
2424

2525
```bash
2626
npm i openapi-merge-cli
27-
npx openapi-merge.cli
27+
npx openapi-merge-cli
2828
```
2929

3030
To get Swift code for the serialized types, generate the types from the combined spec. The OpenAPI generator

Sources/VendoredDocC/RenderIndex.spec.json

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"openapi": "3.0.0",
2+
"openapi": "3.1.0",
33
"info": {
44
"description": "Specification of the Swift-DocC Index.json file.",
55
"version": "0.1.2",
@@ -9,10 +9,12 @@
99
"components": {
1010
"schemas": {
1111
"RenderIndex": {
12+
"description": "maps to https://github.com/swiftlang/swift-docc/blob/main/Sources/SwiftDocC/Indexing/RenderIndexJSON/RenderIndex.swift#L24.",
1213
"type": "object",
1314
"required": [
1415
"schemaVersion",
15-
"interfaceLanguages"
16+
"interfaceLanguages",
17+
"includedArchiveIdentifiers"
1618
],
1719
"properties": {
1820
"schemaVersion": {
@@ -42,9 +44,13 @@
4244
}
4345
},
4446
"Node": {
47+
"description": "maps to https://github.com/swiftlang/swift-docc/blob/main/Sources/SwiftDocC/Indexing/RenderIndexJSON/RenderIndex.swift#L117.",
4548
"type": "object",
4649
"required": [
47-
"title"
50+
"title",
51+
"deprecated",
52+
"external",
53+
"beta"
4854
],
4955
"properties": {
5056
"title": {
@@ -95,15 +101,15 @@
95101
"type": "string"
96102
},
97103
"deprecated": {
98-
"type": "boolean",
104+
"type": ["boolean", "null"],
99105
"default": "false"
100106
},
101107
"external": {
102-
"type": "boolean",
108+
"type": ["boolean", "null"],
103109
"default": "false"
104110
},
105111
"beta": {
106-
"type": "boolean",
112+
"type": ["boolean", "null"],
107113
"default": "false"
108114
},
109115
"icon": {

0 commit comments

Comments
 (0)