You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

166 lines
5.2 KiB
Swift

/*
* Copyright 2022, The Cozo Project Authors.
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
* If a copy of the MPL was not distributed with this file,
* You can obtain one at https://mozilla.org/MPL/2.0/.
*/
import Foundation
import SwiftyJSON
public enum CozoError: Error {
case system(String)
case query(JSON)
}
public class RowHeaders {
public let headers: [String]
init(headers: [String]) {
self.headers = headers
}
lazy var invertedKeys: [String:Int] = {
var ret: [String:Int] = [:]
for i in 0..<self.headers.count {
ret[self.headers[i]] = i
}
return ret
}()
}
public struct NamedRow {
public let headers: RowHeaders
public let fields: [JSON]
public func get(idx: Int) -> JSON {
return self.fields[idx]
}
public func get(key: String) -> JSON? {
if let idx = self.headers.invertedKeys[key] {
return self.fields[idx]
} else {
return nil
}
}
}
extension [NamedRow] {
public func toString() -> String {
var ret = "["
var isFirst = true
for field in self {
if !isFirst {
ret += ", "
} else {
isFirst = false
}
ret += field.toString()
}
ret += "]"
return ret
}
}
extension NamedRow {
public func toString() -> String {
var ret = "{"
for i in 0..<self.headers.headers.count {
if i != 0 {
ret += ", "
}
ret += self.headers.headers[i]
ret += ": "
ret += self.fields[i].rawString(.utf8, options: .init(rawValue: 0))!
}
ret += "}"
return ret
}
}
public class CozoDB {
public let db: DbInstance
public init() {
let db = new_cozo_db("mem", "", "")!
self.db = db
}
public init(kind: String, path: String) throws {
if let db = new_cozo_db(kind, path, "") {
self.db = db
} else {
throw CozoError.system("Cannot create database")
}
}
public func run(_ query: String, params: JSON) throws -> [NamedRow] {
let payload = params.rawString(.utf8, options: .init(rawValue: 0))!
return try self.run(query, stringParams: payload)
}
public func run(_ query: String) throws -> [NamedRow] {
return try self.run(query, stringParams: "")
}
func run(_ query: String, stringParams: String) throws -> [NamedRow] {
let resStr = self.db.run_script_str(query, stringParams).toString()
let dataFromString = resStr.data(using: .utf8, allowLossyConversion: false)!
let json = JSON(dataFromString);
if json["ok"].boolValue {
let jHeaders = json["headers"].arrayValue.map{(j) -> String in
return j.stringValue
}
let headers = RowHeaders(headers: jHeaders)
return json["rows"].arrayValue.map{(j) -> NamedRow in
let fields = j.arrayValue
return NamedRow(headers: headers, fields: fields)
}
} else {
throw CozoError.query(json)
}
}
public func exportRelations(relations: [String]) throws -> JSON {
let payload = JSON(["relations": relations]).rawString(.utf8, options: .init(rawValue: 0))!
let resStr = self.db.export_relations_str(payload).toString()
let dataFromString = resStr.data(using: .utf8, allowLossyConversion: false)!
let json = JSON(dataFromString);
if json["ok"].boolValue {
return json["data"]
} else {
throw CozoError.query(json)
}
}
public func importRelations(data: JSON) throws {
let payload = data.rawString(.utf8, options: .init(rawValue: 0))!
let resStr = self.db.import_relations_str(payload).toString()
let dataFromString = resStr.data(using: .utf8, allowLossyConversion: false)!
let json = JSON(dataFromString);
if !json["ok"].boolValue {
throw CozoError.query(json)
}
}
public func backup(path: String) throws {
let resStr = self.db.backup_db_str(path).toString()
let dataFromString = resStr.data(using: .utf8, allowLossyConversion: false)!
let json = JSON(dataFromString);
if !json["ok"].boolValue {
throw CozoError.query(json)
}
}
public func restore(path: String) throws {
let resStr = self.db.restore_backup_str(path).toString()
let dataFromString = resStr.data(using: .utf8, allowLossyConversion: false)!
let json = JSON(dataFromString);
if !json["ok"].boolValue {
throw CozoError.query(json)
}
}
public func importRelationsFromBackup(path: String, relations: [String]) throws {
let payload = JSON(["relations": relations, "path": path]).rawString(.utf8, options: .init(rawValue: 0))!
let resStr = self.db.import_relations_str(payload).toString()
let dataFromString = resStr.data(using: .utf8, allowLossyConversion: false)!
let json = JSON(dataFromString);
if !json["ok"].boolValue {
throw CozoError.query(json)
}
}
}