Sources/MockoloFramework/Models/Import.swift (102 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. // /// A structure defining an "import" statement parsed by `Generator`, including various modifiers. struct Import: CustomStringConvertible { /// The access level of the import enum ACL: String { case `private` case `fileprivate` case `internal` case `package` case `public` var rank: Int { switch self { case .private: 0 case .fileprivate: 1 case .internal: 2 case .package: 3 case .public: 4 } } } /// A modifier that precedes the "import" keyword. ACL and "@testable" are mutually exclusive. enum Modifier: RawRepresentable { case acl(ACL) case testable var rawValue: String { switch self { case .acl(let acl): acl.rawValue case .testable: "@testable" } } init?(rawValue: String) { if rawValue == "@testable" { self = .testable } else if let acl = ACL(rawValue: rawValue) { self = .acl(acl) } else { return nil } } } /// Name of the module var moduleName: String /// A modifier preceding the "import" keyword (e.g. public, internal, @testable) var modifier: Modifier? var description: String { if let modifier { return "\(modifier.rawValue) import \(moduleName)" } else { return "import \(moduleName)" } } init( moduleName: String, modifier: Modifier? = nil ) { self.moduleName = moduleName self.modifier = modifier } } extension Import { /// Returns a copy with a `.testable` modifier var asTestable: Import { var new = self new.modifier = .testable return new } /// Creates an `Import` by parsing a `String` provided by `Generator`. init?(line: String) { guard let importSpaceRange = line.range(of: String.importSpace) else { return nil } let firstNewlineIndex = line.firstIndex(of: "\n") let lastNewlineIndex = line.lastIndex(of: "\n") let startIndex = firstNewlineIndex ?? line.startIndex let endIndex = lastNewlineIndex ?? line.endIndex moduleName = String(line[importSpaceRange.upperBound..<endIndex]) if importSpaceRange.lowerBound == startIndex { modifier = nil } else { let modifierEndIndex = line.index(before: importSpaceRange.lowerBound) modifier = Modifier(rawValue: String(line[startIndex..<modifierEndIndex])) } } } extension Array where Element == Import { /// Prepares a list of imports for output: /// - consolidates imports of the same module /// - maintains the highest given ACL for a given module /// - overrides the ACL for a given import with `@testable` if any are marked as such /// - sorts by module name func resolved() -> [Import] { var modifierByModuleName = [String: Import.Modifier]() for imp in self { switch (imp.modifier, modifierByModuleName[imp.moduleName]) { case let (.acl(acl), .acl(existingACL)): if acl.rank > existingACL.rank { modifierByModuleName[imp.moduleName] = .acl(acl) } case (.testable, .acl(.internal)), (.testable, .acl(.package)): modifierByModuleName[imp.moduleName] = .testable case (.some(let modifier), .none): modifierByModuleName[imp.moduleName] = modifier default: break } } return Set(map(\.moduleName)).sorted().map { Import( moduleName: $0, modifier: modifierByModuleName[$0] ) } } /// Converts a list of imports into a file-ready `String` func lines() -> String { map { $0.description }.joined(separator: "\n") } }