|
1 | 1 | /*
|
2 | 2 | This source file is part of the Swift.org open source project
|
3 | 3 |
|
4 |
| - Copyright (c) 2018 Apple Inc. and the Swift project authors |
| 4 | + Copyright (c) 2018 - 2021 Apple Inc. and the Swift project authors |
5 | 5 | Licensed under Apache License v2.0 with Runtime Library Exception
|
6 | 6 |
|
7 | 7 | See http://swift.org/LICENSE.txt for license information
|
8 | 8 | See http://swift.org/CONTRIBUTORS.txt for Swift project authors
|
9 | 9 | */
|
10 | 10 |
|
11 | 11 | extension Version: ExpressibleByStringLiteral {
|
12 |
| - |
13 | 12 | /// Initializes a version struct with the provided string literal.
|
14 |
| - /// |
15 |
| - /// - Parameters: |
16 |
| - /// - version: A string literal to use for creating a new version struct. |
| 13 | + /// - Parameter version: A string literal to use for creating a new version struct. |
17 | 14 | public init(stringLiteral value: String) {
|
18 | 15 | if let version = Version(value) {
|
19 |
| - self.init(version) |
| 16 | + self = version |
20 | 17 | } else {
|
21 | 18 | // If version can't be initialized using the string literal, report
|
22 | 19 | // the error and initialize with a dummy value. This is done to
|
@@ -44,51 +41,50 @@ extension Version: ExpressibleByStringLiteral {
|
44 | 41 | }
|
45 | 42 | }
|
46 | 43 |
|
47 |
| -extension Version { |
48 |
| - |
49 |
| - /// Initializes a version struct with the provided version. |
50 |
| - /// |
51 |
| - /// - Parameters: |
52 |
| - /// - version: A version object to use for creating a new version struct. |
53 |
| - public init(_ version: Version) { |
54 |
| - major = version.major |
55 |
| - minor = version.minor |
56 |
| - patch = version.patch |
57 |
| - prereleaseIdentifiers = version.prereleaseIdentifiers |
58 |
| - buildMetadataIdentifiers = version.buildMetadataIdentifiers |
59 |
| - } |
60 |
| - |
| 44 | +extension Version: LosslessStringConvertible { |
61 | 45 | /// Initializes a version struct with the provided version string.
|
62 |
| - /// |
63 |
| - /// - Parameters: |
64 |
| - /// - version: A version string to use for creating a new version struct. |
| 46 | + /// - Parameter version: A version string to use for creating a new version struct. |
65 | 47 | public init?(_ versionString: String) {
|
66 |
| - let prereleaseStartIndex = versionString.firstIndex(of: "-") |
67 |
| - let metadataStartIndex = versionString.firstIndex(of: "+") |
68 |
| - |
69 |
| - let requiredEndIndex = prereleaseStartIndex ?? metadataStartIndex ?? versionString.endIndex |
70 |
| - let requiredCharacters = versionString.prefix(upTo: requiredEndIndex) |
71 |
| - let requiredComponents = requiredCharacters |
72 |
| - .split(separator: ".", maxSplits: 2, omittingEmptySubsequences: false) |
73 |
| - .map(String.init) |
74 |
| - .compactMap({ Int($0) }) |
75 |
| - .filter({ $0 >= 0 }) |
76 |
| - |
77 |
| - guard requiredComponents.count == 3 else { return nil } |
78 |
| - |
79 |
| - self.major = requiredComponents[0] |
80 |
| - self.minor = requiredComponents[1] |
81 |
| - self.patch = requiredComponents[2] |
82 |
| - |
83 |
| - func identifiers(start: String.Index?, end: String.Index) -> [String] { |
84 |
| - guard let start = start else { return [] } |
85 |
| - let identifiers = versionString[versionString.index(after: start)..<end] |
86 |
| - return identifiers.split(separator: ".").map(String.init) |
| 48 | + // SemVer 2.0.0 allows only ASCII alphanumerical characters and "-" in the version string, except for "." and "+" as delimiters. ("-" is used as a delimiter between the version core and pre-release identifiers, but it's allowed within pre-release and metadata identifiers as well.) |
| 49 | + // Alphanumerics check will come later, after each identifier is split out (i.e. after the delimiters are removed). |
| 50 | + guard versionString.allSatisfy(\.isASCII) else { return nil } |
| 51 | + |
| 52 | + let metadataDelimiterIndex = versionString.firstIndex(of: "+") |
| 53 | + // SemVer 2.0.0 requires that pre-release identifiers come before build metadata identifiers |
| 54 | + let prereleaseDelimiterIndex = versionString[..<(metadataDelimiterIndex ?? versionString.endIndex)].firstIndex(of: "-") |
| 55 | + |
| 56 | + let versionCore = versionString[..<(prereleaseDelimiterIndex ?? metadataDelimiterIndex ?? versionString.endIndex)] |
| 57 | + let versionCoreIdentifiers = versionCore.split(separator: ".", omittingEmptySubsequences: false) |
| 58 | + |
| 59 | + guard |
| 60 | + versionCoreIdentifiers.count == 3, |
| 61 | + // Major, minor, and patch versions must be ASCII numbers, according to the semantic versioning standard. |
| 62 | + // Converting each identifier from a substring to an integer doubles as checking if the identifiers have non-numeric characters. |
| 63 | + let major = Int(versionCoreIdentifiers[0]), |
| 64 | + let minor = Int(versionCoreIdentifiers[1]), |
| 65 | + let patch = Int(versionCoreIdentifiers[2]) |
| 66 | + else { return nil } |
| 67 | + |
| 68 | + self.major = major |
| 69 | + self.minor = minor |
| 70 | + self.patch = patch |
| 71 | + |
| 72 | + if let prereleaseDelimiterIndex = prereleaseDelimiterIndex { |
| 73 | + let prereleaseStartIndex = versionString.index(after: prereleaseDelimiterIndex) |
| 74 | + let prereleaseIdentifiers = versionString[prereleaseStartIndex..<(metadataDelimiterIndex ?? versionString.endIndex)].split(separator: ".", omittingEmptySubsequences: false) |
| 75 | + guard prereleaseIdentifiers.allSatisfy( { $0.allSatisfy { $0.isLetter || $0.isNumber || $0 == "-" } } ) else { return nil } |
| 76 | + self.prereleaseIdentifiers = prereleaseIdentifiers.map { String($0) } |
| 77 | + } else { |
| 78 | + self.prereleaseIdentifiers = [] |
| 79 | + } |
| 80 | + |
| 81 | + if let metadataDelimiterIndex = metadataDelimiterIndex { |
| 82 | + let metadataStartIndex = versionString.index(after: metadataDelimiterIndex) |
| 83 | + let buildMetadataIdentifiers = versionString[metadataStartIndex...].split(separator: ".", omittingEmptySubsequences: false) |
| 84 | + guard buildMetadataIdentifiers.allSatisfy( { $0.allSatisfy { $0.isLetter || $0.isNumber || $0 == "-" } } ) else { return nil } |
| 85 | + self.buildMetadataIdentifiers = buildMetadataIdentifiers.map { String($0) } |
| 86 | + } else { |
| 87 | + self.buildMetadataIdentifiers = [] |
87 | 88 | }
|
88 |
| - |
89 |
| - self.prereleaseIdentifiers = identifiers( |
90 |
| - start: prereleaseStartIndex, |
91 |
| - end: metadataStartIndex ?? versionString.endIndex) |
92 |
| - self.buildMetadataIdentifiers = identifiers(start: metadataStartIndex, end: versionString.endIndex) |
93 | 89 | }
|
94 | 90 | }
|
0 commit comments