@@ -103,6 +103,75 @@ extension BSONValue where Self: Equatable {
103103 }
104104}
105105
106+ /// A protocol that numeric `BSONValue`s should conform to. It provides functionality for converting to BSON's native
107+ /// number types.
108+ public protocol BSONNumber : BSONValue {
109+ /// Create an `Int` from this `BSONNumber`.
110+ /// This will return nil if the conversion cannot result in an exact representation.
111+ var intValue : Int ? { get }
112+
113+ /// Create an `Int32` from this `BSONNumber`.
114+ /// This will return nil if the conversion cannot result in an exact representation.
115+ var int32Value : Int32 ? { get }
116+
117+ /// Create an `Int64` from this `BSONNumber`.
118+ /// This will return nil if the conversion cannot result in an exact representation.
119+ var int64Value : Int64 ? { get }
120+
121+ /// Create a `Double` from this `BSONNumber`.
122+ /// This will return nil if the conversion cannot result in an exact representation.
123+ var doubleValue : Double ? { get }
124+
125+ /// Create a `Decimal128` from this `BSONNumber`.
126+ /// This will return nil if the conversion cannot result in an exact representation.
127+ var decimal128Value : Decimal128 ? { get }
128+ }
129+
130+ /// Default conformance to `BSONNumber` for `BinaryInteger`s.
131+ extension BSONNumber where Self: BinaryInteger {
132+ /// Create an `Int` from this `BinaryInteger`.
133+ /// This will return nil if the conversion cannot result in an exact representation.
134+ public var intValue : Int ? { return Int ( exactly: self ) }
135+
136+ /// Create an `Int32` from this `BinaryInteger`.
137+ /// This will return nil if the conversion cannot result in an exact representation.
138+ public var int32Value : Int32 ? { return Int32 ( exactly: self ) }
139+
140+ /// Create an `Int64` from this `BinaryInteger`.
141+ /// This will return nil if the conversion cannot result in an exact representation.
142+ public var int64Value : Int64 ? { return Int64 ( exactly: self ) }
143+
144+ /// Create a `Double` from this `BinaryInteger`.
145+ /// This will return nil if the conversion cannot result in an exact representation.
146+ public var doubleValue : Double ? { return Double ( exactly: self ) }
147+ }
148+
149+ /// Default conformance to `BSONNumber` for `BinaryFloatingPoint`s.
150+ extension BSONNumber where Self: BinaryFloatingPoint {
151+ /// Create an `Int` from this `BinaryFloatingPoint`.
152+ /// This will return nil if the conversion cannot result in an exact representation.
153+ public var intValue : Int ? { return Int ( exactly: self ) }
154+
155+ /// Create an `Int32` from this `BinaryFloatingPoint`.
156+ /// This will return nil if the conversion cannot result in an exact representation.
157+ public var int32Value : Int32 ? { return Int32 ( exactly: self ) }
158+
159+ /// Create an `Int64` from this `BinaryFloatingPoint`.
160+ /// This will return nil if the conversion cannot result in an exact representation.
161+ public var int64Value : Int64 ? { return Int64 ( exactly: self ) }
162+
163+ /// Create a `Double` from this `BinaryFloatingPoint`.
164+ /// This will return nil if the conversion cannot result in an exact representation.
165+ public var doubleValue : Double ? { return Double ( self ) }
166+ }
167+
168+ /// Default implementation of `Decimal128` conversions for all `Numeric`s.
169+ extension BSONNumber where Self: Numeric {
170+ /// Create a `Decimal128` from this `Numeric`.
171+ /// This will return nil if the conversion cannot result in an exact representation.
172+ public var decimal128Value : Decimal128 ? { return Decimal128 ( String ( describing: self ) ) }
173+ }
174+
106175/// An extension of `Array` to represent the BSON array type.
107176extension Array : BSONValue {
108177 public var bsonType : BSONType { return . array }
@@ -427,7 +496,7 @@ public struct DBPointer: BSONValue, Codable, Equatable {
427496}
428497
429498/// A struct to represent the BSON Decimal128 type.
430- public struct Decimal128 : BSONValue , Equatable , Codable , CustomStringConvertible {
499+ public struct Decimal128 : BSONNumber , Equatable , Codable , CustomStringConvertible {
431500 public var bsonType : BSONType { return . decimal128 }
432501
433502 public var description : String {
@@ -502,8 +571,32 @@ public struct Decimal128: BSONValue, Equatable, Codable, CustomStringConvertible
502571 }
503572}
504573
574+ /// Extension of `Decimal128` to add `BSONNumber` conformance.
575+ /// TODO: implement the missing converters (SWIFT-367)
576+ extension Decimal128 {
577+ /// Create an `Int` from this `Decimal128`.
578+ /// Note: this function is not implemented yet and will always return nil.
579+ public var intValue : Int ? { return nil }
580+
581+ /// Create an `Int32` from this `Decimal128`.
582+ /// Note: this function is not implemented yet and will always return nil.
583+ public var int32Value : Int32 ? { return nil }
584+
585+ /// Create an `Int64` from this `Decimal128`.
586+ /// Note: this function is not implemented yet and will always return nil.
587+ public var int64Value : Int64 ? { return nil }
588+
589+ /// Create a `Double` from this `Decimal128`.
590+ /// Note: this function is not implemented yet and will always return nil.
591+ public var doubleValue : Double ? { return nil }
592+
593+ /// Returns this `Decimal128`.
594+ /// This is implemented as part of `BSONNumber` conformance.
595+ public var decimal128Value : Decimal128 ? { return self }
596+ }
597+
505598/// An extension of `Double` to represent the BSON Double type.
506- extension Double : BSONValue {
599+ extension Double : BSONNumber {
507600 public var bsonType : BSONType { return . double }
508601
509602 public func encode( to storage: DocumentStorage , forKey key: String ) throws {
@@ -524,39 +617,66 @@ extension Double: BSONValue {
524617}
525618
526619/// An extension of `Int` to represent the BSON Int32 or Int64 type.
527- /// The `Int` will be encoded as an Int32 if possible, or an Int64 if necessary.
528- extension Int : BSONValue {
529- public var bsonType : BSONType { return self . int32Value != nil ? . int32 : . int64 }
620+ /// On 64-bit systems, `Int` corresponds to a BSON Int64. On 32-bit systems, it corresponds to a BSON Int32.
621+ extension Int : BSONNumber {
622+ /// `Int` corresponds to a BSON int32 or int64 depending upon whether the compilation system is 32 or 64 bit.
623+ /// Use MemoryLayout instead of Int.bitWidth to avoid a compiler warning.
624+ /// See: https://forums.swift.org/t/how-can-i-condition-on-the-size-of-int/9080/4
625+ internal static var bsonType : BSONType {
626+ return MemoryLayout< Int> . size == 4 ? . int32 : . int64
627+ }
628+
629+ public var bsonType : BSONType { return Int . bsonType }
530630
531- internal var int32Value : Int32 ? { return Int32 ( exactly: self ) }
532- internal var int64Value : Int64 ? { return Int64 ( exactly: self ) }
631+ // Return this `Int` as an `Int32` on 32-bit systems or an `Int64` on 64-bit systems
632+ internal var typedValue : BSONNumber {
633+ if self . bsonType == . int64 {
634+ return Int64 ( self )
635+ }
636+ return Int32 ( self )
637+ }
533638
534639 public func encode( to storage: DocumentStorage , forKey key: String ) throws {
535- if let int32 = self . int32Value {
536- return try int32. encode ( to: storage, forKey: key)
640+ try self . typedValue. encode ( to: storage, forKey: key)
641+ }
642+
643+ public func bsonEquals( _ other: BSONValue ? ) -> Bool {
644+ guard let other = other, other. bsonType == self . bsonType else {
645+ return false
537646 }
538- if let int64 = self . int64Value {
539- return try int64. encode ( to: storage, forKey: key)
647+
648+ if let otherInt = other as? Int {
649+ return self == otherInt
540650 }
541651
542- throw RuntimeError . internalError ( message: " `Int` value \( self ) could not be encoded as `Int32` or `Int64` " )
652+ switch ( self . typedValue, other) {
653+ case let ( self32 as Int32 , other32 as Int32 ) :
654+ return self32 == other32
655+ case let ( self64 as Int64 , other64 as Int64 ) :
656+ return self64 == other64
657+ default :
658+ return false
659+ }
543660 }
544661
545662 public static func from( iterator iter: DocumentIterator ) throws -> Int {
546- return try iter. withBSONIterPointer { iterPtr in
547- // TODO: handle this more gracefully (SWIFT-221)
548- switch iter. currentType {
549- case . int32, . int64:
550- return self . init ( Int ( bson_iter_int32 ( iterPtr) ) )
551- default :
552- throw wrongIterTypeError ( iter, expected: Int . self)
553- }
663+ var val : Int ?
664+ if Int . bsonType == . int64 {
665+ val = Int ( exactly: try Int64 . from ( iterator: iter) )
666+ } else {
667+ val = Int ( exactly: try Int32 . from ( iterator: iter) )
668+ }
669+
670+ guard let out = val else {
671+ // This should not occur
672+ throw RuntimeError . internalError ( message: " Couldn't read `Int` from Document " )
554673 }
674+ return out
555675 }
556676}
557677
558678/// An extension of `Int32` to represent the BSON Int32 type.
559- extension Int32 : BSONValue {
679+ extension Int32 : BSONNumber {
560680 public var bsonType : BSONType { return . int32 }
561681
562682 public func encode( to storage: DocumentStorage , forKey key: String ) throws {
@@ -574,10 +694,19 @@ extension Int32: BSONValue {
574694 self . init ( bson_iter_int32 ( iterPtr) )
575695 }
576696 }
697+
698+ public func bsonEquals( _ other: BSONValue ? ) -> Bool {
699+ if let other32 = other as? Int32 {
700+ return self == other32
701+ } else if let otherInt = other as? Int {
702+ return self == otherInt. typedValue as? Int32
703+ }
704+ return false
705+ }
577706}
578707
579708/// An extension of `Int64` to represent the BSON Int64 type.
580- extension Int64 : BSONValue {
709+ extension Int64 : BSONNumber {
581710 public var bsonType : BSONType { return . int64 }
582711
583712 public func encode( to storage: DocumentStorage , forKey key: String ) throws {
@@ -595,6 +724,15 @@ extension Int64: BSONValue {
595724 self . init ( bson_iter_int64 ( iterPtr) )
596725 }
597726 }
727+
728+ public func bsonEquals( _ other: BSONValue ? ) -> Bool {
729+ if let other64 = other as? Int64 {
730+ return self == other64
731+ } else if let otherInt = other as? Int {
732+ return self == otherInt. typedValue as? Int64
733+ }
734+ return false
735+ }
598736}
599737
600738/// A struct to represent the BSON Code and CodeWithScope types.
0 commit comments