         }
     }
     
-    public enum IntegerDecodingStrategy<I: Integer> {
+    public enum IntegerDecodingStrategy<I: FixedWidthInteger> {
         case string
         case int32
         case int64
         case anyInteger
         case roundingAnyNumber
+        case stringOrNumber
         case custom(DecodingStrategy<I>)
+        
+        fileprivate func convert(from string: String) throws -> I {
+            if I.isSigned {
+                guard let int = Int64(string) else {
+                    throw BSONTypeConversionError(from: string, to: I.self)
+                }
+                
+                return numericCast(int)
+            } else {
+                guard let int = UInt64(string) else {
+                    throw BSONTypeConversionError(from: string, to: I.self)
+                }
+                
+                return numericCast(int)
+            }
+        }
+        
+        fileprivate func decode(
+            from decoder: _BSONDecoder,
+            path: @autoclosure () -> [String]
+        ) throws -> I {
+            switch self {
+            case .string, .stringOrNumber:
+                if case .stringOrNumber = self {
+                    guard decoder.identifier == .string else {
+                        throw BSONTypeConversionError(from: decoder.primitive, to: I.self)
+                    }
+                }
+                
+                return try convert(from: decoder.wrapped.unwrap(asType: String.self, path: path()))
+            case .int32:
+                let int = try decoder.wrapped.unwrap(asType: Int32.self, path: path())
+                return try int.convert(to: I.self)
+            case .int64:
+                let int = try decoder.wrapped.unwrap(asType: Int64.self, path: path())
+                return try int.convert(to: I.self)
+            case .anyInteger, .roundingAnyNumber:
+                guard let type = decoder.identifier else {
+                    throw BSONValueNotFound(type: I.self, path: path())
+                }
+                
+                switch (type, self) {
+                case (.int32, _):
+                    let int = try decoder.wrapped.unwrap(asType: Int32.self, path: path())
+                    return try int.convert(to: I.self)
+                case (.int64, _):
+                    // Necessary also for custom integer types
+                    let int = try decoder.wrapped.unwrap(asType: Int64.self, path: path())
+                    return try int.convert(to: I.self)
+                case (.double, .roundingAnyNumber):
+                    let double = try decoder.wrapped.unwrap(asType: Double.self, path: path())
+                    return I(double)
+                default:
+                    throw BSONTypeConversionError(from: decoder.primitive, to: I.self)
+                }
+            case .custom(let strategy):
+                guard let value: I = try strategy(decoder.primitive) else {
+                    throw BSONValueNotFound(type: I.self, path: path())
+                }
+                
+                return value
+            }
+        }
+            
+        fileprivate func decode<K: CodingKey>(
+            from decoder: _BSONDecoder,
+            forKey key: K,
+            path: @autoclosure () -> [String]
+        ) throws -> I {
+            switch self {
+            case .string, .stringOrNumber:
+                if case .stringOrNumber = self {
+                    guard decoder.identifier == .string else {
+                        throw BSONTypeConversionError(from: decoder.primitive, to: I.self)
+                    }
+                }
+                
+                return try convert(from: decoder.wrapped.unwrap(asType: String.self, atKey: key, path: path()))
+            case .int32:
+                let int = try decoder.wrapped.unwrap(asType: Int32.self, atKey: key, path: path())
+                return try int.convert(to: I.self)
+            case .int64:
+                let int = try decoder.wrapped.unwrap(asType: Int64.self, atKey: key, path: path())
+                return try int.convert(to: I.self)
+            case .anyInteger, .roundingAnyNumber:
+                guard let type = decoder.document?.typeIdentifier(of: key.stringValue) else {
+                    throw BSONValueNotFound(type: I.self, path: path())
+                }
+                
+                switch (type, self) {
+                case (.int32, _):
+                    let int = try decoder.wrapped.unwrap(asType: Int32.self, atKey: key, path: path())
+                    return try int.convert(to: I.self)
+                case (.int64, _):
+                    // Necessary also for custom integer types
+                    let int = try decoder.wrapped.unwrap(asType: Int64.self, atKey: key, path: path())
+                    return try int.convert(to: I.self)
+                case (.double, .roundingAnyNumber):
+                    let double = try decoder.wrapped.unwrap(asType: Double.self, atKey: key, path: path())
+                    return I(double)
+                default:
+                    throw BSONTypeConversionError(from: decoder.document?[key.stringValue], to: I.self)
+                }
+            case .custom(let strategy):
+                guard let value: I = try strategy(decoder.document?[key.stringValue]) else {
+                    throw BSONValueNotFound(type: I.self, path: path())
+                }
+                
+                return value
+            }
+        }
     }
     
     public enum DoubleDecodingStrategy {

                
                switch (identifier, self) {
                case (.string, .textual), (.string, .numericAndTextual):
                    double = try Double(decoder.wrapped.decode(asType: String.self, path: path))
                case (.double, _):
                    double = try decoder.wrapped.decode(asType: Double.self, path: path)
                case (.int32, .numerical), (.int32, .numericAndTextual):
                    double = try Double(decoder.wrapped.decode(asType: Int32.self, path: path))
                case (.int64, .numerical), (.int64, .numericAndTextual):
                    double = try Double(decoder.wrapped.decode(asType: Int64.self, path: path))
                default:
                    throw BSONTypeConversionError(from: decoder.primitive, to: Double.self)
                }

    }
    
    func decode(_ type: Int.Type, forKey key: K) throws -> Int {
        return try self.decoder.settings.intDecodingStrategy.decode(from: self.decoder, forKey: key, path: path(forKey: key))
    }
    
    func decode(_ type: Int8.Type, forKey key: K) throws -> Int8 {
        return try self.decoder.settings.int8DecodingStrategy.decode(from: self.decoder, forKey: key, path: path(forKey: key))
    }
    
    func decode(_ type: Int16.Type, forKey key: K) throws -> Int16 {
        return try self.decoder.settings.int16DecodingStrategy.decode(from: self.decoder, forKey: key, path: path(forKey: key))
    }
    
    func decode(_ type: Int32.Type, forKey key: K) throws -> Int32 {
        return try self.decoder.settings.int32DecodingStrategy.decode(from: self.decoder, forKey: key, path: path(forKey: key))
    }
    
    func decode(_ type: Int64.Type, forKey key: K) throws -> Int64 {
        return try self.decoder.settings.int64DecodingStrategy.decode(from: self.decoder, forKey: key, path: path(forKey: key))
    }
    
    func decode(_ type: UInt.Type, forKey key: K) throws -> UInt {
        return try self.decoder.settings.uintDecodingStrategy.decode(from: self.decoder, forKey: key, path: path(forKey: key))
    }
    
    func decode(_ type: UInt8.Type, forKey key: K) throws -> UInt8 {
        return try self.decoder.settings.uint8DecodingStrategy.decode(from: self.decoder, forKey: key, path: path(forKey: key))
    }
    
    func decode(_ type: UInt16.Type, forKey key: K) throws -> UInt16 {
        return try self.decoder.settings.uint16DecodingStrategy.decode(from: self.decoder, forKey: key, path: path(forKey: key))
    }
    
    func decode(_ type: UInt32.Type, forKey key: K) throws -> UInt32 {
        return try self.decoder.settings.uint32DecodingStrategy.decode(from: self.decoder, forKey: key, path: path(forKey: key))
    }
    
    func decode(_ type: UInt64.Type, forKey key: K) throws -> UInt64 {
        return try self.decoder.settings.uint64DecodingStrategy.decode(from: self.decoder, forKey: key, path: path(forKey: key))
    }
    
    func decode<T>(_ type: T.Type, forKey key: K) throws -> T where T : Decodable {
        <#code#>
    }
    
    func nestedContainer<NestedKey>(keyedBy type: NestedKey.Type, forKey key: K) throws -> KeyedDecodingContainer<NestedKey> where NestedKey : CodingKey {
