|
28 | 28 | // |
29 | 29 |
|
30 | 30 | package import Foundation |
| 31 | +import SwiftParser |
| 32 | +import SwiftSyntax |
| 33 | +import Testing |
31 | 34 |
|
32 | | -package protocol DocumentationValidator { |
33 | | - func validateFile(at fileURL: URL) throws -> [ValidationResult] |
34 | | -} |
| 35 | +/// Test harness for extracting and validating Swift code examples from documentation |
| 36 | +package struct DocumentationValidator: Validator { |
| 37 | + /// Swift code validator instance |
| 38 | + private let codeValidator: any SyntaxValidator |
| 39 | + private let codeBlocksFrom: CodeBlockExtractor |
35 | 40 |
|
36 | | -private let privateDefaultPathExtensions = ["md"] |
37 | | -extension DocumentationValidator { |
38 | | - /// Default file extensions for documentation files |
39 | | - package static var defaultPathExtensions: [String] { |
40 | | - privateDefaultPathExtensions |
| 41 | + /// Creates a new documentation test harness |
| 42 | + /// - Parameters: |
| 43 | + /// - codeValidator: Validator for Swift code syntax (defaults to CodeSyntaxValidator) |
| 44 | + /// - fileSearcher: File system searcher (defaults to FileManager.default) |
| 45 | + /// - codeBlocksFrom: Function to extract code blocks from content |
| 46 | + package init( |
| 47 | + codeValidator: any SyntaxValidator = CodeSyntaxValidator(), |
| 48 | + codeBlocksFrom: @escaping CodeBlockExtractor = CodeBlockExtraction.callAsFunction(_:) |
| 49 | + ) { |
| 50 | + self.codeValidator = codeValidator |
| 51 | + self.codeBlocksFrom = codeBlocksFrom |
41 | 52 | } |
42 | 53 |
|
43 | | - /// Validates all Swift code examples found in documentation files |
44 | | - /// - Parameters: |
45 | | - /// - relativePaths: Array of relative paths to search for documentation |
46 | | - /// - projectRoot: Root URL of the project |
47 | | - /// - pathExtensions: File extensions to search for (defaults to ["md"]) |
48 | | - /// - Returns: Array of validation results for all code blocks found |
49 | | - /// - Throws: FileSearchError if file operations fail |
50 | | - package func validate( |
51 | | - relativePaths: [String], |
52 | | - atProjectRoot projectRoot: URL, |
53 | | - withPathExtensions pathExtensions: [String] = Self.defaultPathExtensions, |
54 | | - using fileSearcher: any FileSearcher = FileManager.default |
55 | | - ) throws -> [ValidationResult] { |
56 | | - let documentationFiles = try relativePaths.flatMap { docPath in |
57 | | - let absolutePath = projectRoot.appendingPathComponent(docPath) |
58 | | - return try fileSearcher.findDocumentationFiles( |
59 | | - in: absolutePath, |
60 | | - pathExtensions: pathExtensions |
| 54 | + /// Validates all Swift code examples in a specific documentation file |
| 55 | + /// - Parameter fileURL: URL of the file to validate |
| 56 | + /// - Returns: Array of validation results for code blocks in the file |
| 57 | + /// - Throws: Error if file cannot be read or parsed |
| 58 | + package func validateFile(at fileURL: URL) throws -> [ValidationResult] { |
| 59 | + // let fullPath = try resolveFilePath(filePath) |
| 60 | + let content = try String(contentsOf: fileURL) |
| 61 | + |
| 62 | + let codeBlocks = try codeBlocksFrom(content) |
| 63 | + var results: [ValidationResult] = [] |
| 64 | + |
| 65 | + for (index, codeBlock) in codeBlocks.enumerated() { |
| 66 | + results.append( |
| 67 | + validateCodeBlock(fileURL.codeBlock(codeBlock, at: index)) |
61 | 68 | ) |
62 | 69 | } |
63 | | - var allResults: [ValidationResult] = [] |
64 | 70 |
|
65 | | - for filePath in documentationFiles { |
66 | | - let results = try validateFile(at: filePath) |
67 | | - allResults.append(contentsOf: results) |
68 | | - } |
| 71 | + return results |
| 72 | + } |
69 | 73 |
|
70 | | - return allResults |
| 74 | + /// Validates a single code block |
| 75 | + private func validateCodeBlock( |
| 76 | + _ parameters: CodeBlockValidationParameters |
| 77 | + ) -> ValidationResult { |
| 78 | + guard case .example = parameters.codeBlock.blockType else { |
| 79 | + return ValidationResult( |
| 80 | + parameters: parameters, |
| 81 | + testType: .skipped, |
| 82 | + error: nil |
| 83 | + ) |
| 84 | + } |
| 85 | + // Test compilation and basic execution |
| 86 | + return codeValidator.validateSyntax(from: parameters) |
71 | 87 | } |
72 | 88 | } |
0 commit comments