How does Storing Failable Results for Later / trailing closure syntax to call Result(catching:) work in Swift? - swift

Within chapter on error handling there is an example on expression evaluator and code on storing Failable Results for Later - code below, lines after implementation of function evaluate. This part does throw more errors, would like to ask if you can help to clarify these. I am going back from the beggining of the book again, checking sources and practicing more examples. Still appreciate your help, so when comming back can study it more.
enum Token: CustomStringConvertible {
case number (Int)
case plus
var description: String {
switch self {
case .number (let n):
return "Number: \(n)"
case .plus:
return "Symbol: +"
}
}
}
class Lexer {
enum Error: Swift.Error {
case invalidCharacter (Character)
}
let input: String
var position: String.Index
init(input: String) {
self.input = input
self.position = input.startIndex
}
func peek() -> Character? {
guard position < input.endIndex else {
return nil
}
return input[position]
}
func advance() {
assert(position < input.endIndex, "Cannot advance past endIndex!")
position = input.index(after: position)
}
func getNumber() -> Int {
var value = 0
while let nextCharacter = peek() {
switch nextCharacter {
case "0" ... "9":
let digitValue = Int(String(nextCharacter))!
value = 10*value + digitValue
advance()
default:
return value
}
}
return value
}
func lex() throws -> [Token] {
var tokens = [Token]()
while let nextCharacter = peek() {
switch nextCharacter {
case "0" ... "9":
let value = getNumber()
tokens.append(.number(value))
case "+":
tokens.append(.plus)
advance()
case " ":
advance()
default:
throw Lexer.Error.invalidCharacter(nextCharacter)
}
}
return tokens
}
}
func evaluate(_ input: String) {
print("Evaluating: \(input)")
let lexer = Lexer(input: input)
do {
let tokens = try lexer.lex()
print("Lexer output: \(tokens)")
let parser = Parser(tokens: tokens)
let result = try parser.parse()
print("Parser output: \(result)")
} catch Lexer.Error.invalidCharacter(let character) {
print("Input contained an invalid character: \(character)")
} catch Parser.Error.unexpectedEndOfInput {
print("Unexpected end of input during parsing")
} catch Parser.Error.invalidToken(let token) {
print("Invalid token during parsing: \(token)")
} catch {
print("An error occured: \(error)")
}
}
class Parser {
enum Error: Swift.Error {
case unexpectedEndOfInput
case invalidToken(Token)
}
let tokens: [Token]
var position = 0
init(tokens: [Token]) {
self.tokens = tokens
}
func getNextToken() -> Token? {
guard position < tokens.count else {
return nil
}
let token = tokens[position]
position += 1
return token
}
func getNumber() throws -> Int {
guard let token = getNextToken() else {
throw Parser.Error.unexpectedEndOfInput
}
switch token {
case .number(let value):
return value
case .plus:
throw Parser.Error.invalidToken(token)
}
}
func parse() throws -> Int {
var value = try getNumber()
while let token = getNextToken() {
switch token {
case .plus:
let nextNumber = try getNumber()
value += nextNumber
case .number:
throw Parser.Error.invalidToken(token)
}
}
return value
}
}
evaluate("10 + 1")
// below is code on Storing Failable Results for Later:
let lexer = Lexer(input: "1 + 3 + 3 + 7")
let tokensResult = Result { try lexer.lex() } // Result<Success, Failure>' cannot be constructed because it has no accessible initializers
enum Result<Success, Failure> where Failure : Error {
case .success(Success) // Extraneous '.' in enum 'case' declaration
case .failure(Failure) // Extraneous '.' in enum 'case' declaration
}
switch tokensResult {
case let .success(tokens):
print("Found \(tokens.count) tokens \(tokens)")
case let .failure(error):
print("Could not lex \(lexer.input): \(error)")
}
let numbersResult: Result<[Int],Error> = tokensResult.map {
tokens.compactMap { token in // Cannot find 'tokens' in scope
switch token {
case let .number(digit): return digit
default: return nil
}
}
}
func extractNumbers(from result: Result<[Int],Error>) throws -> [Int] {
return try result.get() // Value of type 'Result<[Int], Error>' has no member 'get'
}

