Add wrapper classes for Rust FFI
This commit is contained in:
parent
d193c9c79e
commit
5a1102bf14
12 changed files with 1285 additions and 12 deletions
|
@ -7,9 +7,6 @@
|
||||||
objects = {
|
objects = {
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
7B744837208DF20D006CFFB0 /* EntityBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B744836208DF20D006CFFB0 /* EntityBuilder.swift */; };
|
|
||||||
7B744839208DF2E1006CFFB0 /* InProgress.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B744838208DF2E1006CFFB0 /* InProgress.swift */; };
|
|
||||||
7B74483B208DF2F9006CFFB0 /* InProgressBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B74483A208DF2F9006CFFB0 /* InProgressBuilder.swift */; };
|
|
||||||
7B74483D208DF667006CFFB0 /* Result+Unwrap.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B74483C208DF667006CFFB0 /* Result+Unwrap.swift */; };
|
7B74483D208DF667006CFFB0 /* Result+Unwrap.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B74483C208DF667006CFFB0 /* Result+Unwrap.swift */; };
|
||||||
7BAE75A22089020E00895D37 /* libmentat_ffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7BEB7D23207BE2AF000369AD /* libmentat_ffi.a */; };
|
7BAE75A22089020E00895D37 /* libmentat_ffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7BEB7D23207BE2AF000369AD /* libmentat_ffi.a */; };
|
||||||
7BAE75A42089022B00895D37 /* libsqlite3.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 7BAE75A32089022B00895D37 /* libsqlite3.tbd */; };
|
7BAE75A42089022B00895D37 /* libsqlite3.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 7BAE75A32089022B00895D37 /* libsqlite3.tbd */; };
|
||||||
|
@ -42,9 +39,6 @@
|
||||||
/* End PBXContainerItemProxy section */
|
/* End PBXContainerItemProxy section */
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
7B744836208DF20D006CFFB0 /* EntityBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EntityBuilder.swift; sourceTree = "<group>"; };
|
|
||||||
7B744838208DF2E1006CFFB0 /* InProgress.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InProgress.swift; sourceTree = "<group>"; };
|
|
||||||
7B74483A208DF2F9006CFFB0 /* InProgressBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InProgressBuilder.swift; sourceTree = "<group>"; };
|
|
||||||
7B74483C208DF667006CFFB0 /* Result+Unwrap.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = "Result+Unwrap.swift"; path = "Mentat/Extensions/Result+Unwrap.swift"; sourceTree = SOURCE_ROOT; };
|
7B74483C208DF667006CFFB0 /* Result+Unwrap.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = "Result+Unwrap.swift"; path = "Mentat/Extensions/Result+Unwrap.swift"; sourceTree = SOURCE_ROOT; };
|
||||||
7B911E1A2085081D000998CB /* libtoodle.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libtoodle.a; path = "../../../../sync-storage-prototype/rust/target/universal/release/libtoodle.a"; sourceTree = "<group>"; };
|
7B911E1A2085081D000998CB /* libtoodle.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libtoodle.a; path = "../../../../sync-storage-prototype/rust/target/universal/release/libtoodle.a"; sourceTree = "<group>"; };
|
||||||
7BAE75A32089022B00895D37 /* libsqlite3.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.tbd; path = usr/lib/libsqlite3.tbd; sourceTree = SDKROOT; };
|
7BAE75A32089022B00895D37 /* libsqlite3.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.tbd; path = usr/lib/libsqlite3.tbd; sourceTree = SDKROOT; };
|
||||||
|
@ -203,9 +197,6 @@
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
7BEB7D2B207D03DA000369AD /* TxReport.swift */,
|
7BEB7D2B207D03DA000369AD /* TxReport.swift */,
|
||||||
7B744836208DF20D006CFFB0 /* EntityBuilder.swift */,
|
|
||||||
7B744838208DF2E1006CFFB0 /* InProgress.swift */,
|
|
||||||
7B74483A208DF2F9006CFFB0 /* InProgressBuilder.swift */,
|
|
||||||
);
|
);
|
||||||
path = Transact;
|
path = Transact;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -324,15 +315,12 @@
|
||||||
7BDB96B32077C38E009D0651 /* RustObject.swift in Sources */,
|
7BDB96B32077C38E009D0651 /* RustObject.swift in Sources */,
|
||||||
7BDB96C62077D347009D0651 /* Date+Int64.swift in Sources */,
|
7BDB96C62077D347009D0651 /* Date+Int64.swift in Sources */,
|
||||||
7BEB7D2C207D03DA000369AD /* TxReport.swift in Sources */,
|
7BEB7D2C207D03DA000369AD /* TxReport.swift in Sources */,
|
||||||
7B744839208DF2E1006CFFB0 /* InProgress.swift in Sources */,
|
|
||||||
7BDB96B42077C38E009D0651 /* OptionalRustObject.swift in Sources */,
|
7BDB96B42077C38E009D0651 /* OptionalRustObject.swift in Sources */,
|
||||||
7B74483B208DF2F9006CFFB0 /* InProgressBuilder.swift in Sources */,
|
|
||||||
7BDB96B22077C38E009D0651 /* RelResult.swift in Sources */,
|
7BDB96B22077C38E009D0651 /* RelResult.swift in Sources */,
|
||||||
7BDB96AF2077C38E009D0651 /* Query.swift in Sources */,
|
7BDB96AF2077C38E009D0651 /* Query.swift in Sources */,
|
||||||
7BDB96CC207B7684009D0651 /* Errors.swift in Sources */,
|
7BDB96CC207B7684009D0651 /* Errors.swift in Sources */,
|
||||||
7BDB96B02077C38E009D0651 /* Mentat.swift in Sources */,
|
7BDB96B02077C38E009D0651 /* Mentat.swift in Sources */,
|
||||||
7BDB96B72077C38E009D0651 /* TypedValue.swift in Sources */,
|
7BDB96B72077C38E009D0651 /* TypedValue.swift in Sources */,
|
||||||
7B744837208DF20D006CFFB0 /* EntityBuilder.swift in Sources */,
|
|
||||||
7BDB96B52077C38E009D0651 /* TupleResult.swift in Sources */,
|
7BDB96B52077C38E009D0651 /* TupleResult.swift in Sources */,
|
||||||
7B74483D208DF667006CFFB0 /* Result+Unwrap.swift in Sources */,
|
7B74483D208DF667006CFFB0 /* Result+Unwrap.swift in Sources */,
|
||||||
);
|
);
|
||||||
|
|
181
sdks/swift/Mentat/Mentat/Core/TypedValue.swift
Normal file
181
sdks/swift/Mentat/Mentat/Core/TypedValue.swift
Normal file
|
@ -0,0 +1,181 @@
|
||||||
|
/* Copyright 2018 Mozilla
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||||
|
* this file except in compliance with the License. You may obtain a copy of the
|
||||||
|
* License at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
|
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations under the License. */
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import MentatStore
|
||||||
|
|
||||||
|
/**
|
||||||
|
A wrapper around Mentat's `TypedValue` Rust object. This class wraps a raw pointer to a Rust `TypedValue`
|
||||||
|
struct and provides accessors to the values according to expected result type.
|
||||||
|
|
||||||
|
As the FFI functions for fetching values are consuming, this class keeps a copy of the result internally after
|
||||||
|
fetching so that the value can be referenced several times.
|
||||||
|
|
||||||
|
Also, due to the consuming nature of the FFI layer, this class also manages it's raw pointer, nilling it after calling the
|
||||||
|
FFI conversion function so that the underlying base class can manage cleanup.
|
||||||
|
*/
|
||||||
|
class TypedValue: OptionalRustObject {
|
||||||
|
|
||||||
|
private var value: Any?
|
||||||
|
|
||||||
|
/**
|
||||||
|
The `ValueType` for this `TypedValue`.
|
||||||
|
- Returns: The `ValueType` for this `TypedValue`.
|
||||||
|
*/
|
||||||
|
var valueType: ValueType {
|
||||||
|
return typed_value_value_type(self.raw!)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func isConsumed() -> Bool {
|
||||||
|
return self.raw == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
This value as a `Int64`. This function will panic if the `ValueType` of this `TypedValue`
|
||||||
|
is not a `Long`
|
||||||
|
|
||||||
|
- Returns: the value of this `TypedValue` as a `Int64`
|
||||||
|
*/
|
||||||
|
func asLong() -> Int64 {
|
||||||
|
defer {
|
||||||
|
self.raw = nil
|
||||||
|
}
|
||||||
|
if !self.isConsumed() {
|
||||||
|
self.value = typed_value_as_long(self.raw!)
|
||||||
|
}
|
||||||
|
return self.value as! Int64
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
This value as an `Entid`. This function will panic if the `ValueType` of this `TypedValue`
|
||||||
|
is not a `Ref`
|
||||||
|
|
||||||
|
- Returns: the value of this `TypedValue` as an `Entid`
|
||||||
|
*/
|
||||||
|
func asEntid() -> Entid {
|
||||||
|
defer {
|
||||||
|
self.raw = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if !self.isConsumed() {
|
||||||
|
self.value = typed_value_as_entid(self.raw!)
|
||||||
|
}
|
||||||
|
return self.value as! Entid
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
This value as a keyword `String`. This function will panic if the `ValueType` of this `TypedValue`
|
||||||
|
is not a `Keyword`
|
||||||
|
|
||||||
|
- Returns: the value of this `TypedValue` as a keyword `String`
|
||||||
|
*/
|
||||||
|
func asKeyword() -> String {
|
||||||
|
defer {
|
||||||
|
self.raw = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if !self.isConsumed() {
|
||||||
|
self.value = String(cString: typed_value_as_kw(self.raw!))
|
||||||
|
}
|
||||||
|
return self.value as! String
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
This value as a `Bool`. This function will panic if the `ValueType` of this `TypedValue`
|
||||||
|
is not a `Boolean`
|
||||||
|
|
||||||
|
- Returns: the value of this `TypedValue` as a `Bool`
|
||||||
|
*/
|
||||||
|
func asBool() -> Bool {
|
||||||
|
defer {
|
||||||
|
self.raw = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if !self.isConsumed() {
|
||||||
|
let v = typed_value_as_boolean(self.raw!)
|
||||||
|
self.value = v > 0
|
||||||
|
}
|
||||||
|
return self.value as! Bool
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
This value as a `Double`. This function will panic if the `ValueType` of this `TypedValue`
|
||||||
|
is not a `Double`
|
||||||
|
|
||||||
|
- Returns: the value of this `TypedValue` as a `Double`
|
||||||
|
*/
|
||||||
|
func asDouble() -> Double {
|
||||||
|
defer {
|
||||||
|
self.raw = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if !self.isConsumed() {
|
||||||
|
self.value = typed_value_as_double(self.raw!)
|
||||||
|
}
|
||||||
|
return self.value as! Double
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
This value as a `Date`. This function will panic if the `ValueType` of this `TypedValue`
|
||||||
|
is not a `Instant`
|
||||||
|
|
||||||
|
- Returns: the value of this `TypedValue` as a `Date`
|
||||||
|
*/
|
||||||
|
func asDate() -> Date {
|
||||||
|
defer {
|
||||||
|
self.raw = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if !self.isConsumed() {
|
||||||
|
let timestamp = typed_value_as_timestamp(self.raw!)
|
||||||
|
self.value = Date(timeIntervalSince1970: TimeInterval(timestamp))
|
||||||
|
}
|
||||||
|
return self.value as! Date
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
This value as a `String`. This function will panic if the `ValueType` of this `TypedValue`
|
||||||
|
is not a `String`
|
||||||
|
|
||||||
|
- Returns: the value of this `TypedValue` as a `String`
|
||||||
|
*/
|
||||||
|
func asString() -> String {
|
||||||
|
defer {
|
||||||
|
self.raw = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if !self.isConsumed() {
|
||||||
|
self.value = String(cString: typed_value_as_string(self.raw!))
|
||||||
|
}
|
||||||
|
return self.value as! String
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
This value as a `UUID`. This function will panic if the `ValueType` of this `TypedValue`
|
||||||
|
is not a `Uuid`
|
||||||
|
|
||||||
|
- Returns: the value of this `TypedValue` as a `UUID?`. If the `UUID` is not valid then this function returns nil.
|
||||||
|
*/
|
||||||
|
func asUUID() -> UUID? {
|
||||||
|
defer {
|
||||||
|
self.raw = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if !self.isConsumed() {
|
||||||
|
let bytes = typed_value_as_uuid(self.raw!).pointee
|
||||||
|
self.value = UUID(uuid: bytes)
|
||||||
|
}
|
||||||
|
return self.value as! UUID?
|
||||||
|
}
|
||||||
|
|
||||||
|
override func cleanup(pointer: OpaquePointer) {
|
||||||
|
typed_value_destroy(pointer)
|
||||||
|
}
|
||||||
|
}
|
30
sdks/swift/Mentat/Mentat/Errors/Errors.swift
Normal file
30
sdks/swift/Mentat/Mentat/Errors/Errors.swift
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
//
|
||||||
|
/* Copyright 2018 Mozilla
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||||
|
* this file except in compliance with the License. You may obtain a copy of the
|
||||||
|
* License at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
|
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations under the License. */
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
public enum QueryError: Error {
|
||||||
|
case invalidKeyword(message: String)
|
||||||
|
case executionFailed(message: String)
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct MentatError: Error {
|
||||||
|
let message: String
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum PointerError: Error {
|
||||||
|
case pointerConsumed
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum ResultError: Error {
|
||||||
|
case error(message: String)
|
||||||
|
case empty
|
||||||
|
}
|
16
sdks/swift/Mentat/Mentat/Extensions/Date+Int64.swift
Normal file
16
sdks/swift/Mentat/Mentat/Extensions/Date+Int64.swift
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
///* 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 http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
extension Date {
|
||||||
|
/**
|
||||||
|
This `Date` as microseconds.
|
||||||
|
|
||||||
|
- Returns: The `timeIntervalSince1970` in microseconds
|
||||||
|
*/
|
||||||
|
func toMicroseconds() -> Int64 {
|
||||||
|
return Int64(self.timeIntervalSince1970 * 1_000_000)
|
||||||
|
}
|
||||||
|
}
|
50
sdks/swift/Mentat/Mentat/Extensions/Result+Unwrap.swift
Normal file
50
sdks/swift/Mentat/Mentat/Extensions/Result+Unwrap.swift
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
/* Copyright 2018 Mozilla
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||||
|
* this file except in compliance with the License. You may obtain a copy of the
|
||||||
|
* License at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
|
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations under the License. */
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import MentatStore
|
||||||
|
|
||||||
|
extension Result {
|
||||||
|
/**
|
||||||
|
Force unwraps a result.
|
||||||
|
Expects there to be a value attached and throws an error is there is not.
|
||||||
|
|
||||||
|
- Throws: `ResultError.error` if the result contains an error
|
||||||
|
- Throws: `ResultError.empty` if the result contains no error but also no result.
|
||||||
|
|
||||||
|
- Returns: The pointer to the successful result value.
|
||||||
|
*/
|
||||||
|
@discardableResult public func unwrap() throws -> UnsafeMutableRawPointer {
|
||||||
|
guard let success = self.ok else {
|
||||||
|
if let error = self.err {
|
||||||
|
throw ResultError.error(message: String(cString: error))
|
||||||
|
}
|
||||||
|
throw ResultError.empty
|
||||||
|
}
|
||||||
|
return success
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Unwraps an optional result, yielding either a successful value or a nil.
|
||||||
|
|
||||||
|
- Throws: `ResultError.error` if the result contains an error
|
||||||
|
|
||||||
|
- Returns: The pointer to the successful result value, or nil if no value is present.
|
||||||
|
*/
|
||||||
|
@discardableResult public func tryUnwrap() throws -> UnsafeMutableRawPointer? {
|
||||||
|
guard let success = self.ok else {
|
||||||
|
if let error = self.err {
|
||||||
|
throw ResultError.error(message: String(cString: error))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return success
|
||||||
|
}
|
||||||
|
}
|
170
sdks/swift/Mentat/Mentat/Mentat.swift
Normal file
170
sdks/swift/Mentat/Mentat/Mentat.swift
Normal file
|
@ -0,0 +1,170 @@
|
||||||
|
/* Copyright 2018 Mozilla
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||||
|
* this file except in compliance with the License. You may obtain a copy of the
|
||||||
|
* License at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
|
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations under the License. */
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
import MentatStore
|
||||||
|
|
||||||
|
typealias Entid = Int64
|
||||||
|
|
||||||
|
/**
|
||||||
|
Protocol to be implemenented by any object that wishes to register for transaction observation
|
||||||
|
*/
|
||||||
|
protocol Observing {
|
||||||
|
func transactionDidOccur(key: String, reports: [TxChange])
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Protocol to be implemented by any object that provides an interface to Mentat's transaction observers.
|
||||||
|
*/
|
||||||
|
protocol Observable {
|
||||||
|
func register(key: String, observer: Observing, attributes: [String])
|
||||||
|
func unregister(key: String)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
The primary class for accessing Mentat's API.
|
||||||
|
This class provides all of the basic API that can be found in Mentat's Store struct.
|
||||||
|
The raw pointer it holds is a pointer to a Store.
|
||||||
|
*/
|
||||||
|
class Mentat: RustObject {
|
||||||
|
fileprivate static var observers = [String: Observing]()
|
||||||
|
|
||||||
|
/**
|
||||||
|
Create a new Mentat with the provided pointer to a Mentat Store
|
||||||
|
- Parameter raw: A pointer to a Mentat Store.
|
||||||
|
*/
|
||||||
|
required override init(raw: OpaquePointer) {
|
||||||
|
super.init(raw: raw)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Open a connection to a Store in a given location.
|
||||||
|
If the store does not already exist, one will be created.
|
||||||
|
|
||||||
|
- Parameter storeURI: The URI as a String of the store to open.
|
||||||
|
If no store URI is provided, an in-memory store will be opened.
|
||||||
|
*/
|
||||||
|
convenience init(storeURI: String = "") {
|
||||||
|
self.init(raw: store_open(storeURI))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Simple transact of an EDN string.
|
||||||
|
- Parameter transaction: The string, as EDN, to be transacted
|
||||||
|
|
||||||
|
- Throws: `MentatError` if the an error occured during the transaction, or the TxReport is nil.
|
||||||
|
|
||||||
|
- Returns: The `TxReport` of the completed transaction
|
||||||
|
*/
|
||||||
|
func transact(transaction: String) throws -> TxReport {
|
||||||
|
let result = store_transact(self.raw, transaction).pointee
|
||||||
|
return TxReport(raw: try result.unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Get the the `Entid` of the attribute.
|
||||||
|
- Parameter attribute: The string represeting the attribute whose `Entid` we are after.
|
||||||
|
The string is represented as `:namespace/name`.
|
||||||
|
|
||||||
|
- Returns: The `Entid` associated with the attribute.
|
||||||
|
*/
|
||||||
|
func entidForAttribute(attribute: String) -> Entid {
|
||||||
|
return Entid(store_entid_for_attribute(self.raw, attribute))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Start a query.
|
||||||
|
- Parameter query: The string represeting the the query to be executed.
|
||||||
|
|
||||||
|
- Returns: The `Query` representing the query that can be executed.
|
||||||
|
*/
|
||||||
|
func query(query: String) -> Query {
|
||||||
|
return Query(raw: store_query(self.raw, query))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Retrieve a single value of an attribute for an Entity
|
||||||
|
- Parameter attribute: The string the attribute whose value is to be returned.
|
||||||
|
The string is represented as `:namespace/name`.
|
||||||
|
- Parameter entid: The `Entid` of the entity we want the value from.
|
||||||
|
|
||||||
|
- Returns: The `TypedValue` containing the value of the attribute for the entity.
|
||||||
|
*/
|
||||||
|
func value(forAttribute attribute: String, ofEntity entid: Entid) throws -> TypedValue? {
|
||||||
|
let result = store_value_for_attribute(self.raw, entid, attribute).pointee
|
||||||
|
return TypedValue(raw: try result.unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Destroys the pointer by passing it back into Rust to be cleaned up
|
||||||
|
override func cleanup(pointer: OpaquePointer) {
|
||||||
|
store_destroy(pointer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Set up `Mentat` to provide an interface to Mentat's transaction observation
|
||||||
|
*/
|
||||||
|
extension Mentat: Observable {
|
||||||
|
/**
|
||||||
|
Register an `Observing` and a set of attributes to observer for transaction observation.
|
||||||
|
The `transactionDidOccur(String: [TxChange]:)` function is called when a transaction
|
||||||
|
occurs in the `Store` that this `Mentat` is connected to that affects the attributes that an
|
||||||
|
`Observing` has registered for.
|
||||||
|
|
||||||
|
- Parameter key: `String` representing an identifier for the `Observing`.
|
||||||
|
- Parameter observer: The `Observing` to be notified when a transaction occurs.
|
||||||
|
- Parameter attributes: An `Array` of `Strings` representing the attributes that the `Observing`
|
||||||
|
wishes to be notified about if they are referenced in a transaction.
|
||||||
|
*/
|
||||||
|
func register(key: String, observer: Observing, attributes: [String]) {
|
||||||
|
let attrEntIds = attributes.map({ (kw) -> Entid in
|
||||||
|
let entid = Entid(self.entidForAttribute(attribute: kw));
|
||||||
|
return entid
|
||||||
|
})
|
||||||
|
|
||||||
|
let ptr = UnsafeMutablePointer<Entid>.allocate(capacity: attrEntIds.count)
|
||||||
|
let entidPointer = UnsafeMutableBufferPointer(start: ptr, count: attrEntIds.count)
|
||||||
|
var _ = entidPointer.initialize(from: attrEntIds)
|
||||||
|
|
||||||
|
guard let firstElement = entidPointer.baseAddress else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
Mentat.observers[key] = observer
|
||||||
|
store_register_observer(self.raw, key, firstElement, Entid(attributes.count), transactionObserverCallback)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Unregister the `Observing` that was registered with the provided key such that it will no longer be called
|
||||||
|
if a transaction occurs that affects the attributes that `Observing` was registered to observe.
|
||||||
|
|
||||||
|
The `Observing` will need to re-register if it wants to start observing again.
|
||||||
|
|
||||||
|
- Parameter key: `String` representing an identifier for the `Observing`.
|
||||||
|
*/
|
||||||
|
func unregister(key: String) {
|
||||||
|
Mentat.observers.removeValue(forKey: key)
|
||||||
|
store_unregister_observer(self.raw, key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
This function needs to be static as callbacks passed into Rust from Swift cannot contain state. Therefore the observers are static, as is
|
||||||
|
the function that we pass into Rust to receive the callback.
|
||||||
|
*/
|
||||||
|
private func transactionObserverCallback(key: UnsafePointer<CChar>, reports: UnsafePointer<TxChangeList>) {
|
||||||
|
let key = String(cString: key)
|
||||||
|
guard let observer = Mentat.observers[key] else { return }
|
||||||
|
DispatchQueue.global(qos: .background).async {
|
||||||
|
observer.transactionDidOccur(key: key, reports: [TxChange]())
|
||||||
|
}
|
||||||
|
}
|
337
sdks/swift/Mentat/Mentat/Query/Query.swift
Normal file
337
sdks/swift/Mentat/Mentat/Query/Query.swift
Normal file
|
@ -0,0 +1,337 @@
|
||||||
|
/* Copyright 2018 Mozilla
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||||
|
* this file except in compliance with the License. You may obtain a copy of the
|
||||||
|
* License at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
|
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations under the License. */
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import MentatStore
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
This class allows you to contruct a query, bind values to variables and run those queries against a mentat DB.
|
||||||
|
|
||||||
|
This class cannot be created directly, but must be created through `Mentat.query(String:)`.
|
||||||
|
|
||||||
|
The types of values you can bind are
|
||||||
|
- `Int64`
|
||||||
|
- `Entid`
|
||||||
|
- `Keyword`
|
||||||
|
- `Bool`
|
||||||
|
- `Double`
|
||||||
|
- `Date`
|
||||||
|
- `String`
|
||||||
|
- `UUID`.
|
||||||
|
|
||||||
|
Each bound variable must have a corresponding value in the query string used to create this query.
|
||||||
|
|
||||||
|
```
|
||||||
|
let query = """
|
||||||
|
[:find ?name ?cat
|
||||||
|
:in ?type
|
||||||
|
:where
|
||||||
|
[?c :community/name ?name]
|
||||||
|
[?c :community/type ?type]
|
||||||
|
[?c :community/category ?cat]]
|
||||||
|
"""
|
||||||
|
mentat.query(query: query)
|
||||||
|
.bind(varName: "?type", toKeyword: ":community.type/website")
|
||||||
|
.run { result in
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Queries can be run and the results returned in a number of different formats. Individual result values are returned as `TypedValues` and
|
||||||
|
the format differences relate to the number and structure of those values. The result format is related to the format provided in the query string.
|
||||||
|
|
||||||
|
- `Rel` - This is the default `run` function and returns a list of rows of values. Queries that wish to have `Rel` results should format their query strings:
|
||||||
|
```
|
||||||
|
let query = """
|
||||||
|
[: find ?a ?b ?c
|
||||||
|
: where ... ]
|
||||||
|
"""
|
||||||
|
mentat.query(query: query)
|
||||||
|
.run { result in
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- `Scalar` - This returns a single value as a result. This can be optional, as the value may not be present. Queries that wish to have `Scalar` results should format their query strings:
|
||||||
|
```
|
||||||
|
let query = """
|
||||||
|
[: find ?a .
|
||||||
|
: where ... ]
|
||||||
|
"""
|
||||||
|
mentat.query(query: query)
|
||||||
|
.runScalar { result in
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- `Coll` - This returns a list of single values as a result. Queries that wish to have `Coll` results should format their query strings:
|
||||||
|
```
|
||||||
|
let query = """
|
||||||
|
[: find [?a ...]
|
||||||
|
: where ... ]
|
||||||
|
"""
|
||||||
|
mentat.query(query: query)
|
||||||
|
.runColl { result in
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- `Tuple` - This returns a single row of values. Queries that wish to have `Tuple` results should format their query strings:
|
||||||
|
```
|
||||||
|
let query = """
|
||||||
|
[: find [?a ?b ?c]
|
||||||
|
: where ... ]
|
||||||
|
"""
|
||||||
|
mentat.query(query: query)
|
||||||
|
.runTuple { result in
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
class Query: OptionalRustObject {
|
||||||
|
|
||||||
|
/**
|
||||||
|
Binds a `Int64` value to the provided variable name.
|
||||||
|
|
||||||
|
- Parameter varName: The name of the variable in the format `?name`.
|
||||||
|
- Parameter value: The value to be bound
|
||||||
|
|
||||||
|
- Throws: `PointerError.pointerConsumed` if the underlying raw pointer has already consumed, which will occur if the query has already been executed.
|
||||||
|
|
||||||
|
- Returns: This `Query` such that further function can be called.
|
||||||
|
*/
|
||||||
|
func bind(varName: String, toLong value: Int64) throws -> Query {
|
||||||
|
query_builder_bind_long(try! self.validPointer(), varName, value)
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Binds a `Entid` value to the provided variable name.
|
||||||
|
|
||||||
|
- Parameter varName: The name of the variable in the format `?name`.
|
||||||
|
- Parameter value: The value to be bound
|
||||||
|
|
||||||
|
- Throws: `PointerError.pointerConsumed` if the underlying raw pointer has already consumed, which will occur if the query has already been executed.
|
||||||
|
|
||||||
|
- Returns: This `Query` such that further function can be called.
|
||||||
|
*/
|
||||||
|
func bind(varName: String, toReference value: Entid) throws -> Query {
|
||||||
|
query_builder_bind_ref(try! self.validPointer(), varName, value)
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Binds a `String` value representing a keyword for an attribute to the provided variable name.
|
||||||
|
Keywords take the format `:namespace/name`.
|
||||||
|
|
||||||
|
- Parameter varName: The name of the variable in the format `?name`.
|
||||||
|
- Parameter value: The value to be bound
|
||||||
|
|
||||||
|
- Throws: `PointerError.pointerConsumed` if the underlying raw pointer has already consumed, which will occur if the query has already been executed.
|
||||||
|
|
||||||
|
- Returns: This `Query` such that further function can be called.
|
||||||
|
*/
|
||||||
|
func bind(varName: String, toReference value: String) throws -> Query {
|
||||||
|
query_builder_bind_ref_kw(try! self.validPointer(), varName, value)
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Binds a keyword `String` value to the provided variable name.
|
||||||
|
Keywords take the format `:namespace/name`.
|
||||||
|
|
||||||
|
- Parameter varName: The name of the variable in the format `?name`.
|
||||||
|
- Parameter value: The value to be bound
|
||||||
|
|
||||||
|
- Throws: `PointerError.pointerConsumed` if the underlying raw pointer has already consumed, which will occur if the query has already been executed.
|
||||||
|
|
||||||
|
- Returns: This `Query` such that further function can be called.
|
||||||
|
*/
|
||||||
|
func bind(varName: String, toKeyword value: String) throws -> Query {
|
||||||
|
query_builder_bind_kw(try! self.validPointer(), varName, value)
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Binds a `Bool` value to the provided variable name.
|
||||||
|
|
||||||
|
- Parameter varName: The name of the variable in the format `?name`.
|
||||||
|
- Parameter value: The value to be bound
|
||||||
|
|
||||||
|
- Throws: `PointerError.pointerConsumed` if the underlying raw pointer has already consumed, which will occur if the query has already been executed.
|
||||||
|
|
||||||
|
- Returns: This `Query` such that further function can be called.
|
||||||
|
*/
|
||||||
|
func bind(varName: String, toBoolean value: Bool) throws -> Query {
|
||||||
|
query_builder_bind_boolean(try! self.validPointer(), varName, value ? 1 : 0)
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Binds a `Double` value to the provided variable name.
|
||||||
|
|
||||||
|
- Parameter varName: The name of the variable in the format `?name`.
|
||||||
|
- Parameter value: The value to be bound
|
||||||
|
|
||||||
|
- Throws: `PointerError.pointerConsumed` if the underlying raw pointer has already consumed, which will occur if the query has already been executed.
|
||||||
|
|
||||||
|
- Returns: This `Query` such that further function can be called.
|
||||||
|
*/
|
||||||
|
func bind(varName: String, toDouble value: Double) throws -> Query {
|
||||||
|
query_builder_bind_double(try! self.validPointer(), varName, value)
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Binds a `Date` value to the provided variable name.
|
||||||
|
|
||||||
|
- Parameter varName: The name of the variable in the format `?name`.
|
||||||
|
- Parameter value: The value to be bound
|
||||||
|
|
||||||
|
- Throws: `PointerError.pointerConsumed` if the underlying raw pointer has already consumed, which will occur if the query has already been executed.
|
||||||
|
|
||||||
|
- Returns: This `Query` such that further function can be called.
|
||||||
|
*/
|
||||||
|
func bind(varName: String, toDate value: Date) throws -> Query {
|
||||||
|
query_builder_bind_timestamp(try! self.validPointer(), varName, value.toMicroseconds())
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Binds a `String` value to the provided variable name.
|
||||||
|
|
||||||
|
- Parameter varName: The name of the variable in the format `?name`.
|
||||||
|
- Parameter value: The value to be bound
|
||||||
|
|
||||||
|
- Throws: `PointerError.pointerConsumed` if the underlying raw pointer has already consumed, which will occur if the query has already been executed.
|
||||||
|
|
||||||
|
- Returns: This `Query` such that further function can be called.
|
||||||
|
*/
|
||||||
|
func bind(varName: String, toString value: String) throws -> Query {
|
||||||
|
query_builder_bind_string(try! self.validPointer(), varName, value)
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Binds a `UUID` value to the provided variable name.
|
||||||
|
|
||||||
|
- Parameter varName: The name of the variable in the format `?name`.
|
||||||
|
- Parameter value: The value to be bound
|
||||||
|
|
||||||
|
- Throws: `PointerError.pointerConsumed` if the underlying raw pointer has already consumed, which will occur if the query has already been executed.
|
||||||
|
|
||||||
|
- Returns: This `Query` such that further function can be called.
|
||||||
|
*/
|
||||||
|
func bind(varName: String, toUuid value: UUID) throws -> Query {
|
||||||
|
var rawUuid = value.uuid
|
||||||
|
withUnsafePointer(to: &rawUuid) { uuidPtr in
|
||||||
|
query_builder_bind_uuid(try! self.validPointer(), varName, uuidPtr)
|
||||||
|
}
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Execute the query with the values bound associated with this `Query` and call the provided callback function with the results as a list of rows of `TypedValues`.
|
||||||
|
|
||||||
|
- Parameter callback: the function to call with the results of this query
|
||||||
|
|
||||||
|
- Throws: `QueryError.executionFailed` if the query fails to execute. This could be because the provided query did not parse, or that
|
||||||
|
variable we incorrectly bound, or that the query provided was not `Rel`.
|
||||||
|
- Throws: `PointerError.pointerConsumed` if the underlying raw pointer has already consumed, which will occur if the query has previously been executed.
|
||||||
|
*/
|
||||||
|
func run(callback: @escaping (RelResult?) -> Void) throws {
|
||||||
|
let result = query_builder_execute(try! self.validPointer())
|
||||||
|
self.raw = nil
|
||||||
|
|
||||||
|
if let err = result.pointee.err {
|
||||||
|
let message = String(cString: err)
|
||||||
|
throw QueryError.executionFailed(message: message)
|
||||||
|
}
|
||||||
|
guard let results = result.pointee.ok else {
|
||||||
|
callback(nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
callback(RelResult(raw: results))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Execute the query with the values bound associated with this `Query` and call the provided callback function with the result as a single `TypedValue`.
|
||||||
|
|
||||||
|
- Parameter callback: the function to call with the results of this query
|
||||||
|
|
||||||
|
- Throws: `QueryError.executionFailed` if the query fails to execute. This could be because the provided query did not parse, that
|
||||||
|
variable we incorrectly bound, or that the query provided was not `Scalar`.
|
||||||
|
- Throws: `PointerError.pointerConsumed` if the underlying raw pointer has already consumed, which will occur if the query has previously been executed.
|
||||||
|
*/
|
||||||
|
func runScalar(callback: @escaping (TypedValue?) -> Void) throws {
|
||||||
|
let result = query_builder_execute_scalar(try! self.validPointer())
|
||||||
|
self.raw = nil
|
||||||
|
|
||||||
|
if let err = result.pointee.err {
|
||||||
|
let message = String(cString: err)
|
||||||
|
throw QueryError.executionFailed(message: message)
|
||||||
|
}
|
||||||
|
guard let results = result.pointee.ok else {
|
||||||
|
callback(nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
callback(TypedValue(raw: OpaquePointer(results)))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Execute the query with the values bound associated with this `Query` and call the provided callback function with the result as a list of single `TypedValues`.
|
||||||
|
|
||||||
|
- Parameter callback: the function to call with the results of this query
|
||||||
|
|
||||||
|
- Throws: `QueryError.executionFailed` if the query fails to execute. This could be because the provided query did not parse, that
|
||||||
|
variable we incorrectly bound, or that the query provided was not `Coll`.
|
||||||
|
- Throws: `PointerError.pointerConsumed` if the underlying raw pointer has already consumed, which will occur if the query has previously been executed.
|
||||||
|
*/
|
||||||
|
func runColl(callback: @escaping (ColResult?) -> Void) throws {
|
||||||
|
let result = query_builder_execute_coll(try! self.validPointer())
|
||||||
|
self.raw = nil
|
||||||
|
|
||||||
|
if let err = result.pointee.err {
|
||||||
|
let message = String(cString: err)
|
||||||
|
throw QueryError.executionFailed(message: message)
|
||||||
|
}
|
||||||
|
guard let results = result.pointee.ok else {
|
||||||
|
callback(nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
callback(ColResult(raw: results))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Execute the query with the values bound associated with this `Query` and call the provided callback function with the result as a list of single `TypedValues`.
|
||||||
|
|
||||||
|
- Parameter callback: the function to call with the results of this query
|
||||||
|
|
||||||
|
- Throws: `QueryError.executionFailed` if the query fails to execute. This could be because the provided query did not parse, that
|
||||||
|
variable we incorrectly bound, or that the query provided was not `Tuple`.
|
||||||
|
- Throws: `PointerError.pointerConsumed` if the underlying raw pointer has already consumed, which will occur if the query has previously been executed.
|
||||||
|
*/
|
||||||
|
func runTuple(callback: @escaping (TupleResult?) -> Void) throws {
|
||||||
|
let result = query_builder_execute_tuple(try! self.validPointer())
|
||||||
|
self.raw = nil
|
||||||
|
|
||||||
|
if let err = result.pointee.err {
|
||||||
|
let message = String(cString: err)
|
||||||
|
throw QueryError.executionFailed(message: message)
|
||||||
|
}
|
||||||
|
guard let results = result.pointee.ok else {
|
||||||
|
callback(nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
callback(TupleResult(raw: OpaquePointer(results)))
|
||||||
|
}
|
||||||
|
|
||||||
|
override func cleanup(pointer: OpaquePointer) {
|
||||||
|
query_builder_destroy(pointer)
|
||||||
|
}
|
||||||
|
}
|
106
sdks/swift/Mentat/Mentat/Query/RelResult.swift
Normal file
106
sdks/swift/Mentat/Mentat/Query/RelResult.swift
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
/* Copyright 2018 Mozilla
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||||
|
* this file except in compliance with the License. You may obtain a copy of the
|
||||||
|
* License at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
|
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations under the License. */
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import MentatStore
|
||||||
|
|
||||||
|
/**
|
||||||
|
Wraps a `Rel` result from a Mentat query.
|
||||||
|
A `Rel` result is a list of rows of `TypedValues`.
|
||||||
|
Individual rows can be fetched or the set can be iterated.
|
||||||
|
|
||||||
|
To fetch individual rows from a `RelResult` use `row(Int32)`.
|
||||||
|
|
||||||
|
```
|
||||||
|
query.run { rows in
|
||||||
|
let row1 = rows.row(0)
|
||||||
|
let row2 = rows.row(1)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
To iterate over the result set use standard iteration flows.
|
||||||
|
```
|
||||||
|
query.run { rows in
|
||||||
|
rows.forEach { row in
|
||||||
|
...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that iteration is consuming and can only be done once.
|
||||||
|
*/
|
||||||
|
class RelResult: OptionalRustObject {
|
||||||
|
|
||||||
|
/**
|
||||||
|
Fetch the row at the requested index.
|
||||||
|
|
||||||
|
- Parameter index: the index of the row to be fetched
|
||||||
|
|
||||||
|
- Throws: `PointerError.pointerConsumed` if the result set has already been iterated.
|
||||||
|
|
||||||
|
- Returns: The row at the requested index as a `TupleResult`, if present, or nil if there is no row at that index.
|
||||||
|
*/
|
||||||
|
func row(index: Int32) throws -> TupleResult? {
|
||||||
|
guard let row = row_at_index(try self.validPointer(), index) else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return TupleResult(raw: row)
|
||||||
|
}
|
||||||
|
|
||||||
|
override func cleanup(pointer: OpaquePointer) {
|
||||||
|
destroy(UnsafeMutableRawPointer(pointer))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Iterator for `RelResult`.
|
||||||
|
|
||||||
|
To iterate over the result set use standard iteration flows.
|
||||||
|
```
|
||||||
|
query.run { result in
|
||||||
|
rows.forEach { row in
|
||||||
|
...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that iteration is consuming and can only be done once.
|
||||||
|
*/
|
||||||
|
class RelResultIterator: OptionalRustObject, IteratorProtocol {
|
||||||
|
typealias Element = TupleResult
|
||||||
|
|
||||||
|
init(iter: OpaquePointer?) {
|
||||||
|
super.init(raw: iter)
|
||||||
|
}
|
||||||
|
|
||||||
|
func next() -> Element? {
|
||||||
|
guard let iter = self.raw,
|
||||||
|
let rowPtr = rows_iter_next(iter) else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return TupleResult(raw: rowPtr)
|
||||||
|
}
|
||||||
|
|
||||||
|
override func cleanup(pointer: OpaquePointer) {
|
||||||
|
typed_value_result_set_iter_destroy(pointer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension RelResult: Sequence {
|
||||||
|
func makeIterator() -> RelResultIterator {
|
||||||
|
do {
|
||||||
|
let rowIter = rows_iter(try self.validPointer())
|
||||||
|
self.raw = nil
|
||||||
|
return RelResultIterator(iter: rowIter)
|
||||||
|
} catch {
|
||||||
|
return RelResultIterator(iter: nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
217
sdks/swift/Mentat/Mentat/Query/TupleResult.swift
Normal file
217
sdks/swift/Mentat/Mentat/Query/TupleResult.swift
Normal file
|
@ -0,0 +1,217 @@
|
||||||
|
/* Copyright 2018 Mozilla
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||||
|
* this file except in compliance with the License. You may obtain a copy of the
|
||||||
|
* License at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
|
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations under the License. */
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import MentatStore
|
||||||
|
|
||||||
|
/**
|
||||||
|
Wraps a `Tuple` result from a Mentat query.
|
||||||
|
A `Tuple` result is a list of `TypedValues`.
|
||||||
|
Individual values can be fetched as `TypedValues` or converted into a requested type.
|
||||||
|
|
||||||
|
Values can be fetched as one of the following types:
|
||||||
|
- `TypedValue`
|
||||||
|
- `Int64`
|
||||||
|
- `Entid`
|
||||||
|
- `Keyword`
|
||||||
|
- `Bool`
|
||||||
|
- `Double`
|
||||||
|
- `Date`
|
||||||
|
- `String`
|
||||||
|
- `UUID`.
|
||||||
|
*/
|
||||||
|
class TupleResult: OptionalRustObject {
|
||||||
|
|
||||||
|
/**
|
||||||
|
Return the `TypedValue` at the specified index.
|
||||||
|
If the index is greater than the number of values then this function will crash.
|
||||||
|
|
||||||
|
- Parameter index: The index of the value to fetch.
|
||||||
|
|
||||||
|
- Returns: The `TypedValue` at that index.
|
||||||
|
*/
|
||||||
|
func get(index: Int) -> TypedValue {
|
||||||
|
return TypedValue(raw: value_at_index(self.raw!, Int32(index)))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Return the `Int64` at the specified index.
|
||||||
|
If the index is greater than the number of values then this function will crash.
|
||||||
|
If the value type if the `TypedValue` at this index is not `Long` then this function will crash.
|
||||||
|
|
||||||
|
- Parameter index: The index of the value to fetch.
|
||||||
|
|
||||||
|
- Returns: The `Int64` at that index.
|
||||||
|
*/
|
||||||
|
func asLong(index: Int) -> Int64 {
|
||||||
|
return value_at_index_as_long(self.raw!, Int32(index))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Return the `Entid` at the specified index.
|
||||||
|
If the index is greater than the number of values then this function will crash.
|
||||||
|
If the value type if the `TypedValue` at this index is not `Ref` then this function will crash.
|
||||||
|
|
||||||
|
- Parameter index: The index of the value to fetch.
|
||||||
|
|
||||||
|
- Returns: The `Entid` at that index.
|
||||||
|
*/
|
||||||
|
func asEntid(index: Int) -> Entid {
|
||||||
|
return value_at_index_as_entid(self.raw!, Int32(index))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Return the keyword `String` at the specified index.
|
||||||
|
If the index is greater than the number of values then this function will crash.
|
||||||
|
If the value type if the `TypedValue` at this index is not `Keyword` then this function will crash.
|
||||||
|
|
||||||
|
- Parameter index: The index of the value to fetch.
|
||||||
|
|
||||||
|
- Returns: The keyword `String` at that index.
|
||||||
|
*/
|
||||||
|
func asKeyword(index: Int) -> String {
|
||||||
|
return String(cString: value_at_index_as_kw(self.raw!, Int32(index)))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Return the `Bool` at the specified index.
|
||||||
|
If the index is greater than the number of values then this function will crash.
|
||||||
|
If the value type if the `TypedValue` at this index is not `Boolean` then this function will crash.
|
||||||
|
|
||||||
|
- Parameter index: The index of the value to fetch.
|
||||||
|
|
||||||
|
- Returns: The `Bool` at that index.
|
||||||
|
*/
|
||||||
|
func asBool(index: Int) -> Bool {
|
||||||
|
return value_at_index_as_boolean(self.raw!, Int32(index)) == 0 ? false : true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Return the `Double` at the specified index.
|
||||||
|
If the index is greater than the number of values then this function will crash.
|
||||||
|
If the value type if the `TypedValue` at this index is not `Double` then this function will crash.
|
||||||
|
|
||||||
|
- Parameter index: The index of the value to fetch.
|
||||||
|
|
||||||
|
- Returns: The `Double` at that index.
|
||||||
|
*/
|
||||||
|
func asDouble(index: Int) -> Double {
|
||||||
|
return value_at_index_as_double(self.raw!, Int32(index))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Return the `Date` at the specified index.
|
||||||
|
If the index is greater than the number of values then this function will crash.
|
||||||
|
If the value type if the `TypedValue` at this index is not `Instant` then this function will crash.
|
||||||
|
|
||||||
|
- Parameter index: The index of the value to fetch.
|
||||||
|
|
||||||
|
- Returns: The `Date` at that index.
|
||||||
|
*/
|
||||||
|
func asDate(index: Int) -> Date {
|
||||||
|
return Date(timeIntervalSince1970: TimeInterval(value_at_index_as_timestamp(self.raw!, Int32(index))))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Return the `String` at the specified index.
|
||||||
|
If the index is greater than the number of values then this function will crash.
|
||||||
|
If the value type if the `TypedValue` at this index is not `String` then this function will crash.
|
||||||
|
|
||||||
|
- Parameter index: The index of the value to fetch.
|
||||||
|
|
||||||
|
- Returns: The `String` at that index.
|
||||||
|
*/
|
||||||
|
func asString(index: Int) -> String {
|
||||||
|
return String(cString: value_at_index_as_string(self.raw!, Int32(index)))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Return the `UUID` at the specified index.
|
||||||
|
If the index is greater than the number of values then this function will crash.
|
||||||
|
If the value type if the `TypedValue` at this index is not `Uuid` then this function will crash.
|
||||||
|
|
||||||
|
- Parameter index: The index of the value to fetch.
|
||||||
|
|
||||||
|
- Returns: The `UUID` at that index.
|
||||||
|
*/
|
||||||
|
func asUUID(index: Int) -> UUID? {
|
||||||
|
return UUID(uuid: value_at_index_as_uuid(self.raw!, Int32(index)).pointee)
|
||||||
|
}
|
||||||
|
|
||||||
|
override func cleanup(pointer: OpaquePointer) {
|
||||||
|
typed_value_list_destroy(pointer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Wraps a `Coll` result from a Mentat query.
|
||||||
|
A `Coll` result is a list of rows of single values of type `TypedValue`.
|
||||||
|
Values for individual rows can be fetched as `TypedValue` or converted into a requested type.
|
||||||
|
|
||||||
|
Row values can be fetched as one of the following types:
|
||||||
|
- `TypedValue`
|
||||||
|
- `Int64`
|
||||||
|
- `Entid`
|
||||||
|
- `Keyword`
|
||||||
|
- `Bool`
|
||||||
|
- `Double`
|
||||||
|
- `Date`
|
||||||
|
- `String`
|
||||||
|
- `UUID`.
|
||||||
|
*/
|
||||||
|
class ColResult: TupleResult {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Iterator for `ColResult`.
|
||||||
|
|
||||||
|
To iterate over the result set use standard iteration flows.
|
||||||
|
```
|
||||||
|
query.runColl { rows in
|
||||||
|
rows.forEach { value in
|
||||||
|
...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that iteration is consuming and can only be done once.
|
||||||
|
*/
|
||||||
|
class ColResultIterator: OptionalRustObject, IteratorProtocol {
|
||||||
|
typealias Element = TypedValue
|
||||||
|
|
||||||
|
init(iter: OpaquePointer?) {
|
||||||
|
super.init(raw: iter)
|
||||||
|
}
|
||||||
|
|
||||||
|
func next() -> Element? {
|
||||||
|
guard let iter = self.raw,
|
||||||
|
let rowPtr = values_iter_next(iter) else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return TypedValue(raw: rowPtr)
|
||||||
|
}
|
||||||
|
|
||||||
|
override func cleanup(pointer: OpaquePointer) {
|
||||||
|
typed_value_list_iter_destroy(pointer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension ColResult: Sequence {
|
||||||
|
func makeIterator() -> ColResultIterator {
|
||||||
|
defer {
|
||||||
|
self.raw = nil
|
||||||
|
}
|
||||||
|
guard let raw = self.raw else {
|
||||||
|
return ColResultIterator(iter: nil)
|
||||||
|
}
|
||||||
|
let rowIter = values_iter(raw)
|
||||||
|
return ColResultIterator(iter: rowIter)
|
||||||
|
}
|
||||||
|
}
|
68
sdks/swift/Mentat/Mentat/Rust/OptionalRustObject.swift
Normal file
68
sdks/swift/Mentat/Mentat/Rust/OptionalRustObject.swift
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
/* Copyright 2018 Mozilla
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||||
|
* this file except in compliance with the License. You may obtain a copy of the
|
||||||
|
* License at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
|
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations under the License. */
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import MentatStore
|
||||||
|
|
||||||
|
/**
|
||||||
|
Base class that wraps an optional `OpaquePointer` representing a pointer to a Rust object.
|
||||||
|
This class should be used to wrap Rust pointer that point to consuming structs, that is, calling a function
|
||||||
|
for that Rust pointer, will cause Rust to destroy the pointer, leaving the Swift pointer dangling.
|
||||||
|
These classes are responsible for ensuring that their raw `OpaquePointer` are `nil`led after calling a consuming
|
||||||
|
FFI function.
|
||||||
|
This class provides cleanup functions on deinit, ensuring that all classes
|
||||||
|
that inherit from it will have their `OpaquePointer` destroyed when the Swift wrapper is destroyed.
|
||||||
|
If a class does not override `cleanup` then a `fatalError` is thrown.
|
||||||
|
The optional pointer is managed here such that is the pointer is nil, then the cleanup function is not called
|
||||||
|
ensuring that we do not double free the pointer on exit.
|
||||||
|
*/
|
||||||
|
class OptionalRustObject: Destroyable {
|
||||||
|
var raw: OpaquePointer?
|
||||||
|
lazy var uniqueId: ObjectIdentifier = {
|
||||||
|
ObjectIdentifier(self)
|
||||||
|
}()
|
||||||
|
|
||||||
|
init(raw: UnsafeMutableRawPointer) {
|
||||||
|
self.raw = OpaquePointer(raw)
|
||||||
|
}
|
||||||
|
|
||||||
|
init(raw: OpaquePointer?) {
|
||||||
|
self.raw = raw
|
||||||
|
}
|
||||||
|
|
||||||
|
func intoRaw() -> OpaquePointer? {
|
||||||
|
return self.raw
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
guard let raw = self.raw else { return }
|
||||||
|
self.cleanup(pointer: raw)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Provides a non-optional `OpaquePointer` if one exists for this class.
|
||||||
|
|
||||||
|
- Throws: `Pointer.pointerConsumed` if the raw pointer wrapped by this class is nil
|
||||||
|
|
||||||
|
- Returns: the raw `OpaquePointer` wrapped by this class.
|
||||||
|
*/
|
||||||
|
func validPointer() throws -> OpaquePointer {
|
||||||
|
guard let r = self.raw else {
|
||||||
|
throw PointerError.pointerConsumed
|
||||||
|
}
|
||||||
|
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func cleanup(pointer: OpaquePointer) {
|
||||||
|
fatalError("\(cleanup) is not implemented.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
49
sdks/swift/Mentat/Mentat/Rust/RustObject.swift
Normal file
49
sdks/swift/Mentat/Mentat/Rust/RustObject.swift
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
/* Copyright 2018 Mozilla
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||||
|
* this file except in compliance with the License. You may obtain a copy of the
|
||||||
|
* License at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
|
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations under the License. */
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import MentatStore
|
||||||
|
|
||||||
|
protocol Destroyable {
|
||||||
|
func cleanup(pointer: OpaquePointer)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Base class that wraps an non-optional `OpaquePointer` representing a pointer to a Rust object.
|
||||||
|
This class provides cleanup functions on deinit, ensuring that all classes
|
||||||
|
that inherit from it will have their `OpaquePointer` destroyed when the Swift wrapper is destroyed.
|
||||||
|
If a class does not override `cleanup` then a `fatalError` is thrown.
|
||||||
|
*/
|
||||||
|
public class RustObject: Destroyable {
|
||||||
|
var raw: OpaquePointer
|
||||||
|
|
||||||
|
init(raw: OpaquePointer) {
|
||||||
|
self.raw = raw
|
||||||
|
}
|
||||||
|
|
||||||
|
init(raw: UnsafeMutableRawPointer) {
|
||||||
|
self.raw = OpaquePointer(raw)
|
||||||
|
}
|
||||||
|
|
||||||
|
init?(raw: OpaquePointer?) {
|
||||||
|
guard let r = raw else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
self.raw = r
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
self.cleanup(pointer: self.raw)
|
||||||
|
}
|
||||||
|
|
||||||
|
func cleanup(pointer: OpaquePointer) {
|
||||||
|
fatalError("\(cleanup) is not implemented.")
|
||||||
|
}
|
||||||
|
}
|
61
sdks/swift/Mentat/Mentat/Transact/TxReport.swift
Normal file
61
sdks/swift/Mentat/Mentat/Transact/TxReport.swift
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
/* Copyright 2018 Mozilla
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||||
|
* this file except in compliance with the License. You may obtain a copy of the
|
||||||
|
* License at http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
|
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations under the License. */
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
import MentatStore
|
||||||
|
|
||||||
|
/**
|
||||||
|
This class wraps a raw pointer than points to a Rust `TxReport` object.
|
||||||
|
|
||||||
|
The `TxReport` contains information about a successful Mentat transaction.
|
||||||
|
|
||||||
|
This information includes:
|
||||||
|
- `txId` - the identifier for the transaction.
|
||||||
|
- `txInstant` - the time that the transaction occured.
|
||||||
|
- a map of temporary identifiers provided in the transaction and the `Entid`s that they were mapped to,
|
||||||
|
|
||||||
|
Access an `Entid` for a temporary identifier that was provided in the transaction can be done through `entid(String:)`.
|
||||||
|
|
||||||
|
```
|
||||||
|
let report = mentat.transact("[[:db/add "a" :foo/boolean true]]")
|
||||||
|
let aEntid = report.entid(forTempId: "a")
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
class TxReport: RustObject {
|
||||||
|
|
||||||
|
// The identifier for the transaction.
|
||||||
|
public var txId: Entid {
|
||||||
|
return tx_report_get_entid(self.raw)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The time that the transaction occured.
|
||||||
|
public var txInstant: Date {
|
||||||
|
return Date(timeIntervalSince1970: TimeInterval(tx_report_get_tx_instant(self.raw)))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Access an `Entid` for a temporary identifier that was provided in the transaction can be done through `entid(String:)`.
|
||||||
|
|
||||||
|
- Parameter tempId: A `String` representing the temporary identifier to fetch the `Entid` for.
|
||||||
|
|
||||||
|
- Returns: The `Entid` for the temporary identifier, if present, otherwise `nil`.
|
||||||
|
*/
|
||||||
|
public func entid(forTempId tempId: String) -> Entid? {
|
||||||
|
guard let entidPtr = tx_report_entity_for_temp_id(self.raw, tempId) else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return entidPtr.pointee
|
||||||
|
}
|
||||||
|
|
||||||
|
override func cleanup(pointer: OpaquePointer) {
|
||||||
|
tx_report_destroy(pointer)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue