diff --git a/Package.swift b/Package.swift
index ff62f58e..7c94b0a8 100644
--- a/Package.swift
+++ b/Package.swift
@@ -85,7 +85,7 @@ let package = Package(
.target(name: "TIPagination", dependencies: ["Cursors", "TISwiftUtils"], path: "TIPagination/Sources"),
.target(name: "TIAuth", dependencies: ["TIFoundationUtils", "TIUIKitCore", "KeychainAccess"], path: "TIAuth/Sources"),
.target(name: "TIEcommerce", dependencies: ["TIFoundationUtils", "TISwiftUtils", "TINetworking", "TIUIKitCore", "TIUIElements"], path: "TIEcommerce/Sources"),
- .target(name: "TITextProcessing", dependencies: ["Antlr4"], path: "TITextProcessing/Sources"),
+ .target(name: "TITextProcessing", dependencies: [.product(name: "Antlr4", package: "antlr4")], path: "TITextProcessing/Sources"),
// MARK: - Tests
@@ -93,5 +93,9 @@ let package = Package(
name: "TITimerTests",
dependencies: ["TIFoundationUtils"],
path: "Tests/TITimerTests"),
+ .testTarget(
+ name: "TITextProcessingTests",
+ dependencies: ["TITextProcessing"],
+ path: "Tests/TITextProcessingTests")
]
)
diff --git a/TITextProcessing/README.md b/TITextProcessing/README.md
new file mode 100644
index 00000000..aa040abb
--- /dev/null
+++ b/TITextProcessing/README.md
@@ -0,0 +1,102 @@
+# `TITextProcessing`
+
+### Библиотека для работы с регулярными выражениями
+
+## - `TextFormatter`
+
+Класс `TextFormatter` представляет из себя сервис, принимающий регулярное выражение на вход и предоставляющий возможность генерации следующих объектов:
+- `Replacement template` из `getRegexReplacement()`;
+- `Placeholder` из `getRegexPlaceholder()`;
+- `Formatter text` из `getFormattedText(_ text: String)`
+
+-
+#### `func getRegexReplacement()`
+
+Метод, преобразующий входящее регулярное выражение в шаблон подстановки, например:
+
+**Input**: `(\\d{4}) ?(\\d{4}) ?(\\d{4}) ?(\\d{4})`
+**Output**: `$1 $2 $3 $4`
+
+-
+#### `func getRegexPlaceholder()`
+
+Метод, преобразующий входящее регулярное выражение в текст-заполнитесь a.k.a placeholder, например:
+
+**Input**: `(\\d{4}) ?(\\d{4}) ?(\\d{4}) ?(\\d{4})`
+**Output**: `1234 5678 9012 3456`
+
+-
+#### `func getFormattedText(_ text: String) -> String`
+
+Метод, преобразующий входящий текст к нужному формату, заранее определенному посредством указания регулярного выражения, например:
+
+**Input**: `2200111555550080`
+**Output**: `2200 1115 5555 0080`
+
+> P.S. Учитываем, что `TextFormatter` был проинициализирован со слеюущим регулярным выражением: `(\\d{4}) ?(\\d{4}) ?(\\d{4}) ?(\\d{4})`
+
+## - `RegexReplaceGenerator`
+
+Класс, отвечающий за генерацию `PCREGeneratorItem` из входящего регулярного выражения. Использует библиотеку `Antlr4` и `PCRE` для работы.
+
+-
+#### `static func generateReplacement(for regex: String) -> PCREGeneratorItem`
+
+Функция, преобразующий входящее регулярное выражение в структуру, содержащую шаблон подстановки и матрицу символов, например:
+
+```swift
+let item = RegexReplaceGenerator. generateReplacement(for: "(\\d{2})\\/?(\\d{2})")
+
+print(item.regexReplaceString)
+
+/*
+Выведет в консоль:
+"$1\\/$2"
+*/
+
+print(item.matrixOfSymbols)
+
+/*
+Выведет в консоль:
+[
+ ["1", "2", "3", "4", "5", "6", "7", "8", "9", "0"],
+ ["1", "2", "3", "4", "5", "6", "7", "8", "9", "0"],
+ ["/"],
+ ["1", "2", "3", "4", "5", "6", "7", "8", "9", "0"],
+ ["1", "2", "3", "4", "5", "6", "7", "8", "9", "0"]
+]
+*/
+```
+
+Итоговый `PCREGeneratorItem` содержит следующие данные:
+
+`regexReplaceString` - итоговый шаблон подстановки для изначального регулярного выражения;
+`matrixOfSymbols` - матрица символов, содержащая все возможные символы для каждого элемента в изначальном регулярном выражении
+
+## - `RegexPlaceholderGenerator`
+
+Класс, отвечающий за генерацию текста-заполнителя a.k.a placeholder.
+
+-
+#### `static func generatePlaceholder(matrixOfSymbols: [[Character]]) -> String`
+
+Функция, преобразующая входящую матрицу символов в текст-заполнитель, например:
+
+```swift
+let matrix: [[Character]] = [
+ ["1", "2", "3", "4", "5", "6", "7", "8", "9", "0"],
+ ["1", "2", "3", "4", "5", "6", "7", "8", "9", "0"],
+ ["/"],
+ ["1", "2", "3", "4", "5", "6", "7", "8", "9", "0"],
+ ["1", "2", "3", "4", "5", "6", "7", "8", "9", "0"]
+]
+
+let placeholder = RegexPlaceholderGenerator.generatePlaceholder(matrixOfSymbols: matrix)
+
+print(placeholder)
+
+/*
+Выведет в консоль:
+"12/34"
+*/
+```
diff --git a/TITextProcessing/Sources/RegexPlaceholderGenerator/RegexPlaceholderGenerator.swift b/TITextProcessing/Sources/RegexPlaceholderGenerator/RegexPlaceholderGenerator.swift
new file mode 100644
index 00000000..9aae3e8c
--- /dev/null
+++ b/TITextProcessing/Sources/RegexPlaceholderGenerator/RegexPlaceholderGenerator.swift
@@ -0,0 +1,56 @@
+//
+// Copyright (c) 2023 Touch Instinct
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the Software), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+import Foundation
+
+public final class RegexPlaceholderGenerator {
+
+ public static func generatePlaceholder(matrixOfSymbols: [[Character]]) -> String {
+ var placeholderStringBuilder = String()
+ var indexes = [Array: Int]()
+
+ matrixOfSymbols.forEach { listOfSymbols in
+ indexes[listOfSymbols] = 0
+ }
+
+ matrixOfSymbols.filter { !$0.isEmpty }.forEach { listOfSymbols in
+ if listOfSymbols.count == 1 {
+ placeholderStringBuilder.append(listOfSymbols[0])
+ return
+ }
+
+ if let index = indexes[listOfSymbols] {
+ var newIndex = index
+
+ if listOfSymbols.count <= newIndex {
+ newIndex = 0
+ }
+
+ placeholderStringBuilder.append(listOfSymbols[newIndex])
+ newIndex += 1
+ indexes[listOfSymbols] = newIndex
+ }
+ }
+
+ return placeholderStringBuilder
+ }
+}
diff --git a/TITextProcessing/Sources/RegexReplaceGenerator/PCREGeneratorItem.swift b/TITextProcessing/Sources/RegexReplaceGenerator/PCREGeneratorItem.swift
new file mode 100644
index 00000000..e13c85e4
--- /dev/null
+++ b/TITextProcessing/Sources/RegexReplaceGenerator/PCREGeneratorItem.swift
@@ -0,0 +1,28 @@
+//
+// Copyright (c) 2023 Touch Instinct
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the Software), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+import Foundation
+
+public struct PCREGeneratorItem {
+ public let regexReplaceString: String
+ public let matrixOfSymbols: [[Character]]
+}
diff --git a/TITextProcessing/Sources/RegexReplaceGenerator/PCREGeneratorListener.swift b/TITextProcessing/Sources/RegexReplaceGenerator/PCREGeneratorListener.swift
new file mode 100644
index 00000000..d6991fa2
--- /dev/null
+++ b/TITextProcessing/Sources/RegexReplaceGenerator/PCREGeneratorListener.swift
@@ -0,0 +1,172 @@
+//
+// Copyright (c) 2023 Touch Instinct
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the Software), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+import Foundation
+import Antlr4
+
+public final class PCREGeneratorListener: PCREBaseListener {
+
+ // MARK: - Properties
+
+ /*
+ Matrix of available symbols for placeholder where
+ index - symbol number and value - all available symbols
+ */
+ private var matrixOfSymbols = [[Character]]()
+
+ /*
+ Regex group index counter, 1 by default
+ */
+ private var currentGroupIndex = 1
+
+ /*
+ The final output replacement of the entered regex
+ */
+ private var regexReplaceString = ""
+
+ /*
+ Search element from the regex
+ Could contain possible elements of a regex, e.g.:
+ [1-2], \\d, [A-B] and elements not related to regex or escaped
+ */
+ private var listOfSymbols = [Character]()
+
+ // MARK: - Overrides
+
+ /*
+ Called when a new capture group found
+ */
+ public override func enterCapture(_ ctx: PCREParser.CaptureContext) {
+ super.enterCapture(ctx)
+
+ regexReplaceString += "$\(currentGroupIndex)"
+ currentGroupIndex += 1
+ }
+
+ /*
+ Called when there is a digit symbol found, e.g.:
+ \d{2} where \d is an indication of a digit symbol
+ */
+ public override func enterShared_atom(_ ctx: PCREParser.Shared_atomContext) {
+ super.enterShared_atom(ctx)
+
+ listOfSymbols = "1234567890".map { Character(String($0)) }
+ matrixOfSymbols.append(listOfSymbols)
+ }
+
+ /*
+ Called when there is a range found, e.g.:
+ [А-дD-f] or [А-д]
+ */
+ public override func enterCharacter_class(_ ctx: PCREParser.Character_classContext) {
+ super.enterCharacter_class(ctx)
+
+ // Range count validation
+ // - true if [А-дD-f]
+ // - false if [А-д]
+ if ctx.cc_atom().count > 1 {
+ listOfSymbols = []
+
+ guard let firstChar = ctx.CharacterClassStart()?.getText() else {
+ listOfSymbols = getAvailableSymbols(for: ctx.getText())
+ return
+ }
+
+ let endChar = ctx.CharacterClassEnd()[0].getText()
+
+ for i in 0 ..< ctx.cc_atom().count {
+ listOfSymbols += getAvailableSymbols(for: firstChar + ctx.cc_atom()[i].getText() + endChar)
+ }
+ } else {
+ listOfSymbols = getAvailableSymbols(for: ctx.getText())
+ }
+
+ matrixOfSymbols.append(listOfSymbols)
+ }
+
+ /*
+ Called when there is a number of element duplication found, e.g.:
+ [A-B]{6} where {6} is a number of required element duplication
+ */
+ public override func enterDigits(_ ctx: PCREParser.DigitsContext) {
+ super.enterDigits(ctx)
+
+ guard let count = Int(ctx.getText()) else {
+ return
+ }
+
+ for _ in 1 ..< count {
+ matrixOfSymbols.append(listOfSymbols)
+ }
+ }
+
+ /*
+ Called when there is a single non-group literal found, e.g.:
+ (?:\\+7 ) where "+", "7" and " " are single non-group literals
+ */
+ public override func enterLiteral(_ ctx: PCREParser.LiteralContext) {
+ super.enterLiteral(ctx)
+
+ guard let text = ctx.shared_literal()?.getText() else {
+ return
+ }
+
+ regexReplaceString += text
+ listOfSymbols = []
+
+ ctx.getText().forEach { symbol in
+ listOfSymbols.append(symbol)
+ }
+
+ matrixOfSymbols.append(listOfSymbols)
+ }
+
+ // MARK: - Public methods
+
+ public func toPCREGeneratorItem() -> PCREGeneratorItem {
+ return PCREGeneratorItem(regexReplaceString: regexReplaceString,
+ matrixOfSymbols: matrixOfSymbols.map { $0.filter { $0 != "\\" } })
+ }
+
+ // MARK: - Private methods
+
+ private func getAvailableSymbols(for ctxText: String) -> [Character] {
+ let startAtomStr = ctxText[ctxText.index(after: ctxText.startIndex)]
+ let endAtomStr = ctxText[ctxText.index(ctxText.endIndex, offsetBy: -2)]
+
+ guard (startAtomStr.isLetter || startAtomStr.isNumber) && (endAtomStr.isLetter || endAtomStr.isNumber) else {
+ return [startAtomStr, endAtomStr]
+ }
+
+ guard let startRangeScalar = startAtomStr.unicodeScalars.first?.value,
+ let endRangeScalar = endAtomStr.unicodeScalars.first?.value else {
+ return [startAtomStr, endAtomStr]
+ }
+
+ let symbols = (startRangeScalar...endRangeScalar)
+ .compactMap(UnicodeScalar.init)
+ .map(Character.init)
+ .filter { $0.isLetter || $0.isNumber }
+
+ return symbols
+ }
+}
diff --git a/TITextProcessing/Sources/RegexReplaceGenerator/RegexReplaceGenerator.swift b/TITextProcessing/Sources/RegexReplaceGenerator/RegexReplaceGenerator.swift
new file mode 100644
index 00000000..bcd0095a
--- /dev/null
+++ b/TITextProcessing/Sources/RegexReplaceGenerator/RegexReplaceGenerator.swift
@@ -0,0 +1,45 @@
+//
+// Copyright (c) 2023 Touch Instinct
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the Software), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+import Foundation
+import Antlr4
+
+public final class RegexReplaceGenerator {
+
+ public static func generateReplacement(for regex: String) -> PCREGeneratorItem {
+ let inputStream = ANTLRInputStream(regex)
+ let lexer = PCRELexer(inputStream)
+ let tokens = CommonTokenStream(lexer)
+ let walker = ParseTreeWalker()
+ let pcreGeneratorListener = PCREGeneratorListener()
+
+ let parser = try? PCREParser(tokens)
+
+ guard let parseContext = try? parser?.parse() as? ParseTree else {
+ fatalError("Cannot parse input regex")
+ }
+
+ try? walker.walk(pcreGeneratorListener, parseContext)
+
+ return pcreGeneratorListener.toPCREGeneratorItem()
+ }
+}
diff --git a/TITextProcessing/Sources/TextFormatter/TextFormatter.swift b/TITextProcessing/Sources/TextFormatter/TextFormatter.swift
new file mode 100644
index 00000000..94badf2b
--- /dev/null
+++ b/TITextProcessing/Sources/TextFormatter/TextFormatter.swift
@@ -0,0 +1,53 @@
+//
+// Copyright (c) 2023 Touch Instinct
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the Software), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+import Foundation
+
+public final class TextFormatter {
+
+ private let regex: String
+
+ public init(regex: String) {
+ self.regex = regex
+ }
+
+ public func getRegexReplacement() -> String {
+ RegexReplaceGenerator.generateReplacement(for: regex).regexReplaceString
+ }
+
+ public func getRegexPlaceholder() -> String {
+ let matrixOfSymbols = RegexReplaceGenerator.generateReplacement(for: regex).matrixOfSymbols
+
+ return RegexPlaceholderGenerator.generatePlaceholder(matrixOfSymbols: matrixOfSymbols)
+ }
+
+ public func getFormattedText(_ text: String) -> String {
+ guard let expression = try? NSRegularExpression(pattern: regex, options: .caseInsensitive) else {
+ fatalError("Cannot create NSRegularExpression from input regex")
+ }
+
+ return expression.stringByReplacingMatches(in: text,
+ options: .reportProgress,
+ range: NSMakeRange(0, text.count),
+ withTemplate: getRegexReplacement())
+ }
+}
diff --git a/Tests/TITextProcessingTests/TITextProcessingTests.swift b/Tests/TITextProcessingTests/TITextProcessingTests.swift
new file mode 100644
index 00000000..ef7e35d3
--- /dev/null
+++ b/Tests/TITextProcessingTests/TITextProcessingTests.swift
@@ -0,0 +1,112 @@
+//
+// Copyright (c) 2023 Touch Instinct
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the Software), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+import XCTest
+@testable import TITextProcessing
+
+final class TITextProcessingTests: XCTestCase {
+
+ func testDateRegex() {
+ // given
+ let regex = "(\\d{2})\\/?(\\d{2})"
+ let inputText = "1525"
+ let formatter = TextFormatter(regex: regex)
+
+ // when
+ let regexReplacement = formatter.getRegexReplacement()
+ let regexPlaceholder = formatter.getRegexPlaceholder()
+ let formattedText = formatter.getFormattedText(inputText)
+
+ // then
+ XCTAssertEqual(regexReplacement, "$1\\/$2")
+ XCTAssertEqual(regexPlaceholder, "12/34")
+ XCTAssertEqual(formattedText, "15/25")
+ }
+
+ func testCardNumberRegex() {
+ // given
+ let regex = "(\\d{4}) ?(\\d{4}) ?(\\d{4}) ?(\\d{4})"
+ let inputText = "2200111555550080"
+ let formatter = TextFormatter(regex: regex)
+
+ // when
+ let regexReplacement = formatter.getRegexReplacement()
+ let regexPlaceholder = formatter.getRegexPlaceholder()
+ let formattedText = formatter.getFormattedText(inputText)
+
+ // then
+ XCTAssertEqual(regexReplacement, "$1 $2 $3 $4")
+ XCTAssertEqual(regexPlaceholder, "1234 5678 9012 3456")
+ XCTAssertEqual(formattedText, "2200 1115 5555 0080")
+ }
+
+ func testPhoneNumberRegex() {
+ // given
+ let regex = "(?:\\+7 )?\\(?(\\d{3})\\)? ?(\\d{3}) ?(\\d{2}) ?(\\d{2})"
+ let inputText = "9995534820"
+ let formatter = TextFormatter(regex: regex)
+
+ // when
+ let regexReplacement = formatter.getRegexReplacement()
+ let regexPlaceholder = formatter.getRegexPlaceholder()
+ let formattedText = formatter.getFormattedText(inputText)
+
+ // then
+ XCTAssertEqual(regexReplacement, "\\+7 \\($1\\) $2 $3 $4")
+ XCTAssertEqual(regexPlaceholder, "+7 (123) 456 78 90")
+ XCTAssertEqual(formattedText, "+7 (999) 553 48 20")
+ }
+
+ func testBirthdayCertificateRegex() {
+ // given
+ let regex = "([A-Z]{2})-?([А-Я]{2}) ?№? ?(\\d{6})"
+ let inputText = "ABЮЯ689323"
+ let formatter = TextFormatter(regex: regex)
+
+ // when
+ let regexReplacement = formatter.getRegexReplacement()
+ let regexPlaceholder = formatter.getRegexPlaceholder()
+ let formattedText = formatter.getFormattedText(inputText)
+
+ // then
+ XCTAssertEqual(regexReplacement, "$1-$2 № $3")
+ XCTAssertEqual(regexPlaceholder, "AB-АБ № 123456")
+ XCTAssertEqual(formattedText, "AB-ЮЯ № 689323")
+ }
+
+ func testRoubleSumRegex() {
+ // given
+ let regex = "(\\d+)([.,]\\d+)? ?₽?"
+ let inputText = "1234.56"
+ let formatter = TextFormatter(regex: regex)
+
+ // when
+ let regexReplacement = formatter.getRegexReplacement()
+ let regexPlaceholder = formatter.getRegexPlaceholder()
+ let formattedText = formatter.getFormattedText(inputText)
+
+ // then
+ XCTAssertEqual(regexReplacement, "$1$2 ₽")
+ XCTAssertEqual(regexPlaceholder, "1.2 ₽")
+ XCTAssertEqual(formattedText, "1234.56 ₽")
+ }
+}