@@ -277,11 +277,22 @@ extension SemanticVersion: Comparable {
277277 }
278278
279279
280+ /// Determines whether the given two semantic versions are equivalent. Equivalence is implied by the precedence
281+ /// rules laid out in SemVer 2.0.0 paragraph 11: https://semver.org/spec/v2.0.0.html#spec-item-11
282+ ///
283+ /// Note that they don't have to be __equal__, in the traditional sense. For instance, `"1.0" == "1.0.0"` and
284+ /// `"1.2.3+45" == "1.2.3+67"`.
285+ ///
286+ /// - Parameters:
287+ /// - lhs: The first version to compare
288+ /// - rhs: The second version to compare
289+ /// - Returns: `true` if the two versions are equivalent
280290 public static func == ( lhs: SemanticVersion , rhs: SemanticVersion ) -> Bool {
281291 return lhs. major == rhs. major
282292 && lhs. minor == rhs. minor
283293 && isEquivalent ( lhs. patch, rhs. patch, isEquivalentToNil: { $0 == 0 } )
284294 && isEquivalent ( lhs. preRelease, rhs. preRelease, isEquivalentToNil: { $0 == " " } )
295+ // According to https://semver.org/spec/v2.0.0.html#spec-item-11, "Build metadata does not figure into precedence"
285296 }
286297}
287298
@@ -322,44 +333,76 @@ public extension SemanticVersion.Extension {
322333
323334
324335
336+ /// Creates a string of period-separated identifiers, so `["public", "RC", "1"]` would become `"public.RC.1"`
325337 public var description : String {
326338 return identifiers. joined ( separator: " . " )
327339 }
340+
341+
342+ /// Creates a string suitable for naïve concatenation with a semantic version string.
343+ /// For example, a pre-release extension like `["RC", "1"]` would become `"-RC.1"`, and a build like
344+ /// `["2018", "01", "15"]` would become `"+2018.01.15"`.
328345 public var descriptionWithPrefix : String {
329346 return " \( type ( of: self ) . prefix) \( description) "
330347 }
331348
332349
350+ /// Creates a new extension by converting the given array into an array of strings
351+ ///
352+ /// - Parameter identifiers: Soon-to-be identifiers
333353 public init ( identifiers: [ CustomStringConvertible ] ) {
334354 self . init ( identifiers: identifiers. map { $0. description } )
335355 }
336356
337357
358+ #if swift(>=4)
359+ /// Creates a new extension by separating the given raw string by any periods in it
360+ ///
361+ /// - Parameter rawString: A raw representation of a semantic version string.
362+ /// This should match the regex `/^[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*$/`.
338363 public init ( _ rawString: Substring ) {
339364 self . init ( identifiers: rawString. split ( separator: " . " ) . map { String ( $0) } )
340365 }
366+ #endif
341367
342368
369+ /// Creates a new extension by separating the given raw string by any periods in it
370+ ///
371+ /// - Parameter rawString: A raw representation of a semantic version string.
372+ /// This should match the regex `/^[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*$/`.
343373 public init ( _ rawString: String ) {
344374 self . init ( identifiers: rawString. split ( separator: " . " ) . map { String ( $0) } )
345375 }
346376
347377
378+ /// Creates a new extension by converting the given varargs into an array of strings
379+ ///
380+ /// - Parameter identifiers: Soon-to-be identifiers
348381 public init ( _ identifiers: CustomStringConvertible ... ) {
349382 self . init ( identifiers: identifiers)
350383 }
351384
352385
386+ /// Creates a new extension by converting the given array/varargs into an array of strings
387+ ///
388+ /// - Parameter identifiers: Soon-to-be identifiers
353389 public init ( arrayLiteral elements: CustomStringConvertible ... ) {
354390 self . init ( identifiers: elements)
355391 }
356392
357393
394+ /// Creaetes a new extension by converting the given integer into a string and assuming that is the only identifier
395+ ///
396+ /// - Parameter value: The only identifier, to become a string
358397 public init ( integerLiteral value: UInt ) {
359- self . init ( value)
398+ self . init ( identifiers : [ value] )
360399 }
361400
362401
402+ /// Creates a new extension by separating the given raw string by any periods in it
403+ ///
404+ /// - Parameter rawString: A raw representation of a semantic version string.
405+ /// This should match the regex `/^[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*$/`.
363406 public init ( stringLiteral value: String ) {
364407 self . init ( value)
365408 }
@@ -369,11 +412,27 @@ public extension SemanticVersion.Extension {
369412
370413public extension SemanticVersion . Extension {
371414
415+ /// Determines whether the given two SemVer extensions are in ascending order. This attempts to exactly match the
416+ /// behavior described in the SemVer 2.0.0 spec paragraph 11: https://semver.org/spec/v2.0.0.html#spec-item-11
417+ ///
418+ /// - Parameters:
419+ /// - lhs: The first extension to compare
420+ /// - rhs: The second extension to compare
421+ /// - Returns: `true` iff `lhs` is less than, or has lower precedence than, `rhs`. If they have the same
422+ /// precedence, this will still return `false`.
423+ /// - Note: To determine the exact order, use `compare(lhs:rhs:)`
372424 public static func < ( lhs: Self , rhs: Self ) -> Bool {
373425 return compare ( lhs: lhs, rhs: rhs) == . orderedAscending
374426 }
375427
376428
429+ /// Compares two SemVer extensions and returns the determined order. This attempts to exactly match the
430+ /// behavior described in the SemVer 2.0.0 spec paragraph 11: https://semver.org/spec/v2.0.0.html#spec-item-11
431+ ///
432+ /// - Parameters:
433+ /// - lhs: The first extension to compare
434+ /// - rhs: The second extension to compare
435+ /// - Returns: The order of the extensions
377436 public static func compare( lhs: Self , rhs: Self ) -> ComparisonResult {
378437
379438 if lhs == rhs {
@@ -424,6 +483,12 @@ public extension SemanticVersion.Extension {
424483 }
425484
426485
486+ /// Determines whether one SemVer 2.0.0 extension is equal to another
487+ ///
488+ /// - Parameters:
489+ /// - lhs: One extension
490+ /// - rhs: Another extension
491+ /// - Returns: `true` iff the given two extensions are equal
427492 public static func == ( lhs: Self , rhs: Self ) -> Bool {
428493 let ( lhsIds, rhsIds) = ( lhs. identifiers, rhs. identifiers)
429494 return lhsIds. count == rhsIds. count && !lhsIds. zip ( with: rhsIds) . contains ( where: { $0. 0 != $0. 1 } )
@@ -434,25 +499,56 @@ public extension SemanticVersion.Extension {
434499
435500private extension NSTextCheckingResult {
436501
437- func group( _ groupIndex: Int , in string: String ) -> Substring ? {
502+ #if swift(>=4)
503+ typealias StringOrSubstring = Substring
504+ #else
505+ typealias StringOrSubstring = String
506+ #endif
507+
508+ /// Finds the group located at the given index in this text checking result, within the given string
509+ ///
510+ /// - Parameters:
511+ /// - groupIndex: The index of the group
512+ /// - string: The string to search
513+ /// - Returns: The substring at the given group index, or `nil` if there is none
514+ func group( _ groupIndex: Int , in string: String ) -> StringOrSubstring ? {
438515 guard let range = self . range ( at: groupIndex) . orNil else {
439516 return nil
440517 }
441518 return _group ( range: range, in: string)
442519 }
443520
521+ /// Finds the group with the given name in this text checking result, within the given string
522+ ///
523+ /// - Parameters:
524+ /// - groupName: The name of the group
525+ /// - string: The string to search
526+ /// - Returns: The substring at the given group index, or `nil` if there is none
444527 @available ( macOS 10 . 13 , iOS 11 , tvOS 11 , watchOS 4 , * )
445- func group( _ groupName: String , in string: String ) -> Substring ? {
528+ func group( _ groupName: String , in string: String ) -> StringOrSubstring ? {
446529 guard let range = self . range ( withName: groupName) . orNil else {
447530 return nil
448531 }
449532 return _group ( range: range, in: string)
450533 }
451534
452535
453- private func _group( range: NSRange , in string: String ) -> Substring ? {
536+ /// The base of the other `group(_:in:)` functions; simply returns the substring of the given string at the given
537+ /// range, iff that range is valid. If the range is invalid (e.g. if it's not inside the string or location is `NSNotFound`)
538+ ///
539+ /// - Parameters:
540+ /// - range: The range of characters to return
541+ /// - string: The string to search
542+ /// - Returns: The substring at the given range, or `nil` if there is none
543+ private func _group( range: NSRange , in string: String ) -> StringOrSubstring ? {
544+ guard range. location != NSNotFound else {
545+ return nil
546+ }
454547 let startingIndex = string. index ( string. startIndex, offsetBy: range. location)
455548 let endingIndex = string. index ( startingIndex, offsetBy: range. length)
549+ guard string. endIndex >= endingIndex else {
550+ return nil
551+ }
456552 return string [ startingIndex..< endingIndex]
457553 }
458554}
0 commit comments