Sources/MockoloFramework/Operations/ImportsHandler.swift (97 lines of code) (raw):

// // Copyright (c) 2018. Uber Technologies // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // import Algorithms func handleImports(pathToImportsMap: ImportMap, customImports: [String]?, excludeImports: [String]?, testableImports: [String]?, relevantPaths: [String]) -> String { var topLevelImports: [Import] = [] var conditionalBlocks: [ConditionalImportBlock] = [] // 1. Collect imports from all relevant files for (path, parsedImports) in pathToImportsMap { guard relevantPaths.contains(path) else { continue } for `import` in parsedImports { switch `import` { case .simple(let simple): topLevelImports.append(simple) case .conditional(let conditional): conditionalBlocks.append(conditional) } } } // 2. Sort conditional blocks by offset (file appearance order) conditionalBlocks.sort(by: { $0.offset < $1.offset }) // 3. Add custom imports if let customImports { topLevelImports.append(contentsOf: customImports.map { Import(moduleName: $0) }) } var contents: [ImportContent] { topLevelImports.map { .simple($0) } + conditionalBlocks.map { .conditional($0) } } // 4. Add testable imports if the import does not exist if let testableImports { let usedNames = Set(visitModuleName(contents)) for name in testableImports { if !usedNames.contains(name) { topLevelImports.append(Import(moduleName: name).asTestable) } } } return renderImportContents( contents, excludeImports: excludeImports, testableImports: testableImports ) } private func renderImportContents( _ contents: [ImportContent], excludeImports: [String]?, testableImports: [String]? ) -> String { var clauseLines: [String] = [] var simpleImports: [Import] = [] func resolveAccumulatedSimpleImports() { if !simpleImports.isEmpty { clauseLines.append(simpleImports.resolved().lines()) simpleImports.removeAll(keepingCapacity: true) } } for content in contents { switch content { case .simple(var `import`): if let excludeImports, excludeImports.contains(`import`.moduleName) { continue } if let testableImports, testableImports.contains(`import`.moduleName) { `import` = `import`.asTestable } simpleImports.append(`import`) case .conditional(let block): // First output accumulated simple imports resolveAccumulatedSimpleImports() var result = "" for clause in block.clauses { switch clause.type { case .if(let condition): result += "#if \(condition)\n" case .elseif(let condition): result += "#elseif \(condition)\n" case .else: result += "#else\n" } // Recursively render nested block result += renderImportContents(clause.contents, excludeImports: excludeImports, testableImports: testableImports) result += "\n" } result += "#endif" clauseLines.append(result) } } resolveAccumulatedSimpleImports() return clauseLines.joined(separator: "\n") } private func visitModuleName(_ contents: [ImportContent]) -> [String] { return contents.flatMap { content in switch content { case .simple(let `import`): return [`import`.moduleName] case .conditional(let block): return visitModuleName(block.clauses.flatMap(\.contents)) } } }