CommandLineToolkit
72 строки · 3.1 Кб
1import Foundation
2
3class NumberValidator {
4private static let unsupportedCharacterSet = CharacterSet(charactersIn: "-+0123456789eE.").inverted
5private static let exponentialSeparator = CharacterSet(charactersIn: "eE")
6
7static let numberFormatter: NumberFormatter = {
8let formatter = NumberFormatter()
9formatter.numberStyle = .decimal
10formatter.locale = Locale(identifier: "en_US")
11return formatter
12}()
13
14enum `Error`: Swift.Error {
15case invalidNumber(String)
16case invalidIntPart(String)
17case invalidFractionalPart(String)
18case invalidExponentialPart(String)
19}
20
21static func validateStringRepresentationOfNumber(_ string: String) throws -> NSNumber {
22guard string.rangeOfCharacter(from: unsupportedCharacterSet) == nil else {
23throw Error.invalidNumber(string)
24}
25
26let parts = string.components(separatedBy: ".")
27
28if parts.count == 1 {
29let intAndExponentialParts = parts[0].components(separatedBy: exponentialSeparator)
30try validateIntPartOfNumber(intAndExponentialParts[0])
31if intAndExponentialParts.count == 2 {
32try validateExponentialPartOfNumber(intAndExponentialParts[1])
33} else if intAndExponentialParts.count > 2 {
34throw Error.invalidNumber(string)
35}
36} else if parts.count == 2 {
37try validateIntPartOfNumber(parts[0])
38let fractionalAndExponentialParts = parts[1].components(separatedBy: exponentialSeparator)
39try validateFractionalPartOfNumber(fractionalAndExponentialParts[0])
40if fractionalAndExponentialParts.count == 2 {
41try validateExponentialPartOfNumber(fractionalAndExponentialParts[1])
42} else if fractionalAndExponentialParts.count > 2 {
43throw Error.invalidNumber(string)
44}
45} else {
46throw Error.invalidNumber(string)
47}
48
49if let number = numberFormatter.number(from: string) {
50return number
51} else {
52throw Error.invalidNumber(string)
53}
54}
55
56static func validateIntPartOfNumber(_ string: String) throws {
57var intPart = string
58if intPart.starts(with: "-") { intPart.removeFirst() }
59guard intPart.rangeOfCharacter(from: CharacterSet(charactersIn: "+eE")) == nil else { throw Error.invalidIntPart(string) }
60guard !intPart.isEmpty else { throw Error.invalidIntPart(string) }
61if intPart.contains("+") { throw Error.invalidIntPart(string) }
62if intPart.starts(with: "0") && intPart.count > 1 { throw Error.invalidIntPart(string) }
63}
64
65static func validateFractionalPartOfNumber(_ fractionalPart: String) throws {
66guard !fractionalPart.isEmpty else { throw Error.invalidFractionalPart(fractionalPart) }
67}
68
69static func validateExponentialPartOfNumber(_ exponentialPart: String) throws {
70guard !exponentialPart.isEmpty else { throw Error.invalidExponentialPart(exponentialPart) }
71}
72}
73