Sources/MockoloFramework/Models/MethodModel.swift (188 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 Foundation public enum MethodKind: Equatable { case funcKind case initKind(required: Bool, override: Bool) case subscriptKind } final class MethodModel: Model { let name: String let returnType: SwiftType? let accessLevel: String let kind: MethodKind let offset: Int64 let length: Int64 let genericTypeParams: [ParamModel] let genericWhereClause: String? let params: [ParamModel] let processed: Bool let modelDescription: String? let isStatic: Bool let isAsync: Bool let throwing: ThrowingKind let funcsWithArgsHistory: [String] let customModifiers: [String : Modifier] var modelType: ModelType { return .method } private var staticKind: String { return isStatic ? .static : "" } /// This is used to uniquely identify methods with the same signature and different generic requirements private var genericWhereClauseToSignatureComponent: String { guard let genericWhereClause else { return "" } let typeRequirementSyntax = ":" let typeEqualitySyntax = "==" var signatureComponents: [String] = [] genericWhereClause.deletingPrefix("where").components(separatedBy: ",").forEach { requirement in if requirement.contains(typeRequirementSyntax) { let components = requirement.components(separatedBy: typeRequirementSyntax).map{ $0.trimmingCharacters(in: .whitespaces) } guard let key = components.first, let value = components.last else { return } let valueDescription = value.replacingOccurrences(of: " ", with: "").replacingOccurrences(of: "&", with: "And") signatureComponents.append(contentsOf: [key, valueDescription]) } else if requirement.contains(typeEqualitySyntax) { let components = requirement.components(separatedBy: typeEqualitySyntax).map{ $0.trimmingCharacters(in: .whitespaces) } guard let key = components.first, let value = components.last else { return } signatureComponents.append(contentsOf: [key, value]) } } return signatureComponents.map { component in var newComponent = component newComponent.removeAll(where: { $0 == "."}) return newComponent }.joined() } var isInitializer: Bool { if case .initKind(_, _) = kind { return true } return false } var isSubscript: Bool { if case .subscriptKind = kind { return true } return false } lazy var signatureComponents: [String] = { let paramLabels = self.params.map {$0.label != "_" ? $0.label : ""} let paramNames = self.params.map(\.name) let paramTypes = self.params.map(\.type) let nameString = self.name var args = zip(paramLabels, paramNames).compactMap { (argLabel: String, argName: String) -> String? in let val = argLabel.isEmpty ? argName : argLabel if val.count < 2 || !nameString.lowercased().hasSuffix(val.lowercased()) { return val.capitalizeFirstLetter } return nil } let genericTypeNames = self.genericTypeParams.map { $0.name.capitalizeFirstLetter + $0.type.displayName } args.append(contentsOf: genericTypeNames) if genericWhereClause != nil { args.append(genericWhereClauseToSignatureComponent) } args.append(contentsOf: paramTypes.map(\.displayName)) if var displayType = self.returnType?.displayName { let capped = min(displayType.count, 32) displayType.removeLast(displayType.count-capped) args.append(displayType) } args.append(self.staticKind) let ret = args.filter{ arg in !arg.isEmpty } return ret }() lazy var argsHistory: ArgumentsHistoryModel? = { if isInitializer || isSubscript { return nil } let ret = ArgumentsHistoryModel(name: name, genericTypeParams: genericTypeParams, params: params, isHistoryAnnotated: funcsWithArgsHistory.contains(name)) return ret }() func handler() -> ClosureModel? { if isInitializer { return nil } return ClosureModel(genericTypeParams: genericTypeParams, params: params.map { ($0.name, $0.type) }, isAsync: isAsync, throwing: throwing, returnType: returnType ?? .init(.voidType)) } init(name: String, typeName: String?, kind: MethodKind, acl: String, genericTypeParams: [ParamModel], genericWhereClause: String?, params: [ParamModel], isAsync: Bool, throwing: ThrowingKind, isStatic: Bool, offset: Int64, length: Int64, funcsWithArgsHistory: [String], customModifiers: [String: Modifier], modelDescription: String?, processed: Bool) { self.name = name.trimmingCharacters(in: .whitespaces) self.returnType = typeName.map { SwiftType($0.trimmingCharacters(in: .whitespaces)) } self.isAsync = isAsync self.throwing = throwing self.offset = offset self.length = length self.kind = kind self.isStatic = isStatic self.params = params self.genericTypeParams = genericTypeParams self.genericWhereClause = genericWhereClause self.processed = processed self.funcsWithArgsHistory = funcsWithArgsHistory self.customModifiers = customModifiers self.modelDescription = modelDescription self.accessLevel = acl } var fullName: String { return self.name + self.signatureComponents.joined() + staticKind } func name(by level: Int) -> String { if level <= 0 { return name } let diff = level - self.signatureComponents.count let postfix = diff > 0 ? String(diff) : self.signatureComponents[level - 1] return name(by: level - 1) + postfix } func render( context: RenderContext, arguments: GenerationArguments ) -> String? { let shouldOverride = context.annotatedTypeKind == .class if processed { var prefix = shouldOverride ? "\(String.override) " : "" if case .initKind(required: let isRequired, override: _) = self.kind { if isRequired { prefix = "" } } if let ret = modelDescription?.trimmingCharacters(in: .newlines) { return prefix + ret } return nil } guard let overloadingResolvedName = context.overloadingResolvedName else { return nil } return applyMethodTemplate(overloadingResolvedName: overloadingResolvedName, arguments: arguments, isOverride: shouldOverride, handler: handler(), context: context) } }