 
 /// connection details to database
 public struct RopeCredentials {
-
+    
     private(set) public var host: String
     private(set) public var port: Int
     private(set) public var dbName: String
     private(set) public var user: String
     private(set) public var password: String
-
+    
     public init(host: String, port: Int, dbName: String, user: String, password: String) {
         self.host = host
         self.port = port

            throw RopeError.invalidQuery(message: message)
        }
    }

    private func close() throws {
        guard self.connected else {
            throw failWithError()
        }

        PQfinish(conn)
        conn = nil
    }

    private func failWithError(_ conn: OpaquePointer? = nil) -> Error {
        let message = String(cString: PQerrorMessage(conn ?? self.conn))
        return RopeError.connectionFailed(message: message)

}

public final class Rope {
    
    private(set) var conn: OpaquePointer!
    private let connectionQueue: DispatchQueue = DispatchQueue(label: "com.rope.connection-queue-\(UUID().uuidString)")
    
    public var connected: Bool {
        guard let conn = conn, PQstatus(conn) == CONNECTION_OK else {
            return false
        }
        return true
    }
    
    deinit {
        try? close()
    }
    
    /// connect to database using RopeCredentials struct
    public static func connect(credentials: RopeCredentials) throws -> Rope {
        let rope = Rope()
        try rope.establishConnection(host: credentials.host, port: credentials.port,
                                     dbName: credentials.dbName, user: credentials.user, password: credentials.password)
        
        return rope
    }
    
    /// connect to database using credential connection arguments
    public static func connect(host: String = "localhost", port: Int = 5432,
                               dbName: String, user: String, password: String) throws -> Rope {
        let rope = Rope()
        try rope.establishConnection(host: host, port: port, dbName: dbName, user: user, password: password)
        
        return rope
    }
    
    private func establishConnection(host: String, port: Int, dbName: String, user: String, password: String) throws {
        let conn = PQsetdbLogin(host, String(port), "", "", dbName, user, password)
        
        guard PQstatus(conn) == CONNECTION_OK else {
            throw failWithError(conn)
        }
        
        self.conn = conn
    }
    
    /// query database with SQL statement
    public func query(_ statement: String) throws -> RopeResult {
        return try execQuery(statement: statement)
    }
    
    /// query database with SQL statement, use $1, $2, etc. for params in SQL
    public func query(statement: String, params: [Any]) throws -> RopeResult {
        return try execQuery(statement: statement, params: params)
    }
    
    private func execQuery(statement: String, params: [Any]? = nil) throws -> RopeResult {
        if statement.isEmpty {
            throw RopeError.emptyQuery
        }
        
        guard let params = params else {
            let result = self.connectionQueue.sync {
                return PQexec(self.conn, statement)
