Sources/MockoloFramework/Utils/StringExtensions.swift (225 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 @_spi(RawSyntax) import SwiftSyntax @_spi(Diagnostics) import SwiftParser extension Int { var tab: String { return String(repeating: " ", count: self) } } extension String { static public let protocolDecl = "protocol ".data(using: .utf8) static public let classDecl = "class ".data(using: .utf8) static let `try` = "try" static let `throws` = "throws" static let `rethrows` = "rethrows" static let async = "async" static let await = "await" static let `inout` = "inout" static let hasBlankInit = "_hasBlankInit" static let `Self` = "Self" static let `static` = "static" static let importSpace = "import " static public let `class` = "class" static let `actor` = "actor" static let actorProtocol = "Actor" static public let `final` = "final" static let override = "override" static let privateSet = "private(set)" static let mockType = "protocol" static let prefix = "prefix" static let anyType = "Any" static let voidType = "()" static let neverType = "Never" static let any = "any" static let some = "some" static let anyObject = "AnyObject" static let fatalError = "fatalError" static let available = "available" static let `public` = "public" static let `open` = "open" static let initializer = "init" static let argsHistorySuffix = "ArgValues" static let handlerSuffix = "Handler" static let observable = "Observable" static let rxObservable = "RxSwift.Observable" static let observableLeftAngleBracket = observable + "<" static let rxObservableLeftAngleBracket = rxObservable + "<" static let anyPublisher = "AnyPublisher" static let anyPublisherLeftAngleBracket = anyPublisher + "<" static let eraseToAnyPublisher = "eraseToAnyPublisher" static let passthroughSubject = "PassthroughSubject" static let currentValueSubject = "CurrentValueSubject" static let publishSubject = "PublishSubject" static let behaviorSubject = "BehaviorSubject" static let replaySubject = "ReplaySubject" static let replaySubjectCreate = ".create(bufferSize: 1)" static let behaviorRelay = "BehaviorRelay" static let variable = "Variable" static let empty = ".empty()" static let observableEmpty = "Observable.empty()" static let rxObservableEmpty = "RxSwift.Observable.empty()" static let `required` = "required" static let `convenience` = "convenience" static let closureArrow = "->" static let moduleColon = "module:" static let typealiasColon = "typealias:" static let combineColon = "combine:" static let rxColon = "rx:" static let varColon = "var:" static let historyColon = "history:" static let modifiersColon = "modifiers:" static let overrideColon = "override:" static let `typealias` = "typealias" static let annotationArgDelimiter = ";" static let subjectSuffix = "Subject" static let underlyingVarPrefix = "_" static let setCallCountSuffix = "SetCallCount" static let callCountSuffix = "CallCount" static let stateSuffix = "State" static let initializerLeftParen = "init(" static let `escaping` = "@escaping" static let autoclosure = "@autoclosure" static let name = "name" static let sendable = "Sendable" static let error = "Error" static let mainActor = "MainActor" static public let mockAnnotation = "@mockable" static public let mockObservable = "@MockObservable" static public let poundIf = "#if " static public let poundEndIf = "#endif" static public let headerDoc = """ /// /// @Generated by Mockolo /// """ var safeName: String { var text = self if let keyword = text.withSyntaxText(Keyword.init), TokenKind.keyword(keyword).isLexerClassifiedKeyword { return "`\(self)`" } return self } var removingExistentialAny: String { var typeName = self if typeName.hasPrefix(.any) { typeName.removeFirst(String.any.count) typeName = typeName.trimmingCharacters(in: .whitespaces) } return typeName } var withSpace: String { return "\(self) " } var withDot: String { return "\(self)." } var withLeftAngleBracket: String { return "\(self)<" } var withRightAngleBracket: String { return "\(self)>" } var withColon: String { return "\(self):" } var withLeftParen: String { return "\(self)(" } var withRightParen: String { return "\(self))" } mutating func withoutTrailingCharacters(_ characters: [String]) -> String { for character in characters { if hasSuffix(character) { _ = self.removeLast() } } return self } func canBeInitParam(type: String, isStatic: Bool) -> Bool { return !(isStatic || type.hasPrefix(.anyPublisher) || (type.hasSuffix("?") && type.contains(String.closureArrow)) || isGenerated(type: SwiftType(type))) } func isGenerated(type: SwiftType) -> Bool { return self.hasPrefix(.underlyingVarPrefix) || self.hasSuffix(.setCallCountSuffix) || self.hasSuffix(.callCountSuffix) || self.hasSuffix(.subjectSuffix) || (self.hasSuffix(.handlerSuffix) && type.isOptional) } func arguments(with delimiter: String) -> [String: String]? { let argstr = self let args = argstr.components(separatedBy: delimiter) var argsMap = [String: String]() for item in args { let keyVal = item.components(separatedBy: "=").map{$0.trimmingCharacters(in: .whitespaces)} if let k = keyVal.first { if k.contains(":") { break } if let v = keyVal.last { argsMap[k] = v } } } return !argsMap.isEmpty ? argsMap : nil } func deletingPrefix(_ prefix: String) -> String { guard self.hasPrefix(prefix) else { return self } return String(self.dropFirst(prefix.count)) } func addingIndent(_ tabs: Int) -> String { self.split(separator: "\n") .map { line in if line.isEmpty { return "" } return "\(tabs.tab)\(line)" } .joined(separator: "\n") } } let separatorsForDisplay = CharacterSet(charactersIn: "<>[] :,()_-.&@#!{}@+\"\'") let separatorsForLiterals = CharacterSet(charactersIn: "?<>[] :,()_-.&@#!{}@+\"\'") extension StringProtocol { var isNotEmpty: Bool { return !isEmpty } var capitalizeFirstLetter: String { return prefix(1).capitalized + dropFirst() } func shouldParse(with exclusionList: [String]) -> Bool { guard hasSuffix(".swift") else { return false } guard !exclusionList.isEmpty else { return true } if let name = components(separatedBy: ".swift").first { for ex in exclusionList { if name.hasSuffix(ex) { return false } } return true } return false } var literalComponents: [String] { return self.components(separatedBy: separatorsForLiterals) } var displayableComponents: [String] { let ret = self.replacingOccurrences(of: "?", with: "Optional") return ret.components(separatedBy: separatorsForDisplay).filter {!$0.isEmpty} } var components: [String] { return self.components(separatedBy: separatorsForDisplay).filter {!$0.isEmpty} } var asTestableImport: String { return "@testable \(self.asImport)" } var asImport: String { return "import \(self)" } var moduleNameInImport: String { guard self.hasPrefix(String.importSpace) else { return "" } return self.dropFirst(String.importSpace.count).trimmingCharacters(in: .whitespaces) } }