Related

Cannot map error after flatMap usage (Never result type)

I have RestManager class which is used for fetching data from Internet and is returning AnyPublisher
class RestManager {
func fetchData<T: Decodable>(url: URL) -> AnyPublisher<T, ErrorType> {
URLSession
.shared
.dataTaskPublisher(for: url)
.tryMap({ data, _ in
let value = try JSONDecoder().decode(T.self, from: data)
if let array = value as? Array<Any>, array.isEmpty {
throw ErrorType.empty
}
return value
})
.mapError { error -> ErrorType in
switch error {
case is ErrorType:
return ErrorType.empty
case let urlError as URLError:
switch urlError.code {
case .notConnectedToInternet, .networkConnectionLost, .timedOut:
return .noInternetConnection
case .cannotDecodeRawData, .cannotDecodeContentData:
return .empty
default:
return .general
}
default:
return .general
}
}
.eraseToAnyPublisher()
}
}
Repository has two functions (getWorldwideData and getCountryData returning AnyPublisher<(WorldwideResponse item or CountryResponse item), ErrorType>)
In viewModel, I made these functions.
private func getData() {
$useCaseSelection
.flatMap { value -> AnyPublisher<Covid19StatisticsDomainItem, ErrorType> in
self.loader = true
self.error = nil
switch value {
case let .country(name):
return self.countryPipeline(name: name)
case .worldwide:
return self.worldwidePipeline()
}
}
.mapError { error in
self.error = error
}
.assign(to: &$homeScreenDomainItem)
}
private func worldwidePipeline() -> AnyPublisher<Covid19StatisticsDomainItem, ErrorType> {
repository
.getWorldwideData()
.map { response -> Covid19StatisticsDomainItem in
self.error = nil
self.loader = false
return Covid19StatisticsDomainItem(worldwideResponseItem: response)
}
.eraseToAnyPublisher()
}
private func countryPipeline(name: String) -> AnyPublisher<Covid19StatisticsDomainItem, ErrorType> {
repository
.getCountryData(for: name)
.map { response -> Covid19StatisticsDomainItem in
self.error = nil
self.loader = false
return Covid19StatisticsDomainItem(countryDayOneStatsResponse: response)
}
.eraseToAnyPublisher()
}
I wanted to make clean code, so I split code into two separate function based on useCaseSelection.
useCaseSelection is enum with two types.
error is ErrorType? value wrapped with #Published, in which I want to save error type if there is any error.
homeScreenDomainItem is Covid19StatisticsDomainItem instance wrapped with #Published.
Problem is in getData function where in MapError pipeline I am getting:
Cannot convert value of type () to closure result type Never
I tried to use setFailureType(to: ErrorType.self) but that is not helping.

How to use Result type to store success/failure states in Swift

Within chapter on error handling / asynchronous programming there is an example on expression evaluator and code on storing Failable Results for Later - code below, lines after implementation of function evaluate. This part does throw more errors, would like to ask if you can help to clarify these. As I was adviced I changed lex() method return type to Result<[Token], Lexer.Error>. And return success or failure. .success(tokens) or .failure(.invalidCharacter(nextCharacter)), however later did not work. Also tried to comment out all lines related to Parser. If you can advice.
Within last lines I include code revised as per input from all of you, thanks for help. Changes commented.
enum Token: CustomStringConvertible {
case number (Int)
case plus
var description: String {
switch self {
case .number (let n):
return "Number: \(n)"
case .plus:
return "Symbol: +"
}
}
}
class Lexer {
enum Error: Swift.Error {
case invalidCharacter (Character)
}
let input: String
var position: String.Index
init(input: String) {
self.input = input
self.position = input.startIndex
}
func peek() -> Character? {
guard position < input.endIndex else {
return nil
}
return input[position]
}
func advance() {
assert(position < input.endIndex, "Cannot advance past endIndex!")
position = input.index(after: position)
}
func getNumber() -> Int {
var value = 0
while let nextCharacter = peek() {
switch nextCharacter {
case "0" ... "9":
let digitValue = Int(String(nextCharacter))!
value = 10*value + digitValue
advance()
default:
return value
}
}
return value
}
func lex() throws -> [Token] { // changed to [Token]
var tokens = [Token]()
while let nextCharacter = peek() {
switch nextCharacter {
case "0" ... "9":
let value = getNumber()
tokens.append(.number(value))
case "+":
tokens.append(.plus)
advance()
case " ":
advance()
default:
throw Lexer.Error.invalidCharacter(nextCharacter)
// .failure(.invalidCharacter(nextCharacter)) did not work here
}
}
return tokens // changed to tokens
}
}
func evaluate(_ input: String) {
print("Evaluating: \(input)")
let lexer = Lexer(input: input)
do {
let tokens = try lexer.lex()
print("Lexer output: \(tokens)")
// let parser = Parser(tokens: tokens)
// let result = try parser.parse()
// print("Parser output: \(result)")
} catch Lexer.Error.invalidCharacter(let character) {
print("Input contained an invalid character: \(character)")
// } catch Parser.Error.unexpectedEndOfInput {
// print("Unexpected end of input during parsing")
// } catch Parser.Error.invalidToken(let token) {
// print("Invalid token during parsing: \(token)")
} catch {
print("An error occured: \(error)")
}
}
/*
class Parser {
enum Error: Swift.Error {
case unexpectedEndOfInput
case invalidToken(Token)
}
let tokens: [Token] // [Token]
var position = 0
init(tokens: [Token]) {
self.tokens = tokens // tokens
}
func getNextToken() -> Token? {
guard position < tokens.count else {
return nil
}
let token = tokens[position]
position += 1
return token
}
func getNumber() throws -> Int {
guard let token = getNextToken() else {
throw Parser.Error.unexpectedEndOfInput
}
switch token {
case .number(let value):
return value
case .plus:
throw Parser.Error.invalidToken(token)
}
}
func parse() throws -> Int {
var value = try getNumber()
while let token = getNextToken() {
switch token {
case .plus:
let nextNumber = try getNumber()
value += nextNumber
case .number:
throw Parser.Error.invalidToken(token)
}
}
return value
}
}
*/
// evaluate("10 + 1")
/*
let lexer = Lexer(input: "1 + 2")
let tokensResult = Result { try lexer.lex() }
print(tokensResult)
*/
/*
let lexer = Lexer(input: "1 + 2 + 3")
let tokensResult = Result { try lexer.lex() } // Result<Success, Failure>' cannot be constructed because it has no accessible initializers
enum Result<Success, Failure> where Failure : Error {
case .success(Success) // Extraneous '.' in enum 'case' declaration
case .failure(Failure) // Extraneous '.' in enum 'case' declaration
}
switch tokensResult {
case let .success(tokens):
print("Found \(tokens.count) tokens \(tokens)")
case let .failure(error):
print("Could not lex \(lexer.input): \(error)")
}
let numbersResult: Result<[Int],Error> = tokensResult.map {
tokens.compactMap { token in // Cannot find 'tokens' in scope
switch token {
case let .number(digit): return digit
default: return nil
}
}
}
func extractNumbers(from result: Result<[Int],Error>) throws -> [Int] {
return try result.get() // Value of type 'Result<[Int], Error>' has no member 'get'
}
*/
let lexer = Lexer(input: "1 + 7")
let tokensResult = Result { try lexer.lex() }
var tokens = [Token]() // added
// deleted redeclaration of Result
switch tokensResult {
case let .success(tokens):
print("Found \(tokens.count) tokens \(tokens)")
case let .failure(error):
print("Could not lex \(lexer.input): \(error)")
}
// before error: Contextual type for closure argument list expects 1 argument, which cannot be implicitly ignored, fixed by: insert _ in
let numbersResult: Result<[Int],Error> = tokensResult.map { _ in
tokens.compactMap { token in
switch token {
case let .number(digit): return digit
default: return nil
}
}
}
func extractNumbers(from result: Result<[Int],Error>) throws -> [Int] {
return try result.get()
}
I think you need to do a lot more reading up on the Result type and how to use it because you have some very strange code above.
Here is my cleaned up version of your code, without a redefinition of Result and a proper way to handle .success
let lexer = Lexer(input: "1 + 2 + 3")
let tokensResult = try lexer.lex()
var tokens = [Token]()
switch tokensResult {
case let .success(result):
print("Found \(tokens.count) tokens \(tokens)")
tokens = result
case let .failure(error):
print("Could not lex \(lexer.input): \(error)")
}
let numbersResult = tokens.compactMap { token in
switch token {
case let .number(digit): return digit
default: return nil
}
}

I'm trying to call a function in a Swift playground, but it is asking for me to declare the function instead

I have already declared a function in a class in the Swift playground, but when I try to call the function to test it with input, it's acting like I am trying to define the function again
func evaluate(_ input: String) {
print("Evaluating: \(input)")
let lexer = Lexer(input: input)
do {
let tokens = try lexer.lex()
print("Lexer output: \(tokens)")
} catch {
print("An error occurred: \(error)")
}
}
evaluate("10 + 3 + 5")
evaluate("1 + 2 + abcdefg")
It's specifically giving the error: "Expected '{' in body of function declaration"
How do I just get it to run the function??
This is the entire code:
import Cocoa
enum Token {
case number(Int)
case plus
}
class Lexer {
enum Error: Swift.Error {
case invalidCharacter(Character)
}
let input: String
var position: String.Index
init(input: String) {
self.input = input
self.position = input.startIndex
}
func peek() -> Character? {
guard position < input.endIndex else {
return nil
}
return input[position]
}
func advance() {
assert(position < input.endIndex, "Cannot advance past endIndex!")
position = input.index(after: position)
}
func getNumber() -> Int {
var value = 0
while let nextCharacter = peek() {
switch nextCharacter {
case "0" ... "9":
let digitValue = Int(String(nextCharacter))!
value = 10*value + digitValue
advance()
default:
return value
}
}
}
func lex() throws -> [Token] {
var tokens = [Token]()
while let nextCharacter = peek() {
switch nextCharacter {
case "0" ... "9":
let value = getNumber()
tokens.append(.number(value))
case "+":
tokens.append(.plus)
advance()
case " ":
advance()
default:
throw Lexer.Error.invalidCharacter(nextCharacter)
}
}
return tokens
}
func evaluate(_ input: String) {
print("Evaluating: \(input)")
let lexer = Lexer(input: input)
do {
let tokens = try lexer.lex()
print("Lexer output: \(tokens)")
} catch {
print("An error occurred: \(error)")
}
}
evaluate("10 + 3 + 5")
evaluate("1 + 2 + abcdefg")
}
You can't run imperative code like evaluate(...) at the top level of a class like you're trying to do. You should move this section outside the body of the Lexer class (eg outside it's closing }):
func evaluate(_ input: String) {
print("Evaluating: \(input)")
let lexer = Lexer(input: input)
do {
let tokens = try lexer.lex()
print("Lexer output: \(tokens)")
} catch {
print("An error occurred: \(error)")
}
}
evaluate("10 + 3 + 5")
evaluate("1 + 2 + abcdefg")
Note that in order to run your code, you'll also have to add a return to the end of getNumber() -- probably return value

Asynchronous iteration using Swift Combine

I am trying to do multiple async operations, in sequence, on an array of data. However I am having problems with the return values of map.
Here is the test code:
import Combine
func getLength(_ string: String) -> Future<Int,Error> {
return Future<Int,Error>{ promise in
print("Length \(string.count)")
promise(.success(string.count))
}
}
func isEven(_ int: Int) -> Future<Bool,Error> {
return Future<Bool,Error>{ promise in
print("Even \(int % 2 == 0)")
promise(.success(int % 2 == 0))
}
}
let stringList = ["a","bbb","c","dddd"]
func testStrings(_ strings:ArraySlice<String>) -> Future<Void,Error> {
var remaining = strings
if let first = remaining.popFirst() {
return getLength(first).map{ length in
return isEven(length)
}.map{ even in
return testStrings(remaining)
}
} else {
return Future { promise in
promise(.success(()))
}
}
}
var storage = Set<AnyCancellable>()
testStrings(ArraySlice<String>(stringList)).sink { _ in } receiveValue: { _ in print("Done") }.store(in: &storage)
This generates the following error:
error: MyPlayground.playground:26:11: error: cannot convert return expression of type 'Publishers.Map<Future<Int, Error>, Future<Void, Error>>' to return type 'Future<Void, Error>'
}.map{ even in
I thought we could use map to convert from one publisher type to the other, but it seems it's wrapped inside a Publishers.Map. How do I get rid of this?
Thanks!
Well it seems that this works:
import Combine
func getLength(_ string: String) -> Future<Int,Error> {
return Future<Int,Error>{ promise in
print("Length \(string.count)")
promise(.success(string.count))
}
}
func isEven(_ int: Int) -> Future<Bool,Error> {
return Future<Bool,Error>{ promise in
print("Even \(int % 2 == 0)")
promise(.success(int % 2 == 0))
}
}
let stringList = ["a","bbb","c","dddd"]
func testStrings(_ strings:ArraySlice<String>) -> AnyPublisher<Void,Error> {
var remaining = strings
if let first = remaining.popFirst() {
return getLength(first).flatMap{ length in
return isEven(length)
}.flatMap{ even in
return testStrings(remaining)
}.eraseToAnyPublisher()
} else {
return Future<Void,Error> { promise in
promise(.success(()))
}.eraseToAnyPublisher()
}
}
var storage = Set<AnyCancellable>()
testStrings(ArraySlice<String>(stringList)).sink { _ in } receiveValue: { _ in print("Done") }.store(in: &storage)

BSON for Swift?

My question's pretty open ended at this point, but I'm curious if anyone has implemented something like SwiftyJSON for BSON in Swift?
I found that the Perfect project has something, but it seems to just be a wrapper around an existing C library (won't do me any good on the iOS side). I toyed with just porting/warping SwiftyJSON, the internals of which are a little above my learning curve so far AND it seems to just be a wrapper around the platform's JSONSerialization.
So is anyone either
A) aware of something done that my googling hasn't discovered yet or
B) or could help nudge me in the right direction with how to generally architect such a thing? (not trying to get others to do work for me)
aside: to forestall the "why don't you just use json" questions... it's because I'm doing quite a bit of MongoDB on the other side AND I work with a lot of Strings and Dates, which have to be ambiguously represented in JSON
In the interest of closure... I ended up writing my own. It is not a complete solution to all of the BSON encodings, just the ones that I'm using. It was fun to figure out how to do it with Swift Enums.
import Foundation
extension GeneratorType {
mutating func next(n: Int) -> [Element] {
var result: [Element] = []
for _ in 1...n {
if let next = self.next() {
result.append(next)
} else {
break
}
}
return result
}
}
extension GeneratorType where Element:Comparable {
mutating func upTo(match:Element) -> [Element]? {
var result: [Element] = []
while let next = self.next() {
if next == match {
return result
}
else {
result.append(next)
}
}
return nil
}
}
extension String {
init?<S : SequenceType, C : UnicodeCodecType where S.Generator.Element == C.CodeUnit>
(codeUnits : S, inout codec : C) {
var str = ""
var generator = codeUnits.generate()
var done = false
while !done {
let r = codec.decode(&generator)
switch (r) {
case .EmptyInput:
done = true
case let .Result(val):
str.append(Character(val))
case .Error:
return nil
}
}
self = str
}
}
enum BSON {
static func toByteArray<T>(value: T) -> [UInt8] {
var io = value
return withUnsafePointer(&io) {
Array(UnsafeBufferPointer(start: UnsafePointer<UInt8>($0), count: sizeof(T)))
}
}
static func fromByteArray<T>(value: [UInt8], _: T.Type) -> T {
return value.withUnsafeBufferPointer {
return UnsafePointer<T>($0.baseAddress).memory
}
}
struct Field {
var name:String
var element:BSON
}
case double(Double)
case string(String)
case document([Field])
case array([BSON])
case binary([UInt8])
// case objectid((UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8))
case boolean(Bool)
case datetime(NSDate)
case null
case int32(Int32)
case int64(Int64)
init() {
self = .document([])
}
var bsonType:UInt8 {
switch self {
case .double: return 0x01
case .string: return 0x02
case .document: return 0x03
case .array: return 0x04
case .binary: return 0x05
// case .objectid: return 0x07
case .boolean: return 0x08
case .datetime: return 0x09
case .null: return 0x0A
case .int32: return 0x10
case .int64: return 0x12
}
}
subscript(key:String) -> BSON {
get {
switch self {
case .document (let fields):
for field in fields {
if field.name == key {
return field.element
}
}
return BSON.null
default:
return BSON.null
}
}
set(newValue) {
var newFields:[Field] = []
switch self {
case .document (let fields):
newFields = fields
var append = true
for (index, field) in newFields.enumerate() {
if field.name == key {
newFields[index].element = newValue
append = false
break
}
}
if append {
newFields.append(Field(name: key, element: newValue))
}
default:
newFields = [Field(name: key, element: newValue)]
}
self = .document(newFields)
}
}
subscript(index:Int) -> BSON {
get {
switch self {
case .array (let elements):
return index < elements.count ? elements[index] : BSON.null
default:
return BSON.null
}
}
set(newValue) {
switch self {
case .array (let elements):
if index < elements.count {
var newElements = elements
newElements[index] = newValue
self = .array(newElements)
}
default:
break
}
}
}
func encoded() -> [UInt8] {
switch self {
case double (let value):
return BSON.toByteArray(value)
case string (let value):
let encoded = value.utf8
return BSON.toByteArray(Int32(encoded.count + 1)) + encoded + [0]
case document (let fields):
var body:[UInt8] = []
for field in fields ?? [] {
body += [field.element.bsonType]
body += field.name.utf8
body += [0]
body += field.element.encoded()
}
body += [0]
return BSON.toByteArray(Int32(body.count + 4)) + body
case array (let elements):
var body:[UInt8] = []
for (index, element) in elements.enumerate() {
body += [element.bsonType]
body += String(index).utf8
body += [0]
body += element.encoded()
}
body += [0]
return BSON.toByteArray(Int32(body.count + 4)) + body
case binary (let bytes):
return BSON.toByteArray(Int32(bytes.count)) + [0x00] + bytes
// case objectid:
// return []
case boolean (let value):
return value ? [0x01] : [0x00]
case datetime (let value):
let since = Int64(value.timeIntervalSince1970 * 1000.0)
return BSON.toByteArray(since)
case null:
return []
case int32 (let value):
return BSON.toByteArray(value)
case int64 (let value):
return BSON.toByteArray(value)
}
}
static func decode(inout stream stream:IndexingGenerator<[UInt8]>, bsonType:UInt8 = 0x03) -> BSON {
switch bsonType {
case 0x01:
let bytes = stream.next(sizeof(Double))
return self.double(fromByteArray(bytes, Double.self))
case 0x02:
let _ = stream.next(sizeof(Int32)) // skip the count
if let buffer = stream.upTo(0) {
var codec = UTF8()
if let decoded = String(codeUnits: buffer, codec: &codec) {
return self.string(decoded)
}
}
fatalError("utf8 parse error!")
case 0x03:
var fields:[Field] = []
stream.next(sizeof(Int32)) // throw out size
while let bsonType = stream.next() {
if bsonType == 0 {
return self.document(fields)
}
if let buffer = stream.upTo(0) {
var codec = UTF8()
if let fieldName = String(codeUnits: buffer, codec: &codec) {
let element = BSON.decode(stream:&stream, bsonType: bsonType)
fields.append(Field(name: fieldName, element: element))
}
}
}
case 0x04:
var elements:[BSON] = []
stream.next(sizeof(Int32)) // throw out size
while let bsonType = stream.next() {
if bsonType == 0 {
return self.array(elements)
}
stream.upTo(0) // skip name
elements.append(BSON.decode(stream:&stream, bsonType: bsonType))
}
case 0x05:
let count = fromByteArray(stream.next(sizeof(Int32)), Int32.self)
assert(stream.next() == 0x00)
return self.binary(stream.next(Int(count)))
case 0x07:
break
case 0x08:
let value = stream.next()
return self.boolean(value == 0x01)
case 0x09:
let milliseconds = fromByteArray(stream.next(sizeof(Int64)), Int64.self)
let interval = NSTimeInterval(milliseconds) / 1000.0
return self.datetime(NSDate(timeIntervalSince1970: interval))
case 0x0A:
return self.null
case 0x10:
let value = fromByteArray(stream.next(sizeof(Int32)), Int32.self)
return self.int32(value)
case 0x12:
let value = fromByteArray(stream.next(sizeof(Int64)), Int64.self)
return self.int64(value)
default:
break
}
fatalError()
}
var document:BSON? {
switch self {
case .document:
return self
default:
return nil
}
}
var double:Double? {
switch self {
case .double (let value):
return value
default:
return nil
}
}
var int32:Int32? {
get {
switch self {
case .int32 (let value):
return value
default:
return nil
}
}
set {
if let newValue = newValue {
self = .int32(newValue)
}
else {
self = .null
}
}
}
var int64:Int64? {
switch self {
case .int64 (let value):
return value
default:
return nil
}
}
var boolean:Bool? {
switch self {
case .boolean (let value):
return value
default:
return nil
}
}
var binary:[UInt8]? {
switch self {
case .binary (let value):
return value
default:
return nil
}
}
var string:String? {
switch self {
case .string (let value):
return value
default:
return nil
}
}
var datetime:NSDate? {
switch self {
case .datetime (let value):
return value
default:
return nil
}
}
var array:[BSON]? {
switch self {
case .array (let elements):
return elements
default:
return nil
}
}
var isNull:Bool {
switch self {
case .null:
return true
default:
return false
}
}
var keyValues:[(String, BSON)] {
switch self {
case document (let fields):
return fields.map() { ($0.name, $0.element) }
default:
return []
}
}
}
extension BSON: Equatable {}
extension BSON.Field: Equatable {}
func == (a:BSON.Field, b:BSON.Field) -> Bool {
return a.name == b.name && a.element == b.element
}
func == (a:BSON, b:BSON) -> Bool {
switch (a, b) {
case (.double(let a), .double(let b)) where a == b: return true
case (.string(let a), .string(let b)) where a == b: return true
case (.document(let a), .document(let b)) where a == b: return true
case (.array(let a), .array(let b)) where a == b: return true
case (.binary(let a), .binary(let b)) where a == b: return true
case (.boolean(let a), .boolean(let b)) where a == b: return true
case (.datetime(let a), .datetime(let b)) where a.isEqualToDate(b): return true
case (.null, .null): return true
case (.int32(let a), .int32(let b)) where a == b: return true
case (.int64(let a), .int64(let b)) where a == b: return true
default: return false
}
}
protocol BSONConvertable {
var bson:BSON { get }
}
extension Int32: BSONConvertable {
var bson:BSON {
return BSON.int32(self)
}
}
extension Int64: BSONConvertable {
var bson:BSON {
return BSON.int64(self)
}
}
extension Int: BSONConvertable {
var bson:BSON {
let wide = self.toIntMax()
if Int32.min.toIntMax() <= wide && wide <= Int32.max.toIntMax() {
return BSON.int32(Int32(wide))
}
else {
return BSON.int64(Int64(wide))
}
}
}
extension Float:BSONConvertable {
var bson:BSON {
return Double(self).bson
}
}
extension Double:BSONConvertable {
var bson:BSON {
return BSON.double(self)
}
}
extension Bool:BSONConvertable {
var bson:BSON {
return BSON.boolean(self)
}
}
extension BSON:BSONConvertable {
var bson:BSON {
return self
}
}
extension NSDate:BSONConvertable {
var bson:BSON {
return BSON.datetime(self)
}
}
extension String:BSONConvertable {
var bson:BSON {
return BSON.string(self)
}
}
extension Array where Element:BSONConvertable {
var bson:BSON {
return BSON.array(self.map({$0.bson}))
}
}
Late to the party but the OpenKitten implementation on github seems to be a good implementation and it is referenced from the BSON spec site.
https://github.com/OpenKitten/BSON