Skip to content

Commit d82da96

Browse files
authored
Deprecate FileSystemProvider and RenderNodeProvider (#1108)
1 parent 66155d8 commit d82da96

16 files changed

+221
-147
lines changed

Sources/SwiftDocC/Indexing/Navigator/NavigatorIndex+Ext.swift

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import Foundation
1414
This class provides a simple way to transform a `FileSystemProvider` into a `RenderNodeProvider` to feed an index builder.
1515
The data from the disk is fetched and processed in an efficient way to build a navigator index.
1616
*/
17+
@available(*, deprecated, message: "This deprecated API will be removed after 6.2 is released.")
1718
public class FileSystemRenderNodeProvider: RenderNodeProvider {
1819

1920
/// The internal `FileSystemProvider` reference.

Sources/SwiftDocC/Indexing/Navigator/NavigatorIndex.swift

+62-12
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import Foundation
1212
import Crypto
1313

1414
/// A protocol to provide data to be indexed.
15+
@available(*, deprecated, message: "This deprecated API will be removed after 6.2 is released.")
1516
public protocol RenderNodeProvider {
1617
/// Get an instance of `RenderNode` to be processed by the index.
1718
/// - Note: Returning `nil` will end the indexing process.
@@ -21,8 +22,6 @@ public protocol RenderNodeProvider {
2122
func getProblems() -> [Problem]
2223
}
2324

24-
25-
2625
/**
2726
A `NavigatorIndex` contains all the necessary information to display the data inside a navigator.
2827
The data ranges from the tree to the necessary pieces of information to filter the content and perform actions in a fast way.
@@ -479,8 +478,12 @@ extension NavigatorIndex {
479478
open class Builder {
480479

481480
/// The data provider.
481+
@available(*, deprecated, message: "This deprecated API will be removed after 6.2 is released")
482482
public let renderNodeProvider: RenderNodeProvider?
483483

484+
/// The documentation archive to build an index from.
485+
public let archiveURL: URL?
486+
484487
/// The output URL.
485488
public let outputURL: URL
486489

@@ -554,26 +557,38 @@ extension NavigatorIndex {
554557
/// Indicates if the page title should be used instead of the navigator title.
555558
private let usePageTitle: Bool
556559

557-
558560
/// Maps the icon render references in the navigator items created by this builder
559561
/// to their image references.
560562
///
561563
/// Use the `NavigatorItem.icon` render reference to look up the full image reference
562564
/// for any custom icons used in this navigator index.
563565
var iconReferences = [String : ImageReference]()
564566

565-
566567
/// Create a new a builder with the given data provider and output URL.
567568
/// - Parameters:
568-
/// - renderNodeProvider: The `RenderNode` provider to use.
569+
/// - archiveURL: The location of the documentation archive that the builder builds an navigator index for.
569570
/// - outputURL: The location where the builder will write the the built navigator index.
570571
/// - bundleIdentifier: The bundle identifier of the documentation that the builder builds a navigator index for.
571572
/// - sortRootChildrenByName: Configure the builder to sort root's children by name.
572573
/// - groupByLanguage: Configure the builder to group the entries by language.
573574
/// - writePathsOnDisk: Configure the builder to write each navigator item's path components to the location.
574575
/// - usePageTitle: Configure the builder to use the "page title" instead of the "navigator title" as the title for each entry.
576+
public init(archiveURL: URL? = nil, outputURL: URL, bundleIdentifier: String, sortRootChildrenByName: Bool = false, groupByLanguage: Bool = false, writePathsOnDisk: Bool = true, usePageTitle: Bool = false) {
577+
self.archiveURL = archiveURL
578+
self.renderNodeProvider = nil
579+
self.outputURL = outputURL
580+
self.bundleIdentifier = bundleIdentifier
581+
self.sortRootChildrenByName = sortRootChildrenByName
582+
self.groupByLanguage = groupByLanguage
583+
self.writePathsOnDisk = writePathsOnDisk
584+
self.usePageTitle = usePageTitle
585+
}
586+
587+
@available(*, deprecated, renamed: "init(archiveURL:outputURL:bundleIdentifier:sortRootChildrenByName:groupByLanguage:writePathsOnDisk:usePageTitle:)", message: "Use 'init(archiveURL:outputURL:bundleIdentifier:sortRootChildrenByName:groupByLanguage:writePathsOnDisk:usePageTitle:)' instead. This deprecated API will be removed after 6.2 is released")
588+
@_disfavoredOverload
575589
public init(renderNodeProvider: RenderNodeProvider? = nil, outputURL: URL, bundleIdentifier: String, sortRootChildrenByName: Bool = false, groupByLanguage: Bool = false, writePathsOnDisk: Bool = true, usePageTitle: Bool = false) {
576590
self.renderNodeProvider = renderNodeProvider
591+
self.archiveURL = nil
577592
self.outputURL = outputURL
578593
self.bundleIdentifier = bundleIdentifier
579594
self.sortRootChildrenByName = sortRootChildrenByName
@@ -1245,13 +1260,43 @@ extension NavigatorIndex {
12451260
problems.append(problem)
12461261
}
12471262

1248-
/**
1249-
Build the index using the passed instance of `RenderNodeProvider` if available.
1250-
- Returns: A list containing all the problems encountered during indexing.
1251-
- Note: If a provider is not available, this method would generate a fatal error.
1252-
*/
1263+
1264+
/// Build the index using the render nodes files in the provided documentation archive.
1265+
/// - Returns: A list containing all the errors encountered during indexing.
1266+
/// - Precondition: Either ``archiveURL`` or ``renderNodeProvider`` is set.
12531267
public func build() -> [Problem] {
1254-
precondition(renderNodeProvider != nil, "Calling build without a renderNodeProvider set is not permitted.")
1268+
if let archiveURL {
1269+
return _build(archiveURL: archiveURL)
1270+
} else {
1271+
return (self as _DeprecatedRenderNodeProviderAccess)._legacyBuild()
1272+
}
1273+
}
1274+
1275+
// After 6.2 is released, move this into `build()`.
1276+
private func _build(archiveURL: URL) -> [Problem] {
1277+
setup()
1278+
1279+
let dataDirectory = archiveURL.appendingPathComponent(NodeURLGenerator.Path.dataFolderName, isDirectory: true)
1280+
for file in FileManager.default.recursiveFiles(startingPoint: dataDirectory) where file.pathExtension.lowercased() == "json" {
1281+
do {
1282+
let data = try Data(contentsOf: file)
1283+
let renderNode = try RenderNode.decode(fromJSON: data)
1284+
try index(renderNode: renderNode)
1285+
} catch {
1286+
problems.append(error.problem(source: file,
1287+
severity: .warning,
1288+
summaryPrefix: "RenderNode indexing process failed"))
1289+
}
1290+
}
1291+
1292+
finalize()
1293+
1294+
return problems
1295+
}
1296+
1297+
@available(*, deprecated, message: "This deprecated API will be removed after 6.2 is released")
1298+
fileprivate func _legacyBuild() -> [Problem] {
1299+
precondition(renderNodeProvider != nil, "Calling `build()` without an `archiveURL` or `renderNodeProvider` set is not permitted.")
12551300

12561301
setup()
12571302

@@ -1274,7 +1319,6 @@ extension NavigatorIndex {
12741319
return availabilityIDs[Int(availabilityID)]
12751320
}
12761321
}
1277-
12781322
}
12791323

12801324
fileprivate extension Error {
@@ -1343,3 +1387,9 @@ enum PathHasher: String {
13431387
}
13441388
}
13451389
}
1390+
1391+
private protocol _DeprecatedRenderNodeProviderAccess {
1392+
// This private function accesses the deprecated RenderNodeProvider
1393+
func _legacyBuild() -> [Problem]
1394+
}
1395+
extension NavigatorIndex.Builder: _DeprecatedRenderNodeProviderAccess {}

Sources/SwiftDocC/Infrastructure/Workspace/FileSystemProvider.swift

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
22
This source file is part of the Swift.org open source project
33

4-
Copyright (c) 2021 Apple Inc. and the Swift project authors
4+
Copyright (c) 2021-2024 Apple Inc. and the Swift project authors
55
Licensed under Apache License v2.0 with Runtime Library Exception
66

77
See https://swift.org/LICENSE.txt for license information
@@ -11,12 +11,14 @@
1111
import Foundation
1212

1313
/// A type that vends a tree of virtual filesystem objects.
14+
@available(*, deprecated, message: "Use 'FileManagerProtocol.recursiveFiles(startingPoint:)' instead. This deprecated API will be removed after 6.2 is released.")
1415
public protocol FileSystemProvider {
1516
/// The organization of the files that this provider provides.
1617
var fileSystem: FSNode { get }
1718
}
1819

1920
/// An element in a virtual filesystem.
21+
@available(*, deprecated, message: "This deprecated API will be removed after 6.2 is released.")
2022
public enum FSNode {
2123
/// A file in a filesystem.
2224
case file(File)

Sources/SwiftDocC/Infrastructure/Workspace/LocalFileSystemDataProvider+BundleDiscovery.swift

+1
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ extension LocalFileSystemDataProvider: DocumentationWorkspaceDataProvider {
142142
}
143143
}
144144

145+
@available(*, deprecated, message: "This deprecated API will be removed after 6.2 is released.")
145146
fileprivate extension [FSNode] {
146147
/// Returns the first file that matches a given predicate.
147148
/// - Parameter predicate: A closure that takes a file as its argument and returns a Boolean value indicating whether the file should be returned from this function.

Sources/SwiftDocC/Infrastructure/Workspace/LocalFileSystemDataProvider.swift

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import Foundation
1212

1313
/// A type that provides documentation bundles that it discovers by traversing the local file system.
14+
@available(*, deprecated, message: "This deprecated API will be removed after 6.2 is released.")
1415
public struct LocalFileSystemDataProvider: FileSystemProvider {
1516
public var identifier: String = UUID().uuidString
1617

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/*
2+
This source file is part of the Swift.org open source project
3+
4+
Copyright (c) 2024 Apple Inc. and the Swift project authors
5+
Licensed under Apache License v2.0 with Runtime Library Exception
6+
7+
See https://swift.org/LICENSE.txt for license information
8+
See https://swift.org/CONTRIBUTORS.txt for Swift project authors
9+
*/
10+
11+
import Foundation
12+
13+
extension FileManagerProtocol {
14+
/// Returns a sequence of all the files in the directory structure from the starting point.
15+
/// - Parameters:
16+
/// - startingPoint: The file or directory that's the top of the directory structure that the file manager traverses.
17+
/// - options: Options for how the file manager enumerates the contents of directories. Defaults to `.skipsHiddenFiles`.
18+
/// - Returns: A sequence of the files in the directory structure.
19+
package func recursiveFiles(startingPoint: URL, options: FileManager.DirectoryEnumerationOptions = .skipsHiddenFiles) -> some Sequence<URL> {
20+
IteratorSequence(FilesIterator(fileManager: self, startingPoint: startingPoint, options: options))
21+
}
22+
}
23+
24+
/// An iterator that traverses the directory structure and returns the files in breadth-first order.
25+
private struct FilesIterator<FileManager: FileManagerProtocol>: IteratorProtocol {
26+
/// The file manager that the iterator uses to traverse the directory structure.
27+
var fileManager: FileManager
28+
var options: Foundation.FileManager.DirectoryEnumerationOptions
29+
30+
private var foundFiles: [URL]
31+
private var foundDirectories: [URL]
32+
33+
init(fileManager: FileManager, startingPoint: URL, options: Foundation.FileManager.DirectoryEnumerationOptions) {
34+
self.fileManager = fileManager
35+
self.options = options
36+
37+
// Check if the starting point is a file or a directory.
38+
if fileManager.directoryExists(atPath: startingPoint.path) {
39+
foundFiles = []
40+
foundDirectories = [startingPoint]
41+
} else {
42+
foundFiles = [startingPoint]
43+
foundDirectories = []
44+
}
45+
}
46+
47+
mutating func next() -> URL? {
48+
// If the iterator has already found some files, return those first
49+
if !foundFiles.isEmpty {
50+
return foundFiles.removeFirst()
51+
}
52+
53+
// Otherwise, check the next found directory and add its contents
54+
guard !foundDirectories.isEmpty else {
55+
// Traversed the entire directory structure
56+
return nil
57+
}
58+
59+
let directory = foundDirectories.removeFirst()
60+
guard let (newFiles, newDirectories) = try? fileManager.contentsOfDirectory(at: directory, options: options) else {
61+
// The iterator protocol doesn't have a mechanism for raising errors. If an error occurs we
62+
return nil
63+
}
64+
65+
foundFiles.append(contentsOf: newFiles)
66+
foundDirectories.append(contentsOf: newDirectories)
67+
68+
// Iterate again after adding new found files and directories.
69+
// This enables the iterator do recurse multiple layers of directories until it finds a file.
70+
return next()
71+
}
72+
}

Sources/SwiftDocCUtilities/Action/Actions/Convert/Indexer.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ extension ConvertAction {
3636
init(outputURL: URL, bundleID: DocumentationBundle.Identifier) throws {
3737
let indexURL = outputURL.appendingPathComponent("index", isDirectory: true)
3838
indexBuilder = Synchronized<NavigatorIndex.Builder>(
39-
NavigatorIndex.Builder(renderNodeProvider: nil,
39+
NavigatorIndex.Builder(
4040
outputURL: indexURL,
4141
bundleIdentifier: bundleID.rawValue,
4242
sortRootChildrenByName: true,

Sources/SwiftDocCUtilities/Action/Actions/IndexAction.swift

+5-6
Original file line numberDiff line numberDiff line change
@@ -13,21 +13,21 @@ import SwiftDocC
1313

1414
/// An action that creates an index of a documentation bundle.
1515
public struct IndexAction: AsyncAction {
16-
let rootURL: URL
16+
let archiveURL: URL
1717
let outputURL: URL
1818
let bundleIdentifier: String
1919

2020
var diagnosticEngine: DiagnosticEngine
2121

2222
/// Initializes the action with the given validated options, creates or uses the given action workspace & context.
23-
public init(documentationBundleURL: URL, outputURL: URL, bundleIdentifier: String, diagnosticEngine: DiagnosticEngine = .init()) throws {
23+
public init(archiveURL: URL, outputURL: URL, bundleIdentifier: String, diagnosticEngine: DiagnosticEngine = .init()) {
2424
// Initialize the action context.
25-
self.rootURL = documentationBundleURL
25+
self.archiveURL = archiveURL
2626
self.outputURL = outputURL
2727
self.bundleIdentifier = bundleIdentifier
2828

2929
self.diagnosticEngine = diagnosticEngine
30-
self.diagnosticEngine.add(DiagnosticConsoleWriter(formattingOptions: [], baseURL: documentationBundleURL))
30+
self.diagnosticEngine.add(DiagnosticConsoleWriter(formattingOptions: [], baseURL: archiveURL))
3131
}
3232

3333
/// Converts each eligible file from the source documentation bundle,
@@ -40,8 +40,7 @@ public struct IndexAction: AsyncAction {
4040
}
4141

4242
private func buildIndex() throws -> [Problem] {
43-
let dataProvider = try LocalFileSystemDataProvider(rootURL: rootURL)
44-
let indexBuilder = NavigatorIndex.Builder(renderNodeProvider: FileSystemRenderNodeProvider(fileSystemProvider: dataProvider),
43+
let indexBuilder = NavigatorIndex.Builder(archiveURL: archiveURL,
4544
outputURL: outputURL,
4645
bundleIdentifier: bundleIdentifier,
4746
sortRootChildrenByName: true,

Sources/SwiftDocCUtilities/Action/Actions/TransformForStaticHostingAction.swift

+1-2
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,7 @@ struct TransformForStaticHostingAction: AsyncAction {
9292
)
9393

9494
// Create a StaticHostableTransformer targeted at the archive data folder
95-
let dataProvider = try LocalFileSystemDataProvider(rootURL: rootURL.appendingPathComponent(NodeURLGenerator.Path.dataFolderName))
96-
let transformer = StaticHostableTransformer(dataProvider: dataProvider, fileManager: fileManager, outputURL: outputURL, indexHTMLData: indexHTMLData)
95+
let transformer = StaticHostableTransformer(dataDirectory: rootURL.appendingPathComponent(NodeURLGenerator.Path.dataFolderName), fileManager: fileManager, outputURL: outputURL, indexHTMLData: indexHTMLData)
9796
try transformer.transform()
9897

9998
}

Sources/SwiftDocCUtilities/ArgumentParsing/ActionExtensions/IndexAction+CommandInitialization.swift

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
22
This source file is part of the Swift.org open source project
33

4-
Copyright (c) 2021 Apple Inc. and the Swift project authors
4+
Copyright (c) 2021-2024 Apple Inc. and the Swift project authors
55
Licensed under Apache License v2.0 with Runtime Library Exception
66

77
See https://swift.org/LICENSE.txt for license information
@@ -12,10 +12,10 @@ import Foundation
1212

1313
extension IndexAction {
1414
/// Initializes ``IndexAction`` from the options in the ``Index`` command.
15-
init(fromIndexCommand index: Docc.Index) throws {
15+
init(fromIndexCommand index: Docc.Index) {
1616
// Initialize the `IndexAction` from the options provided by the `Index` command
17-
try self.init(
18-
documentationBundleURL: index.documentationBundle.urlOrFallback,
17+
self.init(
18+
archiveURL: index.documentationArchive.urlOrFallback,
1919
outputURL: index.outputURL,
2020
bundleIdentifier: index.bundleIdentifier)
2121
}

Sources/SwiftDocCUtilities/ArgumentParsing/Subcommands/Index.swift

+3-3
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ extension Docc {
2121

2222
/// The user-provided path to a `.doccarchive` documentation archive.
2323
@OptionGroup()
24-
public var documentationBundle: DocCArchiveOption
24+
public var documentationArchive: DocCArchiveOption
2525

2626
/// The user-provided bundle name to use for the produced index.
2727
@Option(help: "The bundle name for the index.")
@@ -33,11 +33,11 @@ extension Docc {
3333

3434
/// The path to the directory that all build output should be placed in.
3535
public var outputURL: URL {
36-
documentationBundle.urlOrFallback.appendingPathComponent("index", isDirectory: true)
36+
documentationArchive.urlOrFallback.appendingPathComponent("index", isDirectory: true)
3737
}
3838

3939
public func run() async throws {
40-
let indexAction = try IndexAction(fromIndexCommand: self)
40+
let indexAction = IndexAction(fromIndexCommand: self)
4141
try await indexAction.performAndHandleResult()
4242
}
4343
}

0 commit comments

Comments
 (0)