Compare commits
19 commits
master
...
fluffyemil
Author | SHA1 | Date | |
---|---|---|---|
|
9b23ee0855 | ||
|
0e0316991a | ||
|
c2dbf2c304 | ||
|
31556023e5 | ||
|
f38f27f848 | ||
|
5b52e2115f | ||
|
515300e521 | ||
|
0c758ab6d3 | ||
|
f924da4517 | ||
|
a4fa7fc385 | ||
|
08387d4754 | ||
|
b83bec566a | ||
|
3bd90e4dc7 | ||
|
73a26132d3 | ||
|
717852c531 | ||
|
5a1102bf14 | ||
|
d193c9c79e | ||
|
1518e180df | ||
|
bda1ec4d0a |
76 changed files with 6545 additions and 288 deletions
25
.gitignore
vendored
25
.gitignore
vendored
|
@ -54,3 +54,28 @@ pom.xml.asc
|
|||
/fixtures/*.db-shm
|
||||
/fixtures/*.db-wal
|
||||
/query-parser/out/
|
||||
## Build generated
|
||||
/sdks/swift/Mentat/build/
|
||||
DerivedData
|
||||
build.xcarchive
|
||||
|
||||
## Various settings
|
||||
*.pbxuser
|
||||
!default.pbxuser
|
||||
*.mode1v3
|
||||
!default.mode1v3
|
||||
*.mode2v3
|
||||
!default.mode2v3
|
||||
*.perspectivev3
|
||||
!default.perspectivev3
|
||||
/sdks/swift/Mentat/*.xcodeproj/project.xcworkspace/xcuserdata
|
||||
|
||||
## Other
|
||||
*.xccheckout
|
||||
*.moved-aside
|
||||
*.xcuserstate
|
||||
*.xcscmblueprint
|
||||
|
||||
## Obj-C/Swift specific
|
||||
*.hmap
|
||||
*.ipa
|
4
android_build_all.sh
Executable file
4
android_build_all.sh
Executable file
|
@ -0,0 +1,4 @@
|
|||
# This will eventually become a complete build script, not just for Android
|
||||
cargo build -p mentat_ffi --target i686-linux-android --release
|
||||
cargo build -p mentat_ffi --target armv7-linux-androideabi --release
|
||||
cargo build -p mentat_ffi --target aarch64-linux-android --release
|
|
@ -19,7 +19,7 @@ use rustc_version::{
|
|||
|
||||
/// MIN_VERSION should be changed when there's a new minimum version of rustc required
|
||||
/// to build the project.
|
||||
static MIN_VERSION: &'static str = "1.24.0";
|
||||
static MIN_VERSION: &'static str = "1.25.0";
|
||||
|
||||
fn main() {
|
||||
let ver = version().unwrap();
|
||||
|
|
|
@ -755,6 +755,27 @@ impl Binding {
|
|||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_c_string(self) -> Option<*mut c_char> {
|
||||
match self {
|
||||
Binding::Scalar(v) => v.into_c_string(),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_kw_c_string(self) -> Option<*mut c_char> {
|
||||
match self {
|
||||
Binding::Scalar(v) => v.into_kw_c_string(),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_uuid_c_string(self) -> Option<*mut c_char> {
|
||||
match self {
|
||||
Binding::Scalar(v) => v.into_uuid_c_string(),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -1,10 +1,14 @@
|
|||
[package]
|
||||
name = "mentat_ffi"
|
||||
version = "0.1.0"
|
||||
version = "0.0.1"
|
||||
authors = ["Emily Toop <etoop@mozilla.com>"]
|
||||
|
||||
[lib]
|
||||
name = "mentat_ffi"
|
||||
crate-type = ["lib", "staticlib", "cdylib"]
|
||||
|
||||
[dependencies]
|
||||
libc = "0.2"
|
||||
|
||||
[dependencies.mentat]
|
||||
path = ".."
|
||||
path = "../"
|
||||
|
|
802
ffi/src/lib.rs
802
ffi/src/lib.rs
File diff suppressed because it is too large
Load diff
|
@ -19,19 +19,18 @@ pub mod strings {
|
|||
Keyword,
|
||||
};
|
||||
|
||||
pub fn c_char_to_string(cchar: *const c_char) -> String {
|
||||
pub fn c_char_to_string(cchar: *const c_char) -> &'static str {
|
||||
let c_str = unsafe { CStr::from_ptr(cchar) };
|
||||
let r_str = c_str.to_str().unwrap_or("");
|
||||
r_str.to_string()
|
||||
c_str.to_str().unwrap_or("")
|
||||
}
|
||||
|
||||
pub fn string_to_c_char<T>(r_string: T) -> *mut c_char where T: Into<String> {
|
||||
CString::new(r_string.into()).unwrap().into_raw()
|
||||
}
|
||||
|
||||
pub fn kw_from_string(keyword_string: &'static str) -> Keyword {
|
||||
// TODO: validate. The input might not be a keyword!
|
||||
pub fn kw_from_string(mut keyword_string: String) -> Keyword {
|
||||
let attr_name = keyword_string.split_off(1);
|
||||
let attr_name = keyword_string.trim_left_matches(":");
|
||||
let parts: Vec<&str> = attr_name.split("/").collect();
|
||||
Keyword::namespaced(parts[0], parts[1])
|
||||
}
|
||||
|
|
9
sdks/android/Mentat/.gitignore
vendored
Normal file
9
sdks/android/Mentat/.gitignore
vendored
Normal file
|
@ -0,0 +1,9 @@
|
|||
*.iml
|
||||
.gradle
|
||||
/local.properties
|
||||
/.idea/workspace.xml
|
||||
/.idea/libraries
|
||||
.DS_Store
|
||||
/build
|
||||
/captures
|
||||
.externalNativeBuild
|
17
sdks/android/Mentat/.project
Normal file
17
sdks/android/Mentat/.project
Normal file
|
@ -0,0 +1,17 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>Mentat</name>
|
||||
<comment>Project Mentat created by Buildship.</comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.buildship.core.gradleprojectbuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.buildship.core.gradleprojectnature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
27
sdks/android/Mentat/build.gradle
Normal file
27
sdks/android/Mentat/build.gradle
Normal file
|
@ -0,0 +1,27 @@
|
|||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
|
||||
buildscript {
|
||||
ext.kotlin_version = '1.1.60'
|
||||
repositories {
|
||||
jcenter()
|
||||
google()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.1.1'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
}
|
||||
}
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
jcenter()
|
||||
google()
|
||||
}
|
||||
}
|
||||
|
||||
task clean(type: Delete) {
|
||||
delete rootProject.buildDir
|
||||
}
|
17
sdks/android/Mentat/gradle.properties
Normal file
17
sdks/android/Mentat/gradle.properties
Normal file
|
@ -0,0 +1,17 @@
|
|||
# Project-wide Gradle settings.
|
||||
|
||||
# IDE (e.g. Android Studio) users:
|
||||
# Gradle settings configured through the IDE *will override*
|
||||
# any settings specified in this file.
|
||||
|
||||
# For more details on how to configure your build environment visit
|
||||
# http://www.gradle.org/docs/current/userguide/build_environment.html
|
||||
|
||||
# Specifies the JVM arguments used for the daemon process.
|
||||
# The setting is particularly useful for tweaking memory settings.
|
||||
org.gradle.jvmargs=-Xmx1536m
|
||||
|
||||
# When configured, Gradle will run in incubating parallel mode.
|
||||
# This option should only be used with decoupled projects. More details, visit
|
||||
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
||||
# org.gradle.parallel=true
|
6
sdks/android/Mentat/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
6
sdks/android/Mentat/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
#Thu Mar 29 09:07:24 BST 2018
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip
|
160
sdks/android/Mentat/gradlew
vendored
Executable file
160
sdks/android/Mentat/gradlew
vendored
Executable file
|
@ -0,0 +1,160 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
##############################################################################
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS=""
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
|
||||
warn ( ) {
|
||||
echo "$*"
|
||||
}
|
||||
|
||||
die ( ) {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
esac
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
# Need this for relative symlinks.
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG=`dirname "$PRG"`"/$link"
|
||||
fi
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD="java"
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
MAX_FD="$MAX_FD_LIMIT"
|
||||
fi
|
||||
ulimit -n $MAX_FD
|
||||
if [ $? -ne 0 ] ; then
|
||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||
fi
|
||||
else
|
||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Darwin, add options to specify how the application appears in the dock
|
||||
if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin, switch paths to Windows format before running java
|
||||
if $cygwin ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
SEP=""
|
||||
for dir in $ROOTDIRSRAW ; do
|
||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||
SEP="|"
|
||||
done
|
||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||
# Add a user-defined pattern to the cygpath arguments
|
||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||
fi
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
i=0
|
||||
for arg in "$@" ; do
|
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=$((i+1))
|
||||
done
|
||||
case $i in
|
||||
(0) set -- ;;
|
||||
(1) set -- "$args0" ;;
|
||||
(2) set -- "$args0" "$args1" ;;
|
||||
(3) set -- "$args0" "$args1" "$args2" ;;
|
||||
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
|
||||
function splitJvmOpts() {
|
||||
JVM_OPTS=("$@")
|
||||
}
|
||||
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
|
||||
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
|
||||
|
||||
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
|
90
sdks/android/Mentat/gradlew.bat
vendored
Normal file
90
sdks/android/Mentat/gradlew.bat
vendored
Normal file
|
@ -0,0 +1,90 @@
|
|||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS=
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:init
|
||||
@rem Get command-line arguments, handling Windowz variants
|
||||
|
||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||
if "%@eval[2+2]" == "4" goto 4NT_args
|
||||
|
||||
:win9xME_args
|
||||
@rem Slurp the command line arguments.
|
||||
set CMD_LINE_ARGS=
|
||||
set _SKIP=2
|
||||
|
||||
:win9xME_args_slurp
|
||||
if "x%~1" == "x" goto execute
|
||||
|
||||
set CMD_LINE_ARGS=%*
|
||||
goto execute
|
||||
|
||||
:4NT_args
|
||||
@rem Get arguments from the 4NT Shell from JP Software
|
||||
set CMD_LINE_ARGS=%$
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
6
sdks/android/Mentat/library/.classpath
Normal file
6
sdks/android/Mentat/library/.classpath
Normal file
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8/"/>
|
||||
<classpathentry kind="con" path="org.eclipse.buildship.core.gradleclasspathcontainer"/>
|
||||
<classpathentry kind="output" path="bin/default"/>
|
||||
</classpath>
|
1
sdks/android/Mentat/library/.gitignore
vendored
Normal file
1
sdks/android/Mentat/library/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
/build
|
23
sdks/android/Mentat/library/.project
Normal file
23
sdks/android/Mentat/library/.project
Normal file
|
@ -0,0 +1,23 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>app</name>
|
||||
<comment>Project app created by Buildship.</comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.buildship.core.gradleprojectbuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
<nature>org.eclipse.buildship.core.gradleprojectnature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
42
sdks/android/Mentat/library/build.gradle
Normal file
42
sdks/android/Mentat/library/build.gradle
Normal file
|
@ -0,0 +1,42 @@
|
|||
apply plugin: 'com.android.library'
|
||||
apply plugin: 'kotlin-android'
|
||||
|
||||
android {
|
||||
compileSdkVersion 26
|
||||
defaultConfig {
|
||||
minSdkVersion 26
|
||||
targetSdkVersion 26
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
androidTest.assets.srcDirs += '../../../../fixtures'
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2', {
|
||||
exclude group: 'com.android.support', module: 'support-annotations'
|
||||
})
|
||||
androidTestImplementation 'com.android.support:support-annotations:24.0.0'
|
||||
androidTestImplementation 'com.android.support.test:runner:0.5'
|
||||
androidTestImplementation 'com.android.support.test:rules:0.5'
|
||||
implementation 'com.android.support:appcompat-v7:26.1.0'
|
||||
implementation 'com.android.support.constraint:constraint-layout:1.0.2'
|
||||
implementation 'com.android.support:design:26.1.0'
|
||||
testImplementation 'junit:junit:4.12'
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
|
||||
implementation 'net.java.dev.jna:jna:4.5.1'
|
||||
}
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
25
sdks/android/Mentat/library/proguard-rules.pro
vendored
Normal file
25
sdks/android/Mentat/library/proguard-rules.pro
vendored
Normal file
|
@ -0,0 +1,25 @@
|
|||
# Add project specific ProGuard rules here.
|
||||
# By default, the flags in this file are appended to flags specified
|
||||
# in ~/.android-sdk/tools/proguard/proguard-android.txt
|
||||
# You can edit the include path and order by changing the proguardFiles
|
||||
# directive in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# Add any project specific keep options here:
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
|
@ -0,0 +1,27 @@
|
|||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||
* 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. */
|
||||
|
||||
package com.mozilla.mentat;
|
||||
|
||||
import java.util.EventListener;
|
||||
|
||||
interface ExpectationEventListener extends EventListener {
|
||||
public void fulfill();
|
||||
}
|
||||
|
||||
public class Expectation implements ExpectationEventListener {
|
||||
public boolean isFulfilled = false;
|
||||
public void fulfill() {
|
||||
this.isFulfilled = true;
|
||||
synchronized (this) {
|
||||
notifyAll( );
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,725 @@
|
|||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||
* 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. */
|
||||
|
||||
package com.mozilla.mentat;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.AssetManager;
|
||||
import android.support.test.InstrumentationRegistry;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.text.DateFormat;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Instrumentation test, which will execute on an Android device.
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class FFIIntegrationTest {
|
||||
|
||||
Mentat mentat = null;
|
||||
|
||||
@Test
|
||||
public void openInMemoryStoreSucceeds() throws Exception {
|
||||
Mentat mentat = new Mentat();
|
||||
assertNotNull(mentat);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void openStoreInLocationSucceeds() throws Exception {
|
||||
Context context = InstrumentationRegistry.getTargetContext();
|
||||
String path = context.getDatabasePath("test.db").getAbsolutePath();
|
||||
Mentat mentat = new Mentat(path);
|
||||
assertNotNull(mentat);
|
||||
}
|
||||
|
||||
public String readFile(String fileName) {
|
||||
Context testContext = InstrumentationRegistry.getInstrumentation().getContext();
|
||||
AssetManager assetManager = testContext.getAssets();
|
||||
try {
|
||||
InputStream inputStream = assetManager.open(fileName);
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
|
||||
StringBuilder out = new StringBuilder();
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
out.append(line + "\n");
|
||||
}
|
||||
return out.toString();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public TxReport transactCitiesSchema(Mentat mentat) {
|
||||
String citiesSchema = this.readFile("cities.schema");
|
||||
return mentat.transact(citiesSchema);
|
||||
}
|
||||
|
||||
public TxReport transactSeattleData(Mentat mentat) {
|
||||
String seattleData = this.readFile("all_seattle.edn");
|
||||
return mentat.transact(seattleData);
|
||||
}
|
||||
|
||||
public Mentat openAndInitializeCitiesStore() {
|
||||
if (this.mentat == null) {
|
||||
this.mentat = new Mentat();
|
||||
this.transactCitiesSchema(mentat);
|
||||
this.transactSeattleData(mentat);
|
||||
}
|
||||
|
||||
return this.mentat;
|
||||
}
|
||||
|
||||
public TxReport populateWithTypesSchema(Mentat mentat) {
|
||||
String schema = "[\n" +
|
||||
" [:db/add \"b\" :db/ident :foo/boolean]\n" +
|
||||
" [:db/add \"b\" :db/valueType :db.type/boolean]\n" +
|
||||
" [:db/add \"b\" :db/cardinality :db.cardinality/one]\n" +
|
||||
" [:db/add \"l\" :db/ident :foo/long]\n" +
|
||||
" [:db/add \"l\" :db/valueType :db.type/long]\n" +
|
||||
" [:db/add \"l\" :db/cardinality :db.cardinality/one]\n" +
|
||||
" [:db/add \"r\" :db/ident :foo/ref]\n" +
|
||||
" [:db/add \"r\" :db/valueType :db.type/ref]\n" +
|
||||
" [:db/add \"r\" :db/cardinality :db.cardinality/one]\n" +
|
||||
" [:db/add \"i\" :db/ident :foo/instant]\n" +
|
||||
" [:db/add \"i\" :db/valueType :db.type/instant]\n" +
|
||||
" [:db/add \"i\" :db/cardinality :db.cardinality/one]\n" +
|
||||
" [:db/add \"d\" :db/ident :foo/double]\n" +
|
||||
" [:db/add \"d\" :db/valueType :db.type/double]\n" +
|
||||
" [:db/add \"d\" :db/cardinality :db.cardinality/one]\n" +
|
||||
" [:db/add \"s\" :db/ident :foo/string]\n" +
|
||||
" [:db/add \"s\" :db/valueType :db.type/string]\n" +
|
||||
" [:db/add \"s\" :db/cardinality :db.cardinality/one]\n" +
|
||||
" [:db/add \"k\" :db/ident :foo/keyword]\n" +
|
||||
" [:db/add \"k\" :db/valueType :db.type/keyword]\n" +
|
||||
" [:db/add \"k\" :db/cardinality :db.cardinality/one]\n" +
|
||||
" [:db/add \"u\" :db/ident :foo/uuid]\n" +
|
||||
" [:db/add \"u\" :db/valueType :db.type/uuid]\n" +
|
||||
" [:db/add \"u\" :db/cardinality :db.cardinality/one]\n" +
|
||||
" ]";
|
||||
TxReport report = mentat.transact(schema);
|
||||
Long stringEntid = report.getEntidForTempId("s");
|
||||
|
||||
String data = "[\n" +
|
||||
" [:db/add \"a\" :foo/boolean true]\n" +
|
||||
" [:db/add \"a\" :foo/long 25]\n" +
|
||||
" [:db/add \"a\" :foo/instant #inst \"2017-01-01T11:00:00.000Z\"]\n" +
|
||||
" [:db/add \"a\" :foo/double 11.23]\n" +
|
||||
" [:db/add \"a\" :foo/string \"The higher we soar the smaller we appear to those who cannot fly.\"]\n" +
|
||||
" [:db/add \"a\" :foo/keyword :foo/string]\n" +
|
||||
" [:db/add \"a\" :foo/uuid #uuid \"550e8400-e29b-41d4-a716-446655440000\"]\n" +
|
||||
" [:db/add \"b\" :foo/boolean false]\n" +
|
||||
" [:db/add \"b\" :foo/ref "+ stringEntid +"]\n" +
|
||||
" [:db/add \"b\" :foo/long 50]\n" +
|
||||
" [:db/add \"b\" :foo/instant #inst \"2018-01-01T11:00:00.000Z\"]\n" +
|
||||
" [:db/add \"b\" :foo/double 22.46]\n" +
|
||||
" [:db/add \"b\" :foo/string \"Silence is worse; all truths that are kept silent become poisonous.\"]\n" +
|
||||
" [:db/add \"b\" :foo/uuid #uuid \"4cb3f828-752d-497a-90c9-b1fd516d5644\"]\n" +
|
||||
" ]";
|
||||
return mentat.transact(data);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void transactingVocabularySucceeds() {
|
||||
Mentat mentat = new Mentat();
|
||||
TxReport schemaReport = this.transactCitiesSchema(mentat);
|
||||
assertNotNull(schemaReport);
|
||||
assertTrue(schemaReport.getTxId() > 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void transactingEntitiesSucceeds() {
|
||||
Mentat mentat = new Mentat();
|
||||
this.transactCitiesSchema(mentat);
|
||||
TxReport dataReport = this.transactSeattleData(mentat);
|
||||
assertNotNull(dataReport);
|
||||
assertTrue(dataReport.getTxId() > 0);
|
||||
Long entid = dataReport.getEntidForTempId("a17592186045605");
|
||||
assertEquals(65733, entid.longValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void runScalarSucceeds() throws InterruptedException {
|
||||
Mentat mentat = openAndInitializeCitiesStore();
|
||||
String query = "[:find ?n . :in ?name :where [(fulltext $ :community/name ?name) [[?e ?n]]]]";
|
||||
final Expectation expectation = new Expectation();
|
||||
mentat.query(query).bindString("?name", "Wallingford").runScalar(new ScalarResultHandler() {
|
||||
@Override
|
||||
public void handleValue(TypedValue value) {
|
||||
assertNotNull(value);
|
||||
assertEquals("KOMO Communities - Wallingford", value.asString());
|
||||
expectation.fulfill();
|
||||
}
|
||||
});
|
||||
synchronized (expectation) {
|
||||
expectation.wait(1000);
|
||||
}
|
||||
assertTrue(expectation.isFulfilled);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void runCollSucceeds() throws InterruptedException {
|
||||
Mentat mentat = openAndInitializeCitiesStore();
|
||||
String query = "[:find [?when ...] :where [_ :db/txInstant ?when] :order (asc ?when)]";
|
||||
final Expectation expectation = new Expectation();
|
||||
mentat.query(query).runColl(new CollResultHandler() {
|
||||
@Override
|
||||
public void handleList(CollResult list) {
|
||||
assertNotNull(list);
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
assertNotNull(list.asDate(i));
|
||||
}
|
||||
expectation.fulfill();
|
||||
}
|
||||
});
|
||||
synchronized (expectation) {
|
||||
expectation.wait(1000);
|
||||
}
|
||||
assertTrue(expectation.isFulfilled);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void runCollResultIteratorSucceeds() throws InterruptedException {
|
||||
Mentat mentat = openAndInitializeCitiesStore();
|
||||
String query = "[:find [?when ...] :where [_ :db/txInstant ?when] :order (asc ?when)]";
|
||||
final Expectation expectation = new Expectation();
|
||||
mentat.query(query).runColl(new CollResultHandler() {
|
||||
@Override
|
||||
public void handleList(CollResult list) {
|
||||
assertNotNull(list);
|
||||
|
||||
for(TypedValue value: list) {
|
||||
assertNotNull(value.asDate());
|
||||
}
|
||||
expectation.fulfill();
|
||||
}
|
||||
});
|
||||
synchronized (expectation) {
|
||||
expectation.wait(1000);
|
||||
}
|
||||
assertTrue(expectation.isFulfilled);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void runTupleSucceeds() throws InterruptedException {
|
||||
Mentat mentat = openAndInitializeCitiesStore();
|
||||
String query = "[:find [?name ?cat]\n" +
|
||||
" :where\n" +
|
||||
" [?c :community/name ?name]\n" +
|
||||
" [?c :community/type :community.type/website]\n" +
|
||||
" [(fulltext $ :community/category \"food\") [[?c ?cat]]]]";
|
||||
final Expectation expectation = new Expectation();
|
||||
mentat.query(query).runTuple(new TupleResultHandler() {
|
||||
@Override
|
||||
public void handleRow(TupleResult row) {
|
||||
assertNotNull(row);
|
||||
String name = row.asString(0);
|
||||
String category = row.asString(1);
|
||||
assert(name == "Community Harvest of Southwest Seattle");
|
||||
assert(category == "sustainable food");
|
||||
expectation.fulfill();
|
||||
}
|
||||
});
|
||||
synchronized (expectation) {
|
||||
expectation.wait(1000);
|
||||
}
|
||||
assertTrue(expectation.isFulfilled);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void runRelIteratorSucceeds() throws InterruptedException {
|
||||
Mentat mentat = openAndInitializeCitiesStore();
|
||||
String query = "[:find ?name ?cat\n" +
|
||||
" :where\n" +
|
||||
" [?c :community/name ?name]\n" +
|
||||
" [?c :community/type :community.type/website]\n" +
|
||||
" [(fulltext $ :community/category \"food\") [[?c ?cat]]]]";
|
||||
|
||||
final LinkedHashMap expectedResults = new LinkedHashMap<String, String>();
|
||||
expectedResults.put("InBallard", "food");
|
||||
expectedResults.put("Seattle Chinatown Guide", "food");
|
||||
expectedResults.put("Community Harvest of Southwest Seattle", "sustainable food");
|
||||
expectedResults.put("University District Food Bank", "food bank");
|
||||
final Expectation expectation = new Expectation();
|
||||
mentat.query(query).run(new RelResultHandler() {
|
||||
@Override
|
||||
public void handleRows(RelResult rows) {
|
||||
assertNotNull(rows);
|
||||
int index = 0;
|
||||
for (TupleResult row: rows) {
|
||||
String name = row.asString(0);
|
||||
assertNotNull(name);
|
||||
String category = row.asString(1);
|
||||
assertNotNull(category);
|
||||
String expectedCategory = expectedResults.get(name).toString();
|
||||
assertNotNull(expectedCategory);
|
||||
assertEquals(expectedCategory, category);
|
||||
++index;
|
||||
}
|
||||
assertEquals(expectedResults.size(), index);
|
||||
expectation.fulfill();
|
||||
}
|
||||
});
|
||||
synchronized (expectation) {
|
||||
expectation.wait(1000);
|
||||
}
|
||||
assertTrue(expectation.isFulfilled);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void runRelSucceeds() throws InterruptedException {
|
||||
Mentat mentat = openAndInitializeCitiesStore();
|
||||
String query = "[:find ?name ?cat\n" +
|
||||
" :where\n" +
|
||||
" [?c :community/name ?name]\n" +
|
||||
" [?c :community/type :community.type/website]\n" +
|
||||
" [(fulltext $ :community/category \"food\") [[?c ?cat]]]]";
|
||||
|
||||
final LinkedHashMap expectedResults = new LinkedHashMap<String, String>();
|
||||
expectedResults.put("InBallard", "food");
|
||||
expectedResults.put("Seattle Chinatown Guide", "food");
|
||||
expectedResults.put("Community Harvest of Southwest Seattle", "sustainable food");
|
||||
expectedResults.put("University District Food Bank", "food bank");
|
||||
final Expectation expectation = new Expectation();
|
||||
mentat.query(query).run(new RelResultHandler() {
|
||||
@Override
|
||||
public void handleRows(RelResult rows) {
|
||||
assertNotNull(rows);
|
||||
for (int i = 0; i < expectedResults.size(); ++i) {
|
||||
TupleResult row = rows.rowAtIndex(i);
|
||||
assertNotNull(row);
|
||||
String name = row.asString(0);
|
||||
assertNotNull(name);
|
||||
String category = row.asString(1);
|
||||
assertNotNull(category);
|
||||
String expectedCategory = expectedResults.get(name).toString();
|
||||
assertNotNull(expectedCategory);
|
||||
assertEquals(expectedCategory, category);
|
||||
}
|
||||
expectation.fulfill();
|
||||
}
|
||||
});
|
||||
synchronized (expectation) {
|
||||
expectation.wait(1000);
|
||||
}
|
||||
assertTrue(expectation.isFulfilled);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bindingLongValueSucceeds() throws InterruptedException {
|
||||
Mentat mentat = new Mentat();
|
||||
TxReport report = this.populateWithTypesSchema(mentat);
|
||||
final Long aEntid = report.getEntidForTempId("a");
|
||||
String query = "[:find ?e . :in ?long :where [?e :foo/long ?long]]";
|
||||
final Expectation expectation = new Expectation();
|
||||
mentat.query(query).bindLong("?long", 25).runScalar(new ScalarResultHandler() {
|
||||
@Override
|
||||
public void handleValue(TypedValue value) {
|
||||
assertNotNull(value);
|
||||
assertEquals(aEntid, value.asEntid());
|
||||
expectation.fulfill();
|
||||
}
|
||||
});
|
||||
synchronized (expectation) {
|
||||
expectation.wait(1000);
|
||||
}
|
||||
assertTrue(expectation.isFulfilled);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bindingRefValueSucceeds() throws InterruptedException {
|
||||
Mentat mentat = new Mentat();
|
||||
TxReport report = this.populateWithTypesSchema(mentat);
|
||||
long stringEntid = mentat.entIdForAttribute(":foo/string");
|
||||
final Long bEntid = report.getEntidForTempId("b");
|
||||
String query = "[:find ?e . :in ?ref :where [?e :foo/ref ?ref]]";
|
||||
final Expectation expectation = new Expectation();
|
||||
mentat.query(query).bindEntidReference("?ref", stringEntid).runScalar(new ScalarResultHandler() {
|
||||
@Override
|
||||
public void handleValue(TypedValue value) {
|
||||
assertNotNull(value);
|
||||
assertEquals(bEntid, value.asEntid());
|
||||
expectation.fulfill();
|
||||
}
|
||||
});
|
||||
synchronized (expectation) {
|
||||
expectation.wait(1000);
|
||||
}
|
||||
assertTrue(expectation.isFulfilled);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bindingRefKwValueSucceeds() throws InterruptedException {
|
||||
Mentat mentat = new Mentat();
|
||||
TxReport report = this.populateWithTypesSchema(mentat);
|
||||
String refKeyword = ":foo/string";
|
||||
final Long bEntid = report.getEntidForTempId("b");
|
||||
String query = "[:find ?e . :in ?ref :where [?e :foo/ref ?ref]]";
|
||||
final Expectation expectation = new Expectation();
|
||||
mentat.query(query).bindKeywordReference("?ref", refKeyword).runScalar(new ScalarResultHandler() {
|
||||
@Override
|
||||
public void handleValue(TypedValue value) {
|
||||
assertNotNull(value);
|
||||
assertEquals(bEntid, value.asEntid());
|
||||
expectation.fulfill();
|
||||
}
|
||||
});
|
||||
synchronized (expectation) {
|
||||
expectation.wait(1000);
|
||||
}
|
||||
assertTrue(expectation.isFulfilled);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bindingKwValueSucceeds() throws InterruptedException {
|
||||
Mentat mentat = new Mentat();
|
||||
TxReport report = this.populateWithTypesSchema(mentat);
|
||||
final Long aEntid = report.getEntidForTempId("a");
|
||||
String query = "[:find ?e . :in ?kw :where [?e :foo/keyword ?kw]]";
|
||||
final Expectation expectation = new Expectation();
|
||||
mentat.query(query).bindKeyword("?kw", ":foo/string").runScalar(new ScalarResultHandler() {
|
||||
@Override
|
||||
public void handleValue(TypedValue value) {
|
||||
assertNotNull(value);
|
||||
assertEquals(aEntid, value.asEntid());
|
||||
expectation.fulfill();
|
||||
}
|
||||
});
|
||||
synchronized (expectation) {
|
||||
expectation.wait(1000);
|
||||
}
|
||||
assertTrue(expectation.isFulfilled);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bindingDateValueSucceeds() throws InterruptedException, ParseException {
|
||||
Mentat mentat = new Mentat();
|
||||
TxReport report = this.populateWithTypesSchema(mentat);
|
||||
final Long aEntid = report.getEntidForTempId("a");
|
||||
|
||||
Date date = new Date(1523896758000L);
|
||||
String query = "[:find [?e ?d] :in ?now :where [?e :foo/instant ?d] [(< ?d ?now)]]";
|
||||
final Expectation expectation = new Expectation();
|
||||
mentat.query(query).bindDate("?now", date).runTuple(new TupleResultHandler() {
|
||||
@Override
|
||||
public void handleRow(TupleResult row) {
|
||||
assertNotNull(row);
|
||||
TypedValue value = row.get(0);
|
||||
assertNotNull(value);
|
||||
assertEquals(aEntid, value.asEntid());
|
||||
expectation.fulfill();
|
||||
}
|
||||
});
|
||||
synchronized (expectation) {
|
||||
expectation.wait(1000);
|
||||
}
|
||||
assertTrue(expectation.isFulfilled);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bindingStringValueSucceeds() throws InterruptedException {
|
||||
Mentat mentat = this.openAndInitializeCitiesStore();
|
||||
String query = "[:find ?n . :in ?name :where [(fulltext $ :community/name ?name) [[?e ?n]]]]";
|
||||
final Expectation expectation = new Expectation();
|
||||
mentat.query(query).bindString("?name", "Wallingford").runScalar(new ScalarResultHandler() {
|
||||
@Override
|
||||
public void handleValue(TypedValue value) {
|
||||
assertNotNull(value);
|
||||
assertEquals("KOMO Communities - Wallingford", value.asString());
|
||||
expectation.fulfill();
|
||||
}
|
||||
});
|
||||
synchronized (expectation) {
|
||||
expectation.wait(1000);
|
||||
}
|
||||
assertTrue(expectation.isFulfilled);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bindingUuidValueSucceeds() throws InterruptedException {
|
||||
Mentat mentat = new Mentat();
|
||||
TxReport report = this.populateWithTypesSchema(mentat);
|
||||
final Long aEntid = report.getEntidForTempId("a");
|
||||
String query = "[:find ?e . :in ?uuid :where [?e :foo/uuid ?uuid]]";
|
||||
UUID uuid = UUID.fromString("550e8400-e29b-41d4-a716-446655440000");
|
||||
final Expectation expectation = new Expectation();
|
||||
mentat.query(query).bindUUID("?uuid", uuid).runScalar(new ScalarResultHandler() {
|
||||
@Override
|
||||
public void handleValue(TypedValue value) {
|
||||
assertNotNull(value);
|
||||
assertEquals(aEntid, value.asEntid());
|
||||
expectation.fulfill();
|
||||
}
|
||||
});
|
||||
synchronized (expectation) {
|
||||
expectation.wait(1000);
|
||||
}
|
||||
assertTrue(expectation.isFulfilled);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bindingBooleanValueSucceeds() throws InterruptedException {
|
||||
Mentat mentat = new Mentat();
|
||||
TxReport report = this.populateWithTypesSchema(mentat);
|
||||
final Long aEntid = report.getEntidForTempId("a");
|
||||
String query = "[:find ?e . :in ?bool :where [?e :foo/boolean ?bool]]";
|
||||
final Expectation expectation = new Expectation();
|
||||
mentat.query(query).bindBoolean("?bool", true).runScalar(new ScalarResultHandler() {
|
||||
@Override
|
||||
public void handleValue(TypedValue value) {
|
||||
assertNotNull(value);
|
||||
assertEquals(aEntid, value.asEntid());
|
||||
expectation.fulfill();
|
||||
}
|
||||
});
|
||||
|
||||
synchronized (expectation) {
|
||||
expectation.wait(1000);
|
||||
}
|
||||
assertTrue(expectation.isFulfilled);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bindingDoubleValueSucceeds() throws InterruptedException {
|
||||
Mentat mentat = new Mentat();
|
||||
TxReport report = this.populateWithTypesSchema(mentat);
|
||||
final Long aEntid = report.getEntidForTempId("a");
|
||||
String query = "[:find ?e . :in ?double :where [?e :foo/double ?double]]";
|
||||
final Expectation expectation = new Expectation();
|
||||
mentat.query(query).bindDouble("?double", 11.23).runScalar(new ScalarResultHandler() {
|
||||
@Override
|
||||
public void handleValue(TypedValue value) {
|
||||
assertNotNull(value);
|
||||
assertEquals(aEntid, value.asEntid());
|
||||
expectation.fulfill();
|
||||
}
|
||||
});
|
||||
synchronized (expectation) {
|
||||
expectation.wait(1000);
|
||||
}
|
||||
assertTrue(expectation.isFulfilled);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void typedValueConvertsToLong() throws InterruptedException {
|
||||
Mentat mentat = new Mentat();
|
||||
TxReport report = this.populateWithTypesSchema(mentat);
|
||||
final Long aEntid = report.getEntidForTempId("a");
|
||||
String query = "[:find ?v . :in ?e :where [?e :foo/long ?v]]";
|
||||
final Expectation expectation = new Expectation();
|
||||
mentat.query(query).bindEntidReference("?e", aEntid).runScalar(new ScalarResultHandler() {
|
||||
@Override
|
||||
public void handleValue(TypedValue value) {
|
||||
assertNotNull(value);
|
||||
assertEquals(25, value.asLong().longValue());
|
||||
assertEquals(25, value.asLong().longValue());
|
||||
expectation.fulfill();
|
||||
}
|
||||
});
|
||||
synchronized (expectation) {
|
||||
expectation.wait(1000);
|
||||
}
|
||||
assertTrue(expectation.isFulfilled);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void typedValueConvertsToRef() throws InterruptedException {
|
||||
Mentat mentat = new Mentat();
|
||||
TxReport report = this.populateWithTypesSchema(mentat);
|
||||
final Long aEntid = report.getEntidForTempId("a");
|
||||
String query = "[:find ?e . :where [?e :foo/long 25]]";
|
||||
final Expectation expectation = new Expectation();
|
||||
mentat.query(query).runScalar(new ScalarResultHandler() {
|
||||
@Override
|
||||
public void handleValue(TypedValue value) {
|
||||
assertNotNull(value);
|
||||
assertEquals(aEntid, value.asEntid());
|
||||
assertEquals(aEntid, value.asEntid());
|
||||
expectation.fulfill();
|
||||
}
|
||||
});
|
||||
synchronized (expectation) {
|
||||
expectation.wait(1000);
|
||||
}
|
||||
assertTrue(expectation.isFulfilled);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void typedValueConvertsToKeyword() throws InterruptedException {
|
||||
Mentat mentat = new Mentat();
|
||||
TxReport report = this.populateWithTypesSchema(mentat);
|
||||
final Long aEntid = report.getEntidForTempId("a");
|
||||
String query = "[:find ?v . :in ?e :where [?e :foo/keyword ?v]]";
|
||||
final Expectation expectation = new Expectation();
|
||||
mentat.query(query).bindEntidReference("?e", aEntid).runScalar(new ScalarResultHandler() {
|
||||
@Override
|
||||
public void handleValue(TypedValue value) {
|
||||
assertNotNull(value);
|
||||
assertEquals(":foo/string", value.asKeyword());
|
||||
assertEquals(":foo/string", value.asKeyword());
|
||||
expectation.fulfill();
|
||||
}
|
||||
});
|
||||
synchronized (expectation) {
|
||||
expectation.wait(1000);
|
||||
}
|
||||
assertTrue(expectation.isFulfilled);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void typedValueConvertsToBoolean() throws InterruptedException {
|
||||
Mentat mentat = new Mentat();
|
||||
TxReport report = this.populateWithTypesSchema(mentat);
|
||||
final Long aEntid = report.getEntidForTempId("a");
|
||||
String query = "[:find ?v . :in ?e :where [?e :foo/boolean ?v]]";
|
||||
final Expectation expectation = new Expectation();
|
||||
mentat.query(query).bindEntidReference("?e", aEntid).runScalar(new ScalarResultHandler() {
|
||||
@Override
|
||||
public void handleValue(TypedValue value) {
|
||||
assertNotNull(value);
|
||||
assertEquals(true, value.asBoolean());
|
||||
assertEquals(true, value.asBoolean());
|
||||
expectation.fulfill();
|
||||
}
|
||||
});
|
||||
synchronized (expectation) {
|
||||
expectation.wait(1000);
|
||||
}
|
||||
assertTrue(expectation.isFulfilled);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void typedValueConvertsToDouble() throws InterruptedException {
|
||||
Mentat mentat = new Mentat();
|
||||
TxReport report = this.populateWithTypesSchema(mentat);
|
||||
final Long aEntid = report.getEntidForTempId("a");
|
||||
String query = "[:find ?v . :in ?e :where [?e :foo/double ?v]]";
|
||||
final Expectation expectation = new Expectation();
|
||||
mentat.query(query).bindEntidReference("?e", aEntid).runScalar(new ScalarResultHandler() {
|
||||
@Override
|
||||
public void handleValue(TypedValue value) {
|
||||
assertNotNull(value);
|
||||
assertEquals(new Double(11.23), value.asDouble());
|
||||
assertEquals(new Double(11.23), value.asDouble());
|
||||
expectation.fulfill();
|
||||
}
|
||||
});
|
||||
synchronized (expectation) {
|
||||
expectation.wait(1000);
|
||||
}
|
||||
assertTrue(expectation.isFulfilled);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void typedValueConvertsToDate() throws InterruptedException, ParseException {
|
||||
Mentat mentat = new Mentat();
|
||||
TxReport report = this.populateWithTypesSchema(mentat);
|
||||
final Long aEntid = report.getEntidForTempId("a");
|
||||
String query = "[:find ?v . :in ?e :where [?e :foo/instant ?v]]";
|
||||
final Expectation expectation = new Expectation();
|
||||
DateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZZZZZ", Locale.ENGLISH);
|
||||
format.parse("2017-01-01T11:00:00+00:00");
|
||||
final Calendar expectedDate = format.getCalendar();
|
||||
mentat.query(query).bindEntidReference("?e", aEntid).runScalar(new ScalarResultHandler() {
|
||||
@Override
|
||||
public void handleValue(TypedValue value) {
|
||||
assertNotNull(value);
|
||||
assertEquals(expectedDate.getTime(), value.asDate());
|
||||
assertEquals(expectedDate.getTime(), value.asDate());
|
||||
expectation.fulfill();
|
||||
}
|
||||
});
|
||||
synchronized (expectation) {
|
||||
expectation.wait(1000);
|
||||
}
|
||||
assertTrue(expectation.isFulfilled);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void typedValueConvertsToString() throws InterruptedException {
|
||||
Mentat mentat = new Mentat();
|
||||
TxReport report = this.populateWithTypesSchema(mentat);
|
||||
final Long aEntid = report.getEntidForTempId("a");
|
||||
String query = "[:find ?v . :in ?e :where [?e :foo/string ?v]]";
|
||||
final Expectation expectation = new Expectation();
|
||||
mentat.query(query).bindEntidReference("?e", aEntid).runScalar(new ScalarResultHandler() {
|
||||
@Override
|
||||
public void handleValue(TypedValue value) {
|
||||
assertNotNull(value);
|
||||
assertEquals("The higher we soar the smaller we appear to those who cannot fly.", value.asString());
|
||||
assertEquals("The higher we soar the smaller we appear to those who cannot fly.", value.asString());
|
||||
expectation.fulfill();
|
||||
}
|
||||
});
|
||||
synchronized (expectation) {
|
||||
expectation.wait(1000);
|
||||
}
|
||||
assertTrue(expectation.isFulfilled);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void typedValueConvertsToUUID() throws InterruptedException {
|
||||
Mentat mentat = new Mentat();
|
||||
TxReport report = this.populateWithTypesSchema(mentat);
|
||||
final Long aEntid = report.getEntidForTempId("a");
|
||||
String query = "[:find ?v . :in ?e :where [?e :foo/uuid ?v]]";
|
||||
final UUID expectedUUID = UUID.fromString("550e8400-e29b-41d4-a716-446655440000");
|
||||
final Expectation expectation = new Expectation();
|
||||
mentat.query(query).bindEntidReference("?e", aEntid).runScalar(new ScalarResultHandler() {
|
||||
@Override
|
||||
public void handleValue(TypedValue value) {
|
||||
assertNotNull(value);
|
||||
assertEquals(expectedUUID, value.asUUID());
|
||||
assertEquals(expectedUUID, value.asUUID());
|
||||
expectation.fulfill();
|
||||
}
|
||||
});
|
||||
synchronized (expectation) {
|
||||
expectation.wait(1000);
|
||||
}
|
||||
assertTrue(expectation.isFulfilled);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void valueForAttributeOfEntitySucceeds() throws InterruptedException {
|
||||
Mentat mentat = new Mentat();
|
||||
TxReport report = this.populateWithTypesSchema(mentat);
|
||||
final Long aEntid = report.getEntidForTempId("a");
|
||||
TypedValue value = mentat.valueForAttributeOfEntity(":foo/long", aEntid);
|
||||
assertNotNull(value);
|
||||
assertEquals(25, value.asLong().longValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void entidForAttributeSucceeds() {
|
||||
Mentat mentat = new Mentat();
|
||||
this.populateWithTypesSchema(mentat);
|
||||
long entid = mentat.entIdForAttribute(":foo/long");
|
||||
assertEquals(65540, entid);
|
||||
}
|
||||
}
|
6
sdks/android/Mentat/library/src/main/AndroidManifest.xml
Normal file
6
sdks/android/Mentat/library/src/main/AndroidManifest.xml
Normal file
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.mozilla.mentat">
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
</manifest>
|
|
@ -0,0 +1,48 @@
|
|||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||
* 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. */
|
||||
|
||||
package com.mozilla.mentat;
|
||||
|
||||
import com.sun.jna.Structure;
|
||||
import com.sun.jna.ptr.IntByReference;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Represents a C struct of a list of Strings containing attributes in the format
|
||||
* `:namespace/name`.
|
||||
*/
|
||||
public class AttributeList extends Structure implements Closeable {
|
||||
public static class ByReference extends AttributeList implements Structure.ByReference {
|
||||
}
|
||||
|
||||
public static class ByValue extends AttributeList implements Structure.ByValue {
|
||||
}
|
||||
|
||||
public IntByReference attributes;
|
||||
public int numberOfItems;
|
||||
// Used by the Swift counterpart, JNA does this for us automagically.
|
||||
// But we still need it here so that the number of fields and their order is correct
|
||||
public int len;
|
||||
|
||||
@Override
|
||||
protected List<String> getFieldOrder() {
|
||||
return Arrays.asList("attributes", "numberOfItems", "len");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
if (this.getPointer() != null) {
|
||||
JNA.INSTANCE.destroy(this.getPointer());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||
* 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. */
|
||||
|
||||
package com.mozilla.mentat;
|
||||
|
||||
import com.sun.jna.Pointer;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* Iterator for a {@link CollResult}
|
||||
*/
|
||||
public class ColResultIterator extends RustObject implements Iterator {
|
||||
|
||||
Pointer nextPointer;
|
||||
|
||||
ColResultIterator(Pointer iterator) {
|
||||
this.rawPointer = iterator;
|
||||
}
|
||||
|
||||
private Pointer getNextPointer() {
|
||||
return JNA.INSTANCE.typed_value_list_iter_next(this.rawPointer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
this.nextPointer = getNextPointer();
|
||||
return this.nextPointer != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypedValue next() {
|
||||
Pointer next = this.nextPointer == null ? getNextPointer() : this.nextPointer;
|
||||
if (next == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new TypedValue(next);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
if (this.rawPointer != null) {
|
||||
JNA.INSTANCE.typed_value_list_iter_destroy(this.rawPointer);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||
* 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. */
|
||||
|
||||
package com.mozilla.mentat;
|
||||
|
||||
import com.sun.jna.Pointer;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Wraps a `Coll` result from a Mentat query.
|
||||
* A `Coll` result is a list of rows of single values of type {@link TypedValue}.
|
||||
* Values for individual rows can be fetched as {@link TypedValue} or converted into a requested type.
|
||||
* <p>
|
||||
* Row values can be fetched as one of the following types:
|
||||
* <ul>
|
||||
* <li>{@link TypedValue}</li>
|
||||
* <li>long</li>
|
||||
* <li>Entid (as long)</li>
|
||||
* <li>Keyword (as String)</li>
|
||||
* <li>boolean</li>
|
||||
* <li>double</li>
|
||||
* <li>{@link Date}</li>
|
||||
* <li>{@link String}</li>
|
||||
* <li>{@link UUID}</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* To iterate over the result set use standard iteration flows.
|
||||
*/
|
||||
public class CollResult extends TupleResult implements Iterable<TypedValue> {
|
||||
|
||||
public CollResult(Pointer pointer) {
|
||||
super(pointer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
if (this.rawPointer != null) {
|
||||
JNA.INSTANCE.destroy(this.rawPointer);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ColResultIterator iterator() {
|
||||
Pointer iterPointer = JNA.INSTANCE.typed_value_list_into_iter(this.rawPointer);
|
||||
this.rawPointer = null;
|
||||
if (iterPointer == null) {
|
||||
return null;
|
||||
}
|
||||
return new ColResultIterator(iterPointer);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||
* 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. */
|
||||
|
||||
package com.mozilla.mentat;
|
||||
|
||||
/**
|
||||
* Interface defining the structure of a callback from a query returning a {@link CollResult}.
|
||||
*/
|
||||
public interface CollResultHandler {
|
||||
void handleList(CollResult list);
|
||||
}
|
|
@ -0,0 +1,101 @@
|
|||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||
* 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. */
|
||||
|
||||
package com.mozilla.mentat;
|
||||
|
||||
import com.sun.jna.Library;
|
||||
import com.sun.jna.Native;
|
||||
import com.sun.jna.NativeLibrary;
|
||||
import com.sun.jna.Pointer;
|
||||
|
||||
/**
|
||||
* JNA interface for FFI to Mentat's Rust library
|
||||
* Each function definition here link directly to a function in Mentat's FFI crate.
|
||||
* Signatures must match for the linking to work correctly.
|
||||
*/
|
||||
public interface JNA extends Library {
|
||||
String JNA_LIBRARY_NAME = "mentat_ffi";
|
||||
NativeLibrary JNA_NATIVE_LIB = NativeLibrary.getInstance(JNA_LIBRARY_NAME);
|
||||
|
||||
JNA INSTANCE = (JNA) Native.loadLibrary(JNA_LIBRARY_NAME, JNA.class);
|
||||
|
||||
Pointer store_open(String dbPath);
|
||||
|
||||
void destroy(Pointer obj);
|
||||
void query_builder_destroy(Pointer obj);
|
||||
void store_destroy(Pointer obj);
|
||||
void typed_value_destroy(Pointer obj);
|
||||
void typed_value_list_destroy(Pointer obj);
|
||||
void typed_value_list_iter_destroy(Pointer obj);
|
||||
void typed_value_result_set_destroy(Pointer obj);
|
||||
void typed_value_result_set_iter_destroy(Pointer obj);
|
||||
void tx_report_destroy(Pointer obj);
|
||||
|
||||
// transact
|
||||
RustResult store_transact(Pointer store, String transaction);
|
||||
Pointer tx_report_entity_for_temp_id(Pointer report, String tempid);
|
||||
long tx_report_get_entid(Pointer report);
|
||||
long tx_report_get_tx_instant(Pointer report);
|
||||
|
||||
// sync
|
||||
RustResult store_sync(Pointer store, String userUuid, String serverUri);
|
||||
|
||||
// observers
|
||||
void store_register_observer(Pointer store, String key, Pointer attributes, int len, TxObserverCallback callback);
|
||||
void store_unregister_observer(Pointer store, String key);
|
||||
long store_entid_for_attribute(Pointer store, String attr);
|
||||
|
||||
// Query Building
|
||||
Pointer store_query(Pointer store, String query);
|
||||
RustResult store_value_for_attribute(Pointer store, long entid, String attribute);
|
||||
void query_builder_bind_long(Pointer query, String var, long value);
|
||||
void query_builder_bind_ref(Pointer query, String var, long value);
|
||||
void query_builder_bind_ref_kw(Pointer query, String var, String value);
|
||||
void query_builder_bind_kw(Pointer query, String var, String value);
|
||||
void query_builder_bind_boolean(Pointer query, String var, int value);
|
||||
void query_builder_bind_double(Pointer query, String var, double value);
|
||||
void query_builder_bind_timestamp(Pointer query, String var, long value);
|
||||
void query_builder_bind_string(Pointer query, String var, String value);
|
||||
void query_builder_bind_uuid(Pointer query, String var, Pointer value);
|
||||
|
||||
// Query Execution
|
||||
RustResult query_builder_execute(Pointer query);
|
||||
RustResult query_builder_execute_scalar(Pointer query);
|
||||
RustResult query_builder_execute_coll(Pointer query);
|
||||
RustResult query_builder_execute_tuple(Pointer query);
|
||||
|
||||
// Query Result Processing
|
||||
long typed_value_into_long(Pointer value);
|
||||
long typed_value_into_entid(Pointer value);
|
||||
String typed_value_into_kw(Pointer value);
|
||||
String typed_value_into_string(Pointer value);
|
||||
Pointer typed_value_into_uuid(Pointer value);
|
||||
int typed_value_into_boolean(Pointer value);
|
||||
double typed_value_into_double(Pointer value);
|
||||
long typed_value_into_timestamp(Pointer value);
|
||||
Pointer typed_value_value_type(Pointer value);
|
||||
|
||||
Pointer row_at_index(Pointer rows, int index);
|
||||
Pointer typed_value_result_set_into_iter(Pointer rows);
|
||||
Pointer typed_value_result_set_iter_next(Pointer iter);
|
||||
|
||||
Pointer typed_value_list_into_iter(Pointer rows);
|
||||
Pointer typed_value_list_iter_next(Pointer iter);
|
||||
|
||||
Pointer value_at_index(Pointer rows, int index);
|
||||
long value_at_index_into_long(Pointer rows, int index);
|
||||
long value_at_index_into_entid(Pointer rows, int index);
|
||||
String value_at_index_into_kw(Pointer rows, int index);
|
||||
String value_at_index_into_string(Pointer rows, int index);
|
||||
Pointer value_at_index_into_uuid(Pointer rows, int index);
|
||||
long value_at_index_into_boolean(Pointer rows, int index);
|
||||
double value_at_index_into_double(Pointer rows, int index);
|
||||
long value_at_index_into_timestamp(Pointer rows, int index);
|
||||
}
|
|
@ -0,0 +1,147 @@
|
|||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||
* 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. */
|
||||
|
||||
package com.mozilla.mentat;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import com.sun.jna.Memory;
|
||||
import com.sun.jna.Pointer;
|
||||
|
||||
/**
|
||||
* The primary class for accessing Mentat's API.<br/>
|
||||
* This class provides all of the basic API that can be found in Mentat's Store struct.<br/>
|
||||
* The raw pointer it holds is a pointer to a Store.
|
||||
*/
|
||||
public class Mentat extends RustObject {
|
||||
|
||||
static {
|
||||
System.loadLibrary("mentat_ffi");
|
||||
}
|
||||
|
||||
/**
|
||||
* Open a connection to a Store in a given location.<br/>
|
||||
* If the store does not already exist, one will be created.
|
||||
* @param dbPath The URI as a String of the store to open.
|
||||
*/
|
||||
public Mentat(String dbPath) {
|
||||
this.rawPointer = JNA.INSTANCE.store_open(dbPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Open a connection to an in-memory Store.
|
||||
*/
|
||||
public Mentat() {
|
||||
this.rawPointer = JNA.INSTANCE.store_open("");
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new Mentat with the provided pointer to a Mentat Store
|
||||
* @param rawPointer A pointer to a Mentat Store.
|
||||
*/
|
||||
public Mentat(Pointer rawPointer) { this.rawPointer = rawPointer; }
|
||||
|
||||
/**
|
||||
* Simple transact of an EDN string.
|
||||
* TODO: Throw an exception if the transact fails
|
||||
* @param transaction The string, as EDN, to be transacted.
|
||||
* @return The {@link TxReport} of the completed transaction
|
||||
*/
|
||||
public TxReport transact(String transaction) {
|
||||
RustResult result = JNA.INSTANCE.store_transact(this.rawPointer, transaction);
|
||||
if (result.isFailure()) {
|
||||
Log.e("Mentat", result.err);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (result.isSuccess()) {
|
||||
return new TxReport(result.ok);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the the `Entid` of the attribute
|
||||
* @param attribute The string represeting the attribute whose `Entid` we are after. The string is represented as `:namespace/name`.
|
||||
* @return The `Entid` associated with the attribute.
|
||||
*/
|
||||
public long entIdForAttribute(String attribute) {
|
||||
return JNA.INSTANCE.store_entid_for_attribute(this.rawPointer, attribute);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start a query.
|
||||
* @param query The string represeting the the query to be executed.
|
||||
* @return The {@link Query} representing the query that can be executed.
|
||||
*/
|
||||
public Query query(String query) {
|
||||
return new Query(JNA.INSTANCE.store_query(this.rawPointer, query));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a single value of an attribute for an Entity
|
||||
* TODO: Throw an exception if there is no the result contains an error.
|
||||
* @param attribute The string the attribute whose value is to be returned. The string is represented as `:namespace/name`.
|
||||
* @param entid The `Entid` of the entity we want the value from.
|
||||
* @return The {@link TypedValue} containing the value of the attribute for the entity.
|
||||
*/
|
||||
public TypedValue valueForAttributeOfEntity(String attribute, long entid) {
|
||||
RustResult result = JNA.INSTANCE.store_value_for_attribute(this.rawPointer, entid, attribute);
|
||||
|
||||
if (result.isSuccess()) {
|
||||
return new TypedValue(result.ok);
|
||||
}
|
||||
|
||||
if (result.isFailure()) {
|
||||
Log.e("Mentat", result.err);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register an callback and a set of attributes to observer for transaction observation.
|
||||
* The callback function is called when a transaction occurs in the `Store` that this `Mentat`
|
||||
* is connected to that affects the attributes that an observer has registered for.
|
||||
* @param key `String` representing an identifier for the observer.
|
||||
* @param attributes An array of Strings representing the attributes that the observer wishes
|
||||
* to be notified about if they are referenced in a transaction.
|
||||
* @param callback the function to call when an observer notice is fired.
|
||||
*/
|
||||
public void registerObserver(String key, String[] attributes, TxObserverCallback callback) {
|
||||
// turn string array into int array
|
||||
long[] attrEntids = new long[attributes.length];
|
||||
for(int i = 0; i < attributes.length; i++) {
|
||||
attrEntids[i] = JNA.INSTANCE.store_entid_for_attribute(this.rawPointer, attributes[i]);
|
||||
}
|
||||
final Pointer entidsNativeArray = new Memory(8 * attrEntids.length);
|
||||
entidsNativeArray.write(0, attrEntids, 0, attrEntids.length);
|
||||
JNA.INSTANCE.store_register_observer(rawPointer, key, entidsNativeArray, attrEntids.length, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister the observer that was registered with the provided key such that it will no longer be called
|
||||
* if a transaction occurs that affects the attributes that the observer was registered to observe.
|
||||
* <p/>
|
||||
* The observer will need to re-register if it wants to start observing again.
|
||||
* @param key String representing an identifier for the observer.
|
||||
*/
|
||||
public void unregisterObserver(String key) {
|
||||
JNA.INSTANCE.store_unregister_observer(rawPointer, key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
if (this.rawPointer != null) {
|
||||
JNA.INSTANCE.store_destroy(this.rawPointer);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,330 @@
|
|||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||
* 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. */
|
||||
|
||||
package com.mozilla.mentat;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import com.sun.jna.Memory;
|
||||
import com.sun.jna.Pointer;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Date;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* This class allows you to construct a query, bind values to variables and run those queries against a mentat DB.
|
||||
* <p/>
|
||||
* This class cannot be created directly, but must be created through `Mentat.query(String:)`.
|
||||
* <p/>
|
||||
* The types of values you can bind are:
|
||||
* <ul>
|
||||
* <li>{@link TypedValue}</li>
|
||||
* <li>long</li>
|
||||
* <li>Entid (as long)</li>
|
||||
* <li>Keyword (as String)</li>
|
||||
* <li>boolean</li>
|
||||
* <li>double</li>
|
||||
* <li>{@link Date}</li>
|
||||
* <li>{@link String}</li>
|
||||
* <li>{@link UUID}</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* <p/>
|
||||
* Each bound variable must have a corresponding value in the query string used to create this query.
|
||||
* <p/>
|
||||
* <pre>{@code
|
||||
* String query = "[:find ?name ?cat\n" +
|
||||
* " :in ?type\n" +
|
||||
* " :where\n" +
|
||||
* " [?c :community/name ?name]\n" +
|
||||
* " [?c :community/type ?type]\n" +
|
||||
* " [?c :community/category ?cat]]";
|
||||
* mentat.query(query).bindKeywordReference("?type", ":community.type/website").run(new RelResultHandler() {
|
||||
* @Override
|
||||
* public void handleRows(RelResult rows) {
|
||||
* ...
|
||||
* }
|
||||
* });
|
||||
*}</pre>
|
||||
* <p/>
|
||||
* 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.
|
||||
* <p/>
|
||||
* - `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:
|
||||
*
|
||||
* <pre>{@code
|
||||
* String query = "[: find ?a ?b ?c\n" +
|
||||
* " : where ... ]";
|
||||
* mentat.query(query).run(new RelResultHandler() {
|
||||
* @Override
|
||||
* public void handleRows(RelResult rows) {
|
||||
* ...
|
||||
* }
|
||||
* });
|
||||
*}</pre>
|
||||
* <p/>
|
||||
* - `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:
|
||||
*
|
||||
* <pre>{@code
|
||||
* String query = "[: find ?a .\n" +
|
||||
* " : where ... ]";
|
||||
* mentat.query(query).runScalar(new ScalarResultHandler() {
|
||||
* @Override
|
||||
* public void handleValue(TypedValue value) {
|
||||
* ...
|
||||
* }
|
||||
* });
|
||||
*}</pre>
|
||||
* <p/>
|
||||
* - `Coll` - This returns a list of single values as a result. Queries that wish to have `Coll` results should format their query strings:
|
||||
* <pre>{@code
|
||||
* String query = "[: find [?a ...]\n" +
|
||||
* " : where ... ]";
|
||||
* mentat.query(query).runColl(new ScalarResultHandler() {
|
||||
* @Override
|
||||
* public void handleList(CollResult list) {
|
||||
* ...
|
||||
* }
|
||||
* });
|
||||
*}</pre>
|
||||
* <p/>
|
||||
* - `Tuple` - This returns a single row of values. Queries that wish to have `Tuple` results should format their query strings:
|
||||
* <pre>{@code
|
||||
* String query = "[: find [?a ?b ?c]\n" +
|
||||
* " : where ... ]";
|
||||
* mentat.query(query).runTuple(new TupleResultHandler() {
|
||||
* @Override
|
||||
* public void handleRow(TupleResult row) {
|
||||
* ...
|
||||
* }
|
||||
* });
|
||||
*}</pre>
|
||||
*/
|
||||
public class Query extends RustObject {
|
||||
|
||||
public Query(Pointer pointer) {
|
||||
this.rawPointer = pointer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds a long value to the provided variable name.
|
||||
* TODO: Throw an exception if the query raw pointer has been consumed.
|
||||
* @param varName The name of the variable in the format `?name`.
|
||||
* @param value The value to be bound
|
||||
* @return This {@link Query} such that further function can be called.
|
||||
*/
|
||||
Query bindLong(String varName, long value) {
|
||||
this.validate();
|
||||
JNA.INSTANCE.query_builder_bind_long(this.rawPointer, varName, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds a Entid value to the provided variable name.
|
||||
* TODO: Throw an exception if the query raw pointer has been consumed.
|
||||
* @param varName The name of the variable in the format `?name`.
|
||||
* @param value The value to be bound
|
||||
* @return This {@link Query} such that further function can be called.
|
||||
*/
|
||||
Query bindEntidReference(String varName, long value) {
|
||||
this.validate();
|
||||
JNA.INSTANCE.query_builder_bind_ref(this.rawPointer, varName, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds a String keyword value to the provided variable name.
|
||||
* TODO: Throw an exception if the query raw pointer has been consumed.
|
||||
* @param varName The name of the variable in the format `?name`.
|
||||
* @param value The value to be bound
|
||||
* @return This {@link Query} such that further function can be called.
|
||||
*/
|
||||
Query bindKeywordReference(String varName, String value) {
|
||||
this.validate();
|
||||
JNA.INSTANCE.query_builder_bind_ref_kw(this.rawPointer, varName, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds a keyword value to the provided variable name.
|
||||
* TODO: Throw an exception if the query raw pointer has been consumed.
|
||||
* @param varName The name of the variable in the format `?name`.
|
||||
* @param value The value to be bound
|
||||
* @return This {@link Query} such that further function can be called.
|
||||
*/
|
||||
Query bindKeyword(String varName, String value) {
|
||||
this.validate();
|
||||
JNA.INSTANCE.query_builder_bind_kw(this.rawPointer, varName, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds a boolean value to the provided variable name.
|
||||
* TODO: Throw an exception if the query raw pointer has been consumed.
|
||||
* @param varName The name of the variable in the format `?name`.
|
||||
* @param value The value to be bound
|
||||
* @return This {@link Query} such that further function can be called.
|
||||
*/
|
||||
Query bindBoolean(String varName, boolean value) {
|
||||
this.validate();
|
||||
JNA.INSTANCE.query_builder_bind_boolean(this.rawPointer, varName, value ? 1 : 0);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds a double value to the provided variable name.
|
||||
* TODO: Throw an exception if the query raw pointer has been consumed.
|
||||
* @param varName The name of the variable in the format `?name`.
|
||||
* @param value The value to be bound
|
||||
* @return This {@link Query} such that further function can be called.
|
||||
*/
|
||||
Query bindDouble(String varName, double value) {
|
||||
this.validate();
|
||||
JNA.INSTANCE.query_builder_bind_double(this.rawPointer, varName, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds a {@link Date} value to the provided variable name.
|
||||
* TODO: Throw an exception if the query raw pointer has been consumed.
|
||||
* @param varName The name of the variable in the format `?name`.
|
||||
* @param value The value to be bound
|
||||
* @return This {@link Query} such that further function can be called.
|
||||
*/
|
||||
Query bindDate(String varName, Date value) {
|
||||
this.validate();
|
||||
long timestamp = value.getTime() * 1000;
|
||||
JNA.INSTANCE.query_builder_bind_timestamp(this.rawPointer, varName, timestamp);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds a {@link String} value to the provided variable name.
|
||||
* TODO: Throw an exception if the query raw pointer has been consumed.
|
||||
* @param varName The name of the variable in the format `?name`.
|
||||
* @param value The value to be bound
|
||||
* @return This {@link Query} such that further function can be called.
|
||||
*/
|
||||
Query bindString(String varName, String value) {
|
||||
this.validate();
|
||||
JNA.INSTANCE.query_builder_bind_string(this.rawPointer, varName, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds a {@link UUID} value to the provided variable name.
|
||||
* TODO: Throw an exception if the query raw pointer has been consumed.
|
||||
* @param varName The name of the variable in the format `?name`.
|
||||
* @param value The value to be bound
|
||||
* @return This {@link Query} such that further function can be called.
|
||||
*/
|
||||
Query bindUUID(String varName, UUID value) {
|
||||
this.validate();
|
||||
ByteBuffer bb = ByteBuffer.wrap(new byte[16]);
|
||||
bb.putLong(value.getMostSignificantBits());
|
||||
bb.putLong(value.getLeastSignificantBits());
|
||||
byte[] bytes = bb.array();
|
||||
final Pointer bytesNativeArray = new Memory(bytes.length);
|
||||
bytesNativeArray.write(0, bytes, 0, bytes.length);
|
||||
JNA.INSTANCE.query_builder_bind_uuid(this.rawPointer, varName, bytesNativeArray);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the query with the values bound associated with this {@link Query} and call the provided
|
||||
* callback function with the results as a list of rows of {@link TypedValue}s.
|
||||
* TODO: Throw an exception if the query raw pointer has been consumed or the query fails to execute
|
||||
* @param handler the handler to call with the results of this query
|
||||
*/
|
||||
void run(final RelResultHandler handler) {
|
||||
this.validate();
|
||||
RustResult result = JNA.INSTANCE.query_builder_execute(rawPointer);
|
||||
rawPointer = null;
|
||||
|
||||
if (result.isFailure()) {
|
||||
Log.e("Query", result.err);
|
||||
return;
|
||||
}
|
||||
handler.handleRows(new RelResult(result.ok));
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the query with the values bound associated with this {@link Query} and call the provided
|
||||
* callback function with the results with the result as a single {@link TypedValue}.
|
||||
* TODO: Throw an exception if the query raw pointer has been consumed or the query fails to execute
|
||||
* @param handler the handler to call with the results of this query
|
||||
*/
|
||||
void runScalar(final ScalarResultHandler handler) {
|
||||
this.validate();
|
||||
RustResult result = JNA.INSTANCE.query_builder_execute_scalar(rawPointer);
|
||||
rawPointer = null;
|
||||
|
||||
if (result.isFailure()) {
|
||||
Log.e("Query", result.err);
|
||||
return;
|
||||
}
|
||||
|
||||
if (result.isSuccess()) {
|
||||
handler.handleValue(new TypedValue(result.ok));
|
||||
} else {
|
||||
handler.handleValue(null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the query with the values bound associated with this {@link Query} and call the provided
|
||||
* callback function with the results with the result as a list of single {@link TypedValue}s.
|
||||
* TODO: Throw an exception if the query raw pointer has been consumed or the query fails to execute
|
||||
* @param handler the handler to call with the results of this query
|
||||
*/
|
||||
void runColl(final CollResultHandler handler) {
|
||||
this.validate();
|
||||
RustResult result = JNA.INSTANCE.query_builder_execute_coll(rawPointer);
|
||||
rawPointer = null;
|
||||
|
||||
if (result.isFailure()) {
|
||||
Log.e("Query", result.err);
|
||||
return;
|
||||
}
|
||||
handler.handleList(new CollResult(result.ok));
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the query with the values bound associated with this {@link Query} and call the provided
|
||||
* callback function with the results with the result as a list of single {@link TypedValue}s.
|
||||
* TODO: Throw an exception if the query raw pointer has been consumed or the query fails to execute
|
||||
* @param handler the handler to call with the results of this query
|
||||
*/
|
||||
void runTuple(final TupleResultHandler handler) {
|
||||
this.validate();
|
||||
RustResult result = JNA.INSTANCE.query_builder_execute_tuple(rawPointer);
|
||||
rawPointer = null;
|
||||
|
||||
if (result.isFailure()) {
|
||||
Log.e("Query", result.err);
|
||||
return;
|
||||
}
|
||||
|
||||
if (result.isSuccess()) {
|
||||
handler.handleRow(new TupleResult(result.ok));
|
||||
} else {
|
||||
handler.handleRow(null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
if (this.rawPointer == null) {
|
||||
return;
|
||||
}
|
||||
JNA.INSTANCE.query_builder_destroy(this.rawPointer);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||
* 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. */
|
||||
|
||||
package com.mozilla.mentat;
|
||||
|
||||
import com.sun.jna.Pointer;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* </p>
|
||||
* To fetch individual rows from a `RelResult` use `row(Int32)`.
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
* mentat.query(query).run(new RelResultHandler() {
|
||||
* @Override
|
||||
* public void handleRows(RelResult rows) {
|
||||
* TupleResult row1 = rows.rowAtIndex(0);
|
||||
* TupleResult row2 = rows.rowAtIndex(1);
|
||||
* ...
|
||||
* }
|
||||
* });
|
||||
*}</pre>
|
||||
* </p>
|
||||
* To iterate over the result set use standard iteration flows.
|
||||
* <pre>{@code
|
||||
* mentat.query(query).run(new RelResultHandler() {
|
||||
* @Override
|
||||
* public void handleRows(RelResult rows) {
|
||||
* for (TupleResult row: rows) {
|
||||
* ...
|
||||
* }
|
||||
* }
|
||||
* });
|
||||
*}</pre>
|
||||
* </p>
|
||||
* Note that iteration is consuming and can only be done once.
|
||||
*/
|
||||
public class RelResult extends RustObject implements Iterable<TupleResult> {
|
||||
|
||||
public RelResult(Pointer pointer) {
|
||||
this.rawPointer = pointer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the row at the requested index.
|
||||
* TODO: Throw an exception if the result set has already been iterated.
|
||||
* @param index the index of the row to be fetched
|
||||
* @return The row at the requested index as a `TupleResult`, if present, or nil if there is no row at that index.
|
||||
*/
|
||||
public TupleResult rowAtIndex(int index) {
|
||||
this.validate();
|
||||
Pointer pointer = JNA.INSTANCE.row_at_index(this.rawPointer, index);
|
||||
if (pointer == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new TupleResult(pointer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RelResultIterator iterator() {
|
||||
this.validate();
|
||||
Pointer iterPointer = JNA.INSTANCE.typed_value_result_set_into_iter(this.rawPointer);
|
||||
this.rawPointer = null;
|
||||
if (iterPointer == null) {
|
||||
return null;
|
||||
}
|
||||
return new RelResultIterator(iterPointer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
if (this.rawPointer != null) {
|
||||
JNA.INSTANCE.typed_value_result_set_destroy(this.rawPointer);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||
* 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. */
|
||||
|
||||
package com.mozilla.mentat;
|
||||
|
||||
/**
|
||||
* Interface defining the structure of a callback from a query returning a {@link RelResult}.
|
||||
*/
|
||||
public interface RelResultHandler {
|
||||
void handleRows(RelResult rows);
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||
* 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. */
|
||||
|
||||
package com.mozilla.mentat;
|
||||
|
||||
import com.sun.jna.Pointer;
|
||||
|
||||
import java.util.Iterator;
|
||||
/**
|
||||
* Iterator for a {@link RelResult}
|
||||
*/
|
||||
public class RelResultIterator extends RustObject implements Iterator {
|
||||
|
||||
Pointer nextPointer;
|
||||
|
||||
RelResultIterator(Pointer iterator) {
|
||||
this.rawPointer = iterator;
|
||||
}
|
||||
|
||||
private Pointer getNextPointer() {
|
||||
return JNA.INSTANCE.typed_value_result_set_iter_next(this.rawPointer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
this.nextPointer = getNextPointer();
|
||||
return this.nextPointer != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TupleResult next() {
|
||||
Pointer next = this.nextPointer == null ? getNextPointer() : this.nextPointer;
|
||||
if (next == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new TupleResult(next);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
if (this.rawPointer != null) {
|
||||
JNA.INSTANCE.typed_value_result_set_iter_destroy(this.rawPointer);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||
* 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. */
|
||||
|
||||
package com.mozilla.mentat;
|
||||
|
||||
import com.sun.jna.Pointer;
|
||||
|
||||
import java.io.Closeable;
|
||||
|
||||
/**
|
||||
* Base class that wraps an non-optional {@link Pointer} representing a pointer to a Rust object.
|
||||
* This class implements {@link Closeable} but does not provide an implementation, forcing all
|
||||
* subclasses to implement it. This ensures that all classes that inherit from RustObject
|
||||
* will have their {@link Pointer} destroyed when the Java wrapper is destroyed.
|
||||
*/
|
||||
abstract class RustObject implements Closeable {
|
||||
Pointer rawPointer;
|
||||
|
||||
/**
|
||||
* Throws a {@link NullPointerException} if the underlying {@link Pointer} is null.
|
||||
*/
|
||||
void validate() {
|
||||
if (this.rawPointer == null) {
|
||||
throw new NullPointerException(this.getClass() + " consumed");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||
* 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. */
|
||||
|
||||
package com.mozilla.mentat;
|
||||
|
||||
import com.sun.jna.Pointer;
|
||||
import com.sun.jna.Structure;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Represents a C struct containing a {@link Pointer}s and String that map to a Rust Result.
|
||||
* A RustResult will contain either an ok value, OR an err value, or neither - never both.
|
||||
*/
|
||||
public class RustResult extends Structure implements Closeable {
|
||||
public static class ByReference extends RustResult implements Structure.ByReference {
|
||||
}
|
||||
|
||||
public static class ByValue extends RustResult implements Structure.ByValue {
|
||||
}
|
||||
|
||||
public Pointer ok;
|
||||
public String err;
|
||||
|
||||
/**
|
||||
* Is there an value attached to this result
|
||||
* @return true if a value is present, false otherwise
|
||||
*/
|
||||
public boolean isSuccess() {
|
||||
return this.ok != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is there an error attached to this result?
|
||||
* @return true is an error is present, false otherwise
|
||||
*/
|
||||
public boolean isFailure() {
|
||||
return this.err != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getFieldOrder() {
|
||||
return Arrays.asList("ok", "err");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
if (this.getPointer() != null) {
|
||||
JNA.INSTANCE.destroy(this.getPointer());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||
* 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. */
|
||||
|
||||
package com.mozilla.mentat;
|
||||
|
||||
/**
|
||||
* Interface defining the structure of a callback from a query returning a single {@link TypedValue}.
|
||||
*/
|
||||
public interface ScalarResultHandler {
|
||||
void handleValue(TypedValue value);
|
||||
}
|
|
@ -0,0 +1,168 @@
|
|||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||
* 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. */
|
||||
|
||||
package com.mozilla.mentat;
|
||||
|
||||
import com.sun.jna.Pointer;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Date;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Wraps a `Tuple` result from a Mentat query.
|
||||
* A `Tuple` result is a single row {@link TypedValue}s.
|
||||
* Values for individual fields can be fetched as {@link TypedValue} or converted into a requested type.
|
||||
* <p>
|
||||
* Field values can be fetched as one of the following types:
|
||||
* <ul>
|
||||
* <li>{@link TypedValue}</li>
|
||||
* <li>long</li>
|
||||
* <li>Entid (as long)</li>
|
||||
* <li>Keyword (as String)</li>
|
||||
* <li>boolean</li>
|
||||
* <li>double</li>
|
||||
* <li>{@link Date}</li>
|
||||
* <li>{@link String}</li>
|
||||
* <li>{@link UUID}</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* To iterate over the result set use standard iteration flows.
|
||||
*/
|
||||
public class TupleResult extends RustObject {
|
||||
|
||||
public TupleResult(Pointer pointer) {
|
||||
this.rawPointer = pointer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link TypedValue} at the specified index.
|
||||
* If the index is greater than the number of values then this function will crash.
|
||||
* @param index The index of the value to fetch.
|
||||
* @return The {@link TypedValue} at that index.
|
||||
*/
|
||||
public TypedValue get(Integer index) {
|
||||
this.validate();
|
||||
Pointer pointer = JNA.INSTANCE.value_at_index(this.rawPointer, index);
|
||||
if (pointer == null) {
|
||||
return null;
|
||||
}
|
||||
return new TypedValue(pointer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link Long} 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 {@link TypedValue} at this index is not `Long` then this function will crash.
|
||||
* @param index The index of the value to fetch.
|
||||
* @return The {@link Long} at that index.
|
||||
*/
|
||||
public Long asLong(Integer index) {
|
||||
this.validate();
|
||||
return JNA.INSTANCE.value_at_index_into_long(this.rawPointer, 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 {@link TypedValue} at this index is not `Ref` then this function will crash.
|
||||
* @param index The index of the value to fetch.
|
||||
* @return The Entid at that index.
|
||||
*/
|
||||
public Long asEntid(Integer index) {
|
||||
this.validate();
|
||||
return JNA.INSTANCE.value_at_index_into_entid(this.rawPointer, index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the keyword {@link 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 {@link TypedValue} at this index is not `Keyword` then this function will crash.
|
||||
* @param index The index of the value to fetch.
|
||||
* @return The keyword at that index.
|
||||
*/
|
||||
public String asKeyword(Integer index) {
|
||||
this.validate();
|
||||
return JNA.INSTANCE.value_at_index_into_kw(this.rawPointer, index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link Boolean} 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 {@link TypedValue} at this index is not `Boolean` then this function will crash.
|
||||
* @param index The index of the value to fetch.
|
||||
* @return The {@link Boolean} at that index.
|
||||
*/
|
||||
public Boolean asBool(Integer index) {
|
||||
this.validate();
|
||||
return JNA.INSTANCE.value_at_index_into_boolean(this.rawPointer, index) == 0 ? false : true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link 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 {@link TypedValue} at this index is not `Double` then this function will crash.
|
||||
* @param index The index of the value to fetch.
|
||||
* @return The {@link Double} at that index.
|
||||
*/
|
||||
public Double asDouble(Integer index) {
|
||||
this.validate();
|
||||
return JNA.INSTANCE.value_at_index_into_double(this.rawPointer, index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link 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 {@link TypedValue} at this index is not `Instant` then this function will crash.
|
||||
* @param index The index of the value to fetch.
|
||||
* @return The {@link Date} at that index.
|
||||
*/
|
||||
public Date asDate(Integer index) {
|
||||
this.validate();
|
||||
return new Date(JNA.INSTANCE.value_at_index_into_timestamp(this.rawPointer, index));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link 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 {@link TypedValue} at this index is not `String` then this function will crash.
|
||||
* @param index The index of the value to fetch.
|
||||
* @return The {@link String} at that index.
|
||||
*/
|
||||
public String asString(Integer index) {
|
||||
this.validate();
|
||||
return JNA.INSTANCE.value_at_index_into_string(this.rawPointer, index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link 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 {@link TypedValue} at this index is not `Uuid` then this function will crash.
|
||||
* @param index The index of the value to fetch.
|
||||
* @return The {@link UUID} at that index.
|
||||
*/
|
||||
public UUID asUUID(Integer index) {
|
||||
this.validate();
|
||||
Pointer uuidPtr = JNA.INSTANCE.value_at_index_into_uuid(this.rawPointer, index);
|
||||
byte[] bytes = uuidPtr.getByteArray(0, 16);
|
||||
ByteBuffer bb = ByteBuffer.wrap(bytes);
|
||||
long high = bb.getLong();
|
||||
long low = bb.getLong();
|
||||
|
||||
return new UUID(high, low);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
if (this.rawPointer != null) {
|
||||
JNA.INSTANCE.typed_value_list_destroy(this.rawPointer);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||
* 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. */
|
||||
|
||||
package com.mozilla.mentat;
|
||||
|
||||
/**
|
||||
* Interface defining the structure of a callback from a query returning a {@link TupleResult}.
|
||||
*/
|
||||
public interface TupleResultHandler {
|
||||
void handleRow(TupleResult row);
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||
* 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. */
|
||||
|
||||
package com.mozilla.mentat;
|
||||
|
||||
import com.sun.jna.Pointer;
|
||||
import com.sun.jna.Structure;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Represents a C struct representing changes that occured during a transaction.
|
||||
* These changes contain the transaction identifier, a {@link Pointer} to a list of affected attribute
|
||||
* Entids and the number of items that the list contains.
|
||||
*/
|
||||
public class TxChange extends Structure implements Closeable {
|
||||
public static class ByReference extends TxChange implements Structure.ByReference {
|
||||
}
|
||||
|
||||
public static class ByValue extends TxChange implements Structure.ByValue {
|
||||
}
|
||||
|
||||
public int txid;
|
||||
public Pointer changes;
|
||||
public int numberOfItems;
|
||||
// Used by the Swift counterpart, JNA does this for us automagically.
|
||||
// But we still need it here so that the number of fields and their order is correct
|
||||
public int changes_len;
|
||||
|
||||
/**
|
||||
* Get the affected attributes for this transaction
|
||||
* @return The changes as a list of Entids of affected attributes
|
||||
*/
|
||||
public List<Long> getChanges() {
|
||||
final long[] array = (long[]) changes.getLongArray(0, numberOfItems);
|
||||
Long[] longArray = new Long[numberOfItems];
|
||||
int idx = 0;
|
||||
for (long change: array) {
|
||||
longArray[idx++] = change;
|
||||
}
|
||||
return Arrays.asList(longArray);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getFieldOrder() {
|
||||
return Arrays.asList("txid", "changes", "changes_len", "numberOfItems");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
if (this.getPointer() != null) {
|
||||
JNA.INSTANCE.destroy(this.getPointer());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||
* 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. */
|
||||
|
||||
package com.mozilla.mentat;
|
||||
|
||||
import com.sun.jna.Structure;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Represents a C struct containing a list of {@link TxChange}s that occured.
|
||||
*/
|
||||
public class TxChangeList extends Structure implements Closeable {
|
||||
public static class ByReference extends TxChangeList implements Structure.ByReference {
|
||||
}
|
||||
|
||||
public static class ByValue extends TxChangeList implements Structure.ByValue {
|
||||
}
|
||||
|
||||
public TxChange.ByReference reports;
|
||||
public int numberOfItems;
|
||||
// Used by the Swift counterpart, JNA does this for us automagically.
|
||||
// // But we still need it here so that the number of fields and their order is correct
|
||||
public int len;
|
||||
|
||||
/**
|
||||
* Get the changes that occured
|
||||
* @return a list of {@link TxChange}s for the notification
|
||||
*/
|
||||
public List<TxChange> getReports() {
|
||||
final TxChange[] array = (TxChange[]) reports.toArray(numberOfItems);
|
||||
return Arrays.asList(array);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getFieldOrder() {
|
||||
return Arrays.asList("reports", "numberOfItems", "len");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
final TxChange[] nativeReports = (TxChange[]) reports.toArray(numberOfItems);
|
||||
for (TxChange nativeReport : nativeReports) {
|
||||
nativeReport.close();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||
* 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. */
|
||||
|
||||
package com.mozilla.mentat;
|
||||
|
||||
import com.sun.jna.Callback;
|
||||
|
||||
/**
|
||||
* Protocol to be implemented by any object that wishes to register for transaction observation
|
||||
*/
|
||||
public interface TxObserverCallback extends Callback {
|
||||
void transactionObserverCalled(String key, TxChangeList.ByReference reports);
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||
* 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. */
|
||||
|
||||
package com.mozilla.mentat;
|
||||
|
||||
import com.sun.jna.Pointer;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* This class wraps a raw pointer than points to a Rust `TxReport` object.
|
||||
* </p>
|
||||
* The `TxReport` contains information about a successful Mentat transaction.
|
||||
* </p>
|
||||
* This information includes:
|
||||
* <ul>
|
||||
* <li>`txId` - the identifier for the transaction.</li>
|
||||
* <li>`txInstant` - the time that the transaction occured.</li>
|
||||
* <li>a map of temporary identifiers provided in the transaction and the `Entid`s that they were mapped to.</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
* Access an `Entid` for a temporary identifier that was provided in the transaction can be done through `entid(String:)`.
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
* TxReport report = mentat.transact("[[:db/add "a" :foo/boolean true]]");
|
||||
* long aEntid = report.getEntidForTempId("a");
|
||||
*}</pre>
|
||||
*/
|
||||
public class TxReport extends RustObject {
|
||||
|
||||
private Long txId;
|
||||
private Date txInstant;
|
||||
|
||||
|
||||
public TxReport(Pointer pointer) {
|
||||
this.rawPointer = pointer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the identifier for the transaction.
|
||||
* @return The identifier for the transaction.
|
||||
*/
|
||||
public Long getTxId() {
|
||||
if (this.txId == null) {
|
||||
this.txId = JNA.INSTANCE.tx_report_get_entid(this.rawPointer);
|
||||
}
|
||||
|
||||
return this.txId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the time that the transaction occured.
|
||||
* @return The time that the transaction occured.
|
||||
*/
|
||||
public Date getTxInstant() {
|
||||
if (this.txInstant == null) {
|
||||
this.txInstant = new Date(JNA.INSTANCE.tx_report_get_tx_instant(this.rawPointer));
|
||||
}
|
||||
return this.txInstant;
|
||||
}
|
||||
|
||||
/**
|
||||
* Access an `Entid` for a temporary identifier that was provided in the transaction.
|
||||
* @param tempId A {@link String} representing the temporary identifier to fetch the `Entid` for.
|
||||
* @return The `Entid` for the temporary identifier, if present, otherwise `null`.
|
||||
*/
|
||||
public Long getEntidForTempId(String tempId) {
|
||||
Pointer longPointer = JNA.INSTANCE.tx_report_entity_for_temp_id(this.rawPointer, tempId);
|
||||
if (longPointer == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return longPointer.getLong(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
if (this.rawPointer != null) {
|
||||
JNA.INSTANCE.tx_report_destroy(this.rawPointer);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,157 @@
|
|||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||
* 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. */
|
||||
|
||||
package com.mozilla.mentat;
|
||||
|
||||
import com.sun.jna.Pointer;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Date;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* </p>
|
||||
* 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.
|
||||
* </p>
|
||||
* 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.
|
||||
*/
|
||||
public class TypedValue extends RustObject {
|
||||
|
||||
private Object value;
|
||||
|
||||
private boolean isConsumed() {
|
||||
return this.rawPointer == null;
|
||||
}
|
||||
|
||||
public TypedValue(Pointer pointer) {
|
||||
this.rawPointer = pointer;
|
||||
}
|
||||
|
||||
/**
|
||||
* This value as a {@link Long}. This function will panic if the `ValueType` of this
|
||||
* {@link TypedValue} is not a `Long`
|
||||
* @return the value of this {@link TypedValue} as a {@link Long}
|
||||
*/
|
||||
public Long asLong() {
|
||||
if (!this.isConsumed()) {
|
||||
this.value = JNA.INSTANCE.typed_value_into_long(this.rawPointer);
|
||||
this.rawPointer = null;
|
||||
}
|
||||
return (Long)value;
|
||||
}
|
||||
|
||||
/**
|
||||
* This value as a Entid. This function will panic if the `ValueType` of this
|
||||
* {@link TypedValue} is not a `Ref`
|
||||
* @return the value of this {@link TypedValue} as a Entid
|
||||
*/
|
||||
public Long asEntid() {
|
||||
if (!this.isConsumed()) {
|
||||
this.value = JNA.INSTANCE.typed_value_into_entid(this.rawPointer);
|
||||
this.rawPointer = null;
|
||||
}
|
||||
return (Long)value;
|
||||
}
|
||||
|
||||
/**
|
||||
* This value as a keyword {@link String}. This function will panic if the `ValueType` of this
|
||||
* {@link TypedValue} is not a `Keyword`
|
||||
* @return the value of this {@link TypedValue} as a Keyword
|
||||
*/
|
||||
public String asKeyword() {
|
||||
if (!this.isConsumed()) {
|
||||
this.value = JNA.INSTANCE.typed_value_into_kw(this.rawPointer);
|
||||
this.rawPointer = null;
|
||||
}
|
||||
return (String)value;
|
||||
}
|
||||
|
||||
/**
|
||||
* This value as a {@link Boolean}. This function will panic if the `ValueType` of this
|
||||
* {@link TypedValue} is not a `Boolean`
|
||||
* @return the value of this {@link TypedValue} as a {@link Boolean}
|
||||
*/
|
||||
public Boolean asBoolean() {
|
||||
if (!this.isConsumed()) {
|
||||
long value = JNA.INSTANCE.typed_value_into_boolean(this.rawPointer);
|
||||
this.value = value == 0 ? false : true;
|
||||
this.rawPointer = null;
|
||||
}
|
||||
return (Boolean) this.value;
|
||||
}
|
||||
|
||||
/**
|
||||
* This value as a {@link Double}. This function will panic if the `ValueType` of this
|
||||
* {@link TypedValue} is not a `Double`
|
||||
* @return the value of this {@link TypedValue} as a {@link Double}
|
||||
*/
|
||||
public Double asDouble() {
|
||||
if (!this.isConsumed()) {
|
||||
this.value = JNA.INSTANCE.typed_value_into_double(this.rawPointer);
|
||||
this.rawPointer = null;
|
||||
}
|
||||
return (Double)value;
|
||||
}
|
||||
|
||||
/**
|
||||
* This value as a {@link Date}. This function will panic if the `ValueType` of this
|
||||
* {@link TypedValue} is not a `Instant`
|
||||
* @return the value of this {@link TypedValue} as a {@link Date}
|
||||
*/
|
||||
public Date asDate() {
|
||||
if (!this.isConsumed()) {
|
||||
this.value = new Date(JNA.INSTANCE.typed_value_into_timestamp(this.rawPointer) * 1_000);
|
||||
this.rawPointer = null;
|
||||
}
|
||||
return (Date)this.value;
|
||||
}
|
||||
|
||||
/**
|
||||
* This value as a {@link String}. This function will panic if the `ValueType` of this
|
||||
* {@link TypedValue} is not a `String`
|
||||
* @return the value of this {@link TypedValue} as a {@link String}
|
||||
*/
|
||||
public String asString() {
|
||||
if (!this.isConsumed()) {
|
||||
this.value = JNA.INSTANCE.typed_value_into_string(this.rawPointer);
|
||||
this.rawPointer = null;
|
||||
}
|
||||
return (String)value;
|
||||
}
|
||||
|
||||
/**
|
||||
* This value as a {@link UUID}. This function will panic if the `ValueType` of this
|
||||
* {@link TypedValue} is not a `Uuid`
|
||||
* @return the value of this {@link TypedValue} as a {@link UUID}
|
||||
*/
|
||||
public UUID asUUID() {
|
||||
if (!this.isConsumed()) {
|
||||
Pointer uuidPtr = JNA.INSTANCE.typed_value_into_uuid(this.rawPointer);
|
||||
byte[] bytes = uuidPtr.getByteArray(0, 16);
|
||||
ByteBuffer bb = ByteBuffer.wrap(bytes);
|
||||
long high = bb.getLong();
|
||||
long low = bb.getLong();
|
||||
this.value = new UUID(high, low);
|
||||
this.rawPointer = null;
|
||||
}
|
||||
return (UUID)this.value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
if (this.rawPointer != null) {
|
||||
JNA.INSTANCE.typed_value_destroy(this.rawPointer);
|
||||
}
|
||||
}
|
||||
}
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
sdks/android/Mentat/library/src/main/jniLibs/x86/libmentat_ffi.so
Executable file
BIN
sdks/android/Mentat/library/src/main/jniLibs/x86/libmentat_ffi.so
Executable file
Binary file not shown.
|
@ -0,0 +1,3 @@
|
|||
<resources>
|
||||
<string name="app_name">Mentat</string>
|
||||
</resources>
|
1
sdks/android/Mentat/settings.gradle
Normal file
1
sdks/android/Mentat/settings.gradle
Normal file
|
@ -0,0 +1 @@
|
|||
include ':library'
|
615
sdks/swift/Mentat/Mentat.xcodeproj/project.pbxproj
Normal file
615
sdks/swift/Mentat/Mentat.xcodeproj/project.pbxproj
Normal file
|
@ -0,0 +1,615 @@
|
|||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 50;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
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 */; };
|
||||
7BAE75A42089022B00895D37 /* libsqlite3.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 7BAE75A32089022B00895D37 /* libsqlite3.tbd */; };
|
||||
7BDB96942077C299009D0651 /* Mentat.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7BDB968A2077C299009D0651 /* Mentat.framework */; };
|
||||
7BDB96992077C299009D0651 /* MentatTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BDB96982077C299009D0651 /* MentatTests.swift */; };
|
||||
7BDB969B2077C299009D0651 /* Mentat.h in Headers */ = {isa = PBXBuildFile; fileRef = 7BDB968D2077C299009D0651 /* Mentat.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
7BDB96AF2077C38E009D0651 /* Query.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BDB96A62077C38D009D0651 /* Query.swift */; };
|
||||
7BDB96B02077C38E009D0651 /* Mentat.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BDB96A72077C38D009D0651 /* Mentat.swift */; };
|
||||
7BDB96B12077C38E009D0651 /* store.h in Headers */ = {isa = PBXBuildFile; fileRef = 7BDB96A82077C38E009D0651 /* store.h */; };
|
||||
7BDB96B22077C38E009D0651 /* RelResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BDB96A92077C38E009D0651 /* RelResult.swift */; };
|
||||
7BDB96B32077C38E009D0651 /* RustObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BDB96AA2077C38E009D0651 /* RustObject.swift */; };
|
||||
7BDB96B42077C38E009D0651 /* OptionalRustObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BDB96AB2077C38E009D0651 /* OptionalRustObject.swift */; };
|
||||
7BDB96B52077C38E009D0651 /* TupleResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BDB96AC2077C38E009D0651 /* TupleResult.swift */; };
|
||||
7BDB96B72077C38E009D0651 /* TypedValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BDB96AE2077C38E009D0651 /* TypedValue.swift */; };
|
||||
7BDB96C22077CD98009D0651 /* libresolv.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 7BDB96C12077CD98009D0651 /* libresolv.tbd */; };
|
||||
7BDB96C62077D347009D0651 /* Date+Int64.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BDB96C52077D346009D0651 /* Date+Int64.swift */; };
|
||||
7BDB96C9207B735A009D0651 /* fixtures in Resources */ = {isa = PBXBuildFile; fileRef = 7BDB96C8207B735A009D0651 /* fixtures */; };
|
||||
7BDB96CC207B7684009D0651 /* Errors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BDB96CB207B7684009D0651 /* Errors.swift */; };
|
||||
7BEB7D2C207D03DA000369AD /* TxReport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BEB7D2B207D03DA000369AD /* TxReport.swift */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
7BDB96952077C299009D0651 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 7BDB96812077C299009D0651 /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = 7BDB96892077C299009D0651;
|
||||
remoteInfo = Mentat;
|
||||
};
|
||||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
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>"; };
|
||||
7BAE75A32089022B00895D37 /* libsqlite3.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.tbd; path = usr/lib/libsqlite3.tbd; sourceTree = SDKROOT; };
|
||||
7BDB968A2077C299009D0651 /* Mentat.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Mentat.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
7BDB968D2077C299009D0651 /* Mentat.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Mentat.h; sourceTree = "<group>"; };
|
||||
7BDB968E2077C299009D0651 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
7BDB96932077C299009D0651 /* MentatTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MentatTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
7BDB96982077C299009D0651 /* MentatTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MentatTests.swift; sourceTree = "<group>"; };
|
||||
7BDB969A2077C299009D0651 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
7BDB96A62077C38D009D0651 /* Query.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Query.swift; sourceTree = "<group>"; };
|
||||
7BDB96A72077C38D009D0651 /* Mentat.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Mentat.swift; sourceTree = "<group>"; };
|
||||
7BDB96A82077C38E009D0651 /* store.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = store.h; sourceTree = "<group>"; };
|
||||
7BDB96A92077C38E009D0651 /* RelResult.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RelResult.swift; sourceTree = "<group>"; };
|
||||
7BDB96AA2077C38E009D0651 /* RustObject.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RustObject.swift; sourceTree = "<group>"; };
|
||||
7BDB96AB2077C38E009D0651 /* OptionalRustObject.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OptionalRustObject.swift; sourceTree = "<group>"; };
|
||||
7BDB96AC2077C38E009D0651 /* TupleResult.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TupleResult.swift; sourceTree = "<group>"; };
|
||||
7BDB96AE2077C38E009D0651 /* TypedValue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TypedValue.swift; sourceTree = "<group>"; };
|
||||
7BDB96BF2077CD7A009D0651 /* libmentat.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libmentat.a; path = ../../../target/universal/release/libmentat.a; sourceTree = "<group>"; };
|
||||
7BDB96C12077CD98009D0651 /* libresolv.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libresolv.tbd; path = usr/lib/libresolv.tbd; sourceTree = SDKROOT; };
|
||||
7BDB96C32077D090009D0651 /* module.map */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.module-map"; path = module.map; sourceTree = "<group>"; };
|
||||
7BDB96C52077D346009D0651 /* Date+Int64.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Date+Int64.swift"; sourceTree = "<group>"; };
|
||||
7BDB96C8207B735A009D0651 /* fixtures */ = {isa = PBXFileReference; lastKnownFileType = folder; name = fixtures; path = ../../../../fixtures; sourceTree = "<group>"; };
|
||||
7BDB96CB207B7684009D0651 /* Errors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Errors.swift; sourceTree = "<group>"; };
|
||||
7BEB7D21207BDDEF000369AD /* libtoodle.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libtoodle.a; path = ../../../target/universal/release/libtoodle.a; sourceTree = "<group>"; };
|
||||
7BEB7D23207BE2AF000369AD /* libmentat_ffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libmentat_ffi.a; path = ../../../target/universal/release/libmentat_ffi.a; sourceTree = "<group>"; };
|
||||
7BEB7D2B207D03DA000369AD /* TxReport.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TxReport.swift; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
7BDB96862077C299009D0651 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
7BAE75A42089022B00895D37 /* libsqlite3.tbd in Frameworks */,
|
||||
7BDB96C22077CD98009D0651 /* libresolv.tbd in Frameworks */,
|
||||
7BAE75A22089020E00895D37 /* libmentat_ffi.a in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
7BDB96902077C299009D0651 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
7BDB96942077C299009D0651 /* Mentat.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
7BDB96802077C299009D0651 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
7BDB968C2077C299009D0651 /* Mentat */,
|
||||
7BDB96972077C299009D0651 /* MentatTests */,
|
||||
7BDB968B2077C299009D0651 /* Products */,
|
||||
7BDB96BE2077CD7A009D0651 /* Frameworks */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
7BDB968B2077C299009D0651 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
7BDB968A2077C299009D0651 /* Mentat.framework */,
|
||||
7BDB96932077C299009D0651 /* MentatTests.xctest */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
7BDB968C2077C299009D0651 /* Mentat */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
7BDB96CA207B7672009D0651 /* Errors */,
|
||||
7BDB96C42077D346009D0651 /* Extensions */,
|
||||
7BDB96BA2077C42B009D0651 /* Core */,
|
||||
7BDB96A42077C301009D0651 /* Query */,
|
||||
7BDB96B92077C403009D0651 /* Rust */,
|
||||
7BDB96A82077C38E009D0651 /* store.h */,
|
||||
7BDB96A72077C38D009D0651 /* Mentat.swift */,
|
||||
7BEB7D26207BE5BB000369AD /* Transact */,
|
||||
7BDB968D2077C299009D0651 /* Mentat.h */,
|
||||
7BDB968E2077C299009D0651 /* Info.plist */,
|
||||
7BDB96C32077D090009D0651 /* module.map */,
|
||||
);
|
||||
path = Mentat;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
7BDB96972077C299009D0651 /* MentatTests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
7BDB96C8207B735A009D0651 /* fixtures */,
|
||||
7BDB96982077C299009D0651 /* MentatTests.swift */,
|
||||
7BDB969A2077C299009D0651 /* Info.plist */,
|
||||
);
|
||||
path = MentatTests;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
7BDB96A42077C301009D0651 /* Query */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
7BDB96A62077C38D009D0651 /* Query.swift */,
|
||||
7BDB96A92077C38E009D0651 /* RelResult.swift */,
|
||||
7BDB96AC2077C38E009D0651 /* TupleResult.swift */,
|
||||
);
|
||||
path = Query;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
7BDB96B92077C403009D0651 /* Rust */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
7BDB96AB2077C38E009D0651 /* OptionalRustObject.swift */,
|
||||
7BDB96AA2077C38E009D0651 /* RustObject.swift */,
|
||||
);
|
||||
path = Rust;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
7BDB96BA2077C42B009D0651 /* Core */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
7BDB96AE2077C38E009D0651 /* TypedValue.swift */,
|
||||
);
|
||||
path = Core;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
7BDB96BE2077CD7A009D0651 /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
7BAE75A32089022B00895D37 /* libsqlite3.tbd */,
|
||||
7B911E1A2085081D000998CB /* libtoodle.a */,
|
||||
7BEB7D23207BE2AF000369AD /* libmentat_ffi.a */,
|
||||
7BEB7D21207BDDEF000369AD /* libtoodle.a */,
|
||||
7BDB96C12077CD98009D0651 /* libresolv.tbd */,
|
||||
7BDB96BF2077CD7A009D0651 /* libmentat.a */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
7BDB96C42077D346009D0651 /* Extensions */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
7B74483C208DF667006CFFB0 /* Result+Unwrap.swift */,
|
||||
7BDB96C52077D346009D0651 /* Date+Int64.swift */,
|
||||
);
|
||||
path = Extensions;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
7BDB96CA207B7672009D0651 /* Errors */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
7BDB96CB207B7684009D0651 /* Errors.swift */,
|
||||
);
|
||||
path = Errors;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
7BEB7D26207BE5BB000369AD /* Transact */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
7BEB7D2B207D03DA000369AD /* TxReport.swift */,
|
||||
);
|
||||
path = Transact;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXHeadersBuildPhase section */
|
||||
7BDB96872077C299009D0651 /* Headers */ = {
|
||||
isa = PBXHeadersBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
7BDB96B12077C38E009D0651 /* store.h in Headers */,
|
||||
7BDB969B2077C299009D0651 /* Mentat.h in Headers */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXHeadersBuildPhase section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
7BDB96892077C299009D0651 /* Mentat */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 7BDB969E2077C299009D0651 /* Build configuration list for PBXNativeTarget "Mentat" */;
|
||||
buildPhases = (
|
||||
7BDB96852077C299009D0651 /* Sources */,
|
||||
7BDB96862077C299009D0651 /* Frameworks */,
|
||||
7BDB96872077C299009D0651 /* Headers */,
|
||||
7BDB96882077C299009D0651 /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = Mentat;
|
||||
productName = Mentat;
|
||||
productReference = 7BDB968A2077C299009D0651 /* Mentat.framework */;
|
||||
productType = "com.apple.product-type.framework";
|
||||
};
|
||||
7BDB96922077C299009D0651 /* MentatTests */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 7BDB96A12077C299009D0651 /* Build configuration list for PBXNativeTarget "MentatTests" */;
|
||||
buildPhases = (
|
||||
7BDB968F2077C299009D0651 /* Sources */,
|
||||
7BDB96902077C299009D0651 /* Frameworks */,
|
||||
7BDB96912077C299009D0651 /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
7BDB96962077C299009D0651 /* PBXTargetDependency */,
|
||||
);
|
||||
name = MentatTests;
|
||||
productName = MentatTests;
|
||||
productReference = 7BDB96932077C299009D0651 /* MentatTests.xctest */;
|
||||
productType = "com.apple.product-type.bundle.unit-test";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
7BDB96812077C299009D0651 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastSwiftUpdateCheck = 0930;
|
||||
LastUpgradeCheck = 0930;
|
||||
ORGANIZATIONNAME = Mozilla;
|
||||
TargetAttributes = {
|
||||
7BDB96892077C299009D0651 = {
|
||||
CreatedOnToolsVersion = 9.3;
|
||||
LastSwiftMigration = 0930;
|
||||
};
|
||||
7BDB96922077C299009D0651 = {
|
||||
CreatedOnToolsVersion = 9.3;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 7BDB96842077C299009D0651 /* Build configuration list for PBXProject "Mentat" */;
|
||||
compatibilityVersion = "Xcode 9.3";
|
||||
developmentRegion = en;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
en,
|
||||
);
|
||||
mainGroup = 7BDB96802077C299009D0651;
|
||||
productRefGroup = 7BDB968B2077C299009D0651 /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
7BDB96892077C299009D0651 /* Mentat */,
|
||||
7BDB96922077C299009D0651 /* MentatTests */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXResourcesBuildPhase section */
|
||||
7BDB96882077C299009D0651 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
7BDB96912077C299009D0651 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
7BDB96C9207B735A009D0651 /* fixtures in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
7BDB96852077C299009D0651 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
7BDB96B32077C38E009D0651 /* RustObject.swift in Sources */,
|
||||
7BDB96C62077D347009D0651 /* Date+Int64.swift in Sources */,
|
||||
7BEB7D2C207D03DA000369AD /* TxReport.swift in Sources */,
|
||||
7BDB96B42077C38E009D0651 /* OptionalRustObject.swift in Sources */,
|
||||
7BDB96B22077C38E009D0651 /* RelResult.swift in Sources */,
|
||||
7BDB96AF2077C38E009D0651 /* Query.swift in Sources */,
|
||||
7BDB96CC207B7684009D0651 /* Errors.swift in Sources */,
|
||||
7BDB96B02077C38E009D0651 /* Mentat.swift in Sources */,
|
||||
7BDB96B72077C38E009D0651 /* TypedValue.swift in Sources */,
|
||||
7BDB96B52077C38E009D0651 /* TupleResult.swift in Sources */,
|
||||
7B74483D208DF667006CFFB0 /* Result+Unwrap.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
7BDB968F2077C299009D0651 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
7BDB96992077C299009D0651 /* MentatTests.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXTargetDependency section */
|
||||
7BDB96962077C299009D0651 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = 7BDB96892077C299009D0651 /* Mentat */;
|
||||
targetProxy = 7BDB96952077C299009D0651 /* PBXContainerItemProxy */;
|
||||
};
|
||||
/* End PBXTargetDependency section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
7BDB969C2077C299009D0651 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 11.3;
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
OTHER_CFLAGS = "-fembed-bitcode";
|
||||
OTHER_LDFLAGS = "";
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||
SWIFT_INCLUDE_PATHS = "$(SRCROOT)/Mentat";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
VERSION_INFO_PREFIX = "";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
7BDB969D2077C299009D0651 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 11.3;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
OTHER_CFLAGS = "-fembed-bitcode";
|
||||
OTHER_LDFLAGS = "";
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_COMPILATION_MODE = wholemodule;
|
||||
SWIFT_INCLUDE_PATHS = "$(SRCROOT)/Mentat";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-O";
|
||||
VALIDATE_PRODUCT = YES;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
VERSION_INFO_PREFIX = "";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
7BDB969F2077C299009D0651 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_IDENTITY = "";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEFINES_MODULE = YES;
|
||||
DEVELOPMENT_TEAM = 8BHJ767F4Y;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 1;
|
||||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||
INFOPLIST_FILE = Mentat/Info.plist;
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
"@loader_path/Frameworks",
|
||||
);
|
||||
LIBRARY_SEARCH_PATHS = "$(PROJECT_DIR)/../../../target/universal/release";
|
||||
OTHER_CFLAGS = "-fembed-bitcode";
|
||||
OTHER_LDFLAGS = "";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.mozilla.Mentat;
|
||||
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_INCLUDE_PATHS = "$(SRCROOT)/Mentat";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 4.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
7BDB96A02077C299009D0651 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_IDENTITY = "";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEFINES_MODULE = YES;
|
||||
DEVELOPMENT_TEAM = 8BHJ767F4Y;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 1;
|
||||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||
ENABLE_TESTABILITY = NO;
|
||||
INFOPLIST_FILE = Mentat/Info.plist;
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
"@loader_path/Frameworks",
|
||||
);
|
||||
LIBRARY_SEARCH_PATHS = "$(PROJECT_DIR)/../../../target/universal/release";
|
||||
OTHER_CFLAGS = "-fembed-bitcode";
|
||||
OTHER_LDFLAGS = "";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.mozilla.Mentat;
|
||||
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_INCLUDE_PATHS = "$(SRCROOT)/Mentat";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "";
|
||||
SWIFT_VERSION = 4.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
7BDB96A22077C299009D0651 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEVELOPMENT_TEAM = 8BHJ767F4Y;
|
||||
INFOPLIST_FILE = MentatTests/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
"@loader_path/Frameworks",
|
||||
);
|
||||
OTHER_CFLAGS = "";
|
||||
OTHER_LDFLAGS = "";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.mozilla.MentatTests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 4.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
7BDB96A32077C299009D0651 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEVELOPMENT_TEAM = 8BHJ767F4Y;
|
||||
INFOPLIST_FILE = MentatTests/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
"@loader_path/Frameworks",
|
||||
);
|
||||
OTHER_CFLAGS = "";
|
||||
OTHER_LDFLAGS = "";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.mozilla.MentatTests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 4.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
7BDB96842077C299009D0651 /* Build configuration list for PBXProject "Mentat" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
7BDB969C2077C299009D0651 /* Debug */,
|
||||
7BDB969D2077C299009D0651 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
7BDB969E2077C299009D0651 /* Build configuration list for PBXNativeTarget "Mentat" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
7BDB969F2077C299009D0651 /* Debug */,
|
||||
7BDB96A02077C299009D0651 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
7BDB96A12077C299009D0651 /* Build configuration list for PBXNativeTarget "MentatTests" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
7BDB96A22077C299009D0651 /* Debug */,
|
||||
7BDB96A32077C299009D0651 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = 7BDB96812077C299009D0651 /* Project object */;
|
||||
}
|
7
sdks/swift/Mentat/Mentat.xcodeproj/project.xcworkspace/contents.xcworkspacedata
generated
Normal file
7
sdks/swift/Mentat/Mentat.xcodeproj/project.xcworkspace/contents.xcworkspacedata
generated
Normal file
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "self:Mentat.xcodeproj">
|
||||
</FileRef>
|
||||
</Workspace>
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>IDEDidComputeMac32BitWarning</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict/>
|
||||
</plist>
|
|
@ -0,0 +1,107 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0930"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "7BDB96892077C299009D0651"
|
||||
BuildableName = "Mentat.framework"
|
||||
BlueprintName = "Mentat"
|
||||
ReferencedContainer = "container:Mentat.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "7BDB96922077C299009D0651"
|
||||
BuildableName = "MentatTests.xctest"
|
||||
BlueprintName = "MentatTests"
|
||||
ReferencedContainer = "container:Mentat.xcodeproj">
|
||||
</BuildableReference>
|
||||
<SkippedTests>
|
||||
<Test
|
||||
Identifier = "MentatTests/testBindBoolean()">
|
||||
</Test>
|
||||
<Test
|
||||
Identifier = "MentatTests/testBindDate()">
|
||||
</Test>
|
||||
</SkippedTests>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "7BDB96892077C299009D0651"
|
||||
BuildableName = "Mentat.framework"
|
||||
BlueprintName = "Mentat"
|
||||
ReferencedContainer = "container:Mentat.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "7BDB96892077C299009D0651"
|
||||
BuildableName = "Mentat.framework"
|
||||
BlueprintName = "Mentat"
|
||||
ReferencedContainer = "container:Mentat.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "7BDB96892077C299009D0651"
|
||||
BuildableName = "Mentat.framework"
|
||||
BlueprintName = "Mentat"
|
||||
ReferencedContainer = "container:Mentat.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
|
@ -0,0 +1,99 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0930"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "7BDB96892077C299009D0651"
|
||||
BuildableName = "Mentat.framework"
|
||||
BlueprintName = "Mentat"
|
||||
ReferencedContainer = "container:Mentat.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "7BDB96922077C299009D0651"
|
||||
BuildableName = "MentatTests.xctest"
|
||||
BlueprintName = "MentatTests"
|
||||
ReferencedContainer = "container:Mentat.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "7BDB96892077C299009D0651"
|
||||
BuildableName = "Mentat.framework"
|
||||
BlueprintName = "Mentat"
|
||||
ReferencedContainer = "container:Mentat.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Release"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "7BDB96892077C299009D0651"
|
||||
BuildableName = "Mentat.framework"
|
||||
BlueprintName = "Mentat"
|
||||
ReferencedContainer = "container:Mentat.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "7BDB96892077C299009D0651"
|
||||
BuildableName = "Mentat.framework"
|
||||
BlueprintName = "Mentat"
|
||||
ReferencedContainer = "container:Mentat.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
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_into_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_into_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_into_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_into_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_into_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_into_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_into_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_into_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
|
||||
}
|
22
sdks/swift/Mentat/Mentat/Extensions/Date+Int64.swift
Normal file
22
sdks/swift/Mentat/Mentat/Extensions/Date+Int64.swift
Normal file
|
@ -0,0 +1,22 @@
|
|||
/* 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
|
||||
|
||||
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
|
||||
}
|
||||
}
|
24
sdks/swift/Mentat/Mentat/Info.plist
Normal file
24
sdks/swift/Mentat/Mentat/Info.plist
Normal file
|
@ -0,0 +1,24 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string></string>
|
||||
</dict>
|
||||
</plist>
|
18
sdks/swift/Mentat/Mentat/Mentat.h
Normal file
18
sdks/swift/Mentat/Mentat/Mentat.h
Normal file
|
@ -0,0 +1,18 @@
|
|||
/* 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 <UIKit/UIKit.h>
|
||||
|
||||
//! Project version number for Mentat.
|
||||
FOUNDATION_EXPORT double MentatVersionNumber;
|
||||
|
||||
//! Project version string for Mentat.
|
||||
FOUNDATION_EXPORT const unsigned char MentatVersionString[];
|
||||
|
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 implemented 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 construct 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 = typed_value_result_set_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 = typed_value_result_set_into_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_into_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_into_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_into_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_into_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_into_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_into_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_into_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_into_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 = typed_value_list_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 = typed_value_list_into_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.
|
||||
|
||||
- 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)
|
||||
}
|
||||
}
|
4
sdks/swift/Mentat/Mentat/module.map
Normal file
4
sdks/swift/Mentat/Mentat/module.map
Normal file
|
@ -0,0 +1,4 @@
|
|||
module MentatStore [system][extern_c] {
|
||||
header "store.h"
|
||||
export *
|
||||
}
|
171
sdks/swift/Mentat/Mentat/store.h
Normal file
171
sdks/swift/Mentat/Mentat/store.h
Normal file
|
@ -0,0 +1,171 @@
|
|||
/* 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. */
|
||||
|
||||
#ifndef store_h
|
||||
#define store_h
|
||||
#include <stdint.h>
|
||||
#include <Foundation/NSObjCRuntime.h>
|
||||
|
||||
/*
|
||||
* This file contains headers for all of the structs and functions that map directly to the functions
|
||||
* defined in mentat/ffi/src/lib.rs.
|
||||
*
|
||||
* The C in this file is specifically formatted to be used with Objective C and Swift and contains
|
||||
* macros and flags that will not be recognised by other C based languages.
|
||||
*/
|
||||
|
||||
/*
|
||||
A mapping of the TxChange repr(C) Rust object.
|
||||
The memory for this is managed by Swift.
|
||||
*/
|
||||
struct TxChange {
|
||||
int64_t txid;
|
||||
int64_t*_Nonnull* _Nonnull changes;
|
||||
uint64_t len;
|
||||
};
|
||||
|
||||
/*
|
||||
A mapping of the TxChangeList repr(C) Rust object.
|
||||
The memory for this is managed by Swift.
|
||||
*/
|
||||
struct TxChangeList {
|
||||
struct TxChange*_Nonnull* _Nonnull reports;
|
||||
uint64_t len;
|
||||
};
|
||||
typedef struct TxChangeList TxChangeList;
|
||||
|
||||
/*
|
||||
A mapping of the ExternResult repr(C) Rust object.
|
||||
The memory for this is managed by Swift.
|
||||
*/
|
||||
struct Result {
|
||||
void* _Nullable ok;
|
||||
char* _Nullable err;
|
||||
};
|
||||
typedef struct Result Result;
|
||||
|
||||
/*
|
||||
A mapping of the ExternOption repr(C) Rust object.
|
||||
The memory for this is managed by Swift.
|
||||
*/
|
||||
struct Option {
|
||||
void* _Nullable value;
|
||||
};
|
||||
typedef struct Option Option;
|
||||
|
||||
/*
|
||||
A Mapping for the ValueType Rust object.
|
||||
*/
|
||||
typedef NS_ENUM(NSInteger, ValueType) {
|
||||
ValueTypeRef = 1,
|
||||
ValueTypeBoolean,
|
||||
ValueTypeInstant,
|
||||
ValueTypeLong,
|
||||
ValueTypeDouble,
|
||||
ValueTypeString,
|
||||
ValueTypeKeyword,
|
||||
ValueTypeUuid
|
||||
};
|
||||
|
||||
// Opaque Structs mapping to Rust types that are passed over the FFI boundary
|
||||
struct EntityBuilder;
|
||||
struct InProgress;
|
||||
struct InProgressBuilder;
|
||||
struct Query;
|
||||
struct QueryResultRow;
|
||||
struct QueryResultRows;
|
||||
struct QueryRowsIterator;
|
||||
struct QueryRowIterator;
|
||||
struct Store;
|
||||
struct TxReport;
|
||||
struct TypedValue;
|
||||
|
||||
// Store
|
||||
struct Store*_Nonnull store_open(const char*_Nonnull uri);
|
||||
|
||||
// Destructors.
|
||||
void destroy(void* _Nullable obj);
|
||||
void query_builder_destroy(struct Query* _Nullable obj);
|
||||
void store_destroy(struct Store* _Nonnull obj);
|
||||
void tx_report_destroy(struct TxReport* _Nonnull obj);
|
||||
void typed_value_destroy(struct TypedValue* _Nullable obj);
|
||||
void typed_value_list_destroy(struct QueryResultRow* _Nullable obj);
|
||||
void typed_value_list_iter_destroy(struct QueryRowIterator* _Nullable obj);
|
||||
void typed_value_result_set_destroy(struct QueryResultRows* _Nullable obj);
|
||||
void typed_value_result_set_iter_destroy(struct QueryRowsIterator* _Nullable obj);
|
||||
|
||||
// transact
|
||||
struct Result*_Nonnull store_transact(struct Store*_Nonnull store, const char* _Nonnull transaction);
|
||||
const int64_t* _Nullable tx_report_entity_for_temp_id(const struct TxReport* _Nonnull report, const char* _Nonnull tempid);
|
||||
int64_t tx_report_get_entid(const struct TxReport* _Nonnull report);
|
||||
int64_t tx_report_get_tx_instant(const struct TxReport* _Nonnull report);
|
||||
|
||||
|
||||
// Sync
|
||||
struct Result*_Nonnull store_sync(struct Store*_Nonnull store, const char* _Nonnull user_uuid, const char* _Nonnull server_uri);
|
||||
|
||||
// Observers
|
||||
void store_register_observer(struct Store*_Nonnull store, const char* _Nonnull key, const int64_t* _Nonnull attributes, const int64_t len, void (*_Nonnull callback_fn)(const char* _Nonnull key, const struct TxChangeList* _Nonnull reports));
|
||||
void store_unregister_observer(struct Store*_Nonnull store, const char* _Nonnull key);
|
||||
int64_t store_entid_for_attribute(struct Store*_Nonnull store, const char*_Nonnull attr);
|
||||
int64_t changelist_entry_at(const struct TxChange* _Nonnull report, size_t index);
|
||||
|
||||
// Query
|
||||
struct Query*_Nonnull store_query(struct Store*_Nonnull store, const char* _Nonnull query);
|
||||
struct Result*_Nonnull store_value_for_attribute(struct Store*_Nonnull store, const int64_t entid, const char* _Nonnull attribute);
|
||||
|
||||
// Query Variable Binding
|
||||
void query_builder_bind_long(struct Query*_Nonnull query, const char* _Nonnull var, const int64_t value);
|
||||
void query_builder_bind_ref(struct Query*_Nonnull query, const char* _Nonnull var, const int64_t value);
|
||||
void query_builder_bind_ref_kw(struct Query*_Nonnull query, const char* _Nonnull var, const char* _Nonnull value);
|
||||
void query_builder_bind_kw(struct Query*_Nonnull query, const char* _Nonnull var, const char* _Nonnull value);
|
||||
void query_builder_bind_boolean(struct Query*_Nonnull query, const char* _Nonnull var, const int32_t value);
|
||||
void query_builder_bind_double(struct Query*_Nonnull query, const char* _Nonnull var, const double value);
|
||||
void query_builder_bind_timestamp(struct Query*_Nonnull query, const char* _Nonnull var, const int64_t value);
|
||||
void query_builder_bind_string(struct Query*_Nonnull query, const char* _Nonnull var, const char* _Nonnull value);
|
||||
void query_builder_bind_uuid(struct Query*_Nonnull query, const char* _Nonnull var, const uuid_t* _Nonnull value);
|
||||
|
||||
// Query execution
|
||||
struct Result*_Nonnull query_builder_execute(struct Query*_Nonnull query);
|
||||
struct Result*_Nonnull query_builder_execute_scalar(struct Query*_Nonnull query);
|
||||
struct Result*_Nonnull query_builder_execute_coll(struct Query*_Nonnull query);
|
||||
struct Result*_Nonnull query_builder_execute_tuple(struct Query*_Nonnull query);
|
||||
|
||||
// Query Result Processing
|
||||
int64_t typed_value_into_long(struct TypedValue*_Nonnull value);
|
||||
int64_t typed_value_into_entid(struct TypedValue*_Nonnull value);
|
||||
const char* _Nonnull typed_value_into_kw(struct TypedValue*_Nonnull value);
|
||||
int32_t typed_value_into_boolean(struct TypedValue*_Nonnull value);
|
||||
double typed_value_into_double(struct TypedValue*_Nonnull value);
|
||||
int64_t typed_value_into_timestamp(struct TypedValue*_Nonnull value);
|
||||
const char* _Nonnull typed_value_into_string(struct TypedValue*_Nonnull value);
|
||||
const uuid_t* _Nonnull typed_value_into_uuid(struct TypedValue*_Nonnull value);
|
||||
enum ValueType typed_value_value_type(struct TypedValue*_Nonnull value);
|
||||
|
||||
struct QueryResultRow* _Nullable row_at_index(struct QueryResultRows* _Nonnull rows, const int32_t index);
|
||||
struct QueryRowsIterator* _Nonnull typed_value_result_set_into_iter(struct QueryResultRows* _Nonnull rows);
|
||||
struct QueryResultRow* _Nullable typed_value_result_set_iter_next(struct QueryRowsIterator* _Nonnull iter);
|
||||
struct QueryRowIterator* _Nonnull typed_value_list_into_iter(struct QueryResultRow* _Nonnull row);
|
||||
struct TypedValue* _Nullable typed_value_list_iter_next(struct QueryRowIterator* _Nonnull iter);
|
||||
|
||||
struct TypedValue* _Nonnull value_at_index(struct QueryResultRow* _Nonnull row, const int32_t index);
|
||||
int64_t value_at_index_into_long(struct QueryResultRow* _Nonnull row, const int32_t index);
|
||||
int64_t value_at_index_into_entid(struct QueryResultRow* _Nonnull row, const int32_t index);
|
||||
const char* _Nonnull value_at_index_into_kw(struct QueryResultRow* _Nonnull row, const int32_t index);
|
||||
int32_t value_at_index_into_boolean(struct QueryResultRow* _Nonnull row, const int32_t index);
|
||||
double value_at_index_into_double(struct QueryResultRow* _Nonnull row, const int32_t index);
|
||||
int64_t value_at_index_into_timestamp(struct QueryResultRow* _Nonnull row, const int32_t index);
|
||||
const char* _Nonnull value_at_index_into_string(struct QueryResultRow* _Nonnull row, const int32_t index);
|
||||
const uuid_t* _Nonnull value_at_index_into_uuid(struct QueryResultRow* _Nonnull row, const int32_t index);
|
||||
|
||||
// Transaction change lists
|
||||
const struct TxChange* _Nullable tx_change_list_entry_at(const struct TxChangeList* _Nonnull list, size_t index);
|
||||
|
||||
#endif /* store_h */
|
22
sdks/swift/Mentat/MentatTests/Info.plist
Normal file
22
sdks/swift/Mentat/MentatTests/Info.plist
Normal file
|
@ -0,0 +1,22 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>BNDL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
</dict>
|
||||
</plist>
|
765
sdks/swift/Mentat/MentatTests/MentatTests.swift
Normal file
765
sdks/swift/Mentat/MentatTests/MentatTests.swift
Normal file
|
@ -0,0 +1,765 @@
|
|||
/* 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 XCTest
|
||||
|
||||
@testable import Mentat
|
||||
|
||||
class MentatTests: XCTestCase {
|
||||
|
||||
var citiesSchema: String?
|
||||
var seattleData: String?
|
||||
var store: Mentat?
|
||||
|
||||
override func setUp() {
|
||||
super.setUp()
|
||||
// Put setup code here. This method is called before the invocation of each test method in the class.
|
||||
}
|
||||
|
||||
override func tearDown() {
|
||||
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
||||
super.tearDown()
|
||||
}
|
||||
|
||||
// test that a store can be opened in memory
|
||||
func testOpenInMemoryStore() {
|
||||
XCTAssertNotNil(Mentat().raw)
|
||||
}
|
||||
|
||||
// test that a store can be opened in a specific location
|
||||
func testOpenStoreInLocation() {
|
||||
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
|
||||
let documentsURL = paths[0]
|
||||
let storeURI = documentsURL.appendingPathComponent("test.db", isDirectory: false).absoluteString
|
||||
XCTAssertNotNil(Mentat(storeURI: storeURI).raw)
|
||||
}
|
||||
|
||||
func readFile(forResource resource: String, withExtension ext: String, subdirectory: String ) throws -> String {
|
||||
let bundle = Bundle(for: type(of: self))
|
||||
let schemaUrl = bundle.url(forResource: resource, withExtension: ext, subdirectory: subdirectory)!
|
||||
let contents = try String(contentsOf: schemaUrl)
|
||||
return contents
|
||||
}
|
||||
|
||||
func readCitiesSchema() throws -> String {
|
||||
guard let schema = self.citiesSchema else {
|
||||
self.citiesSchema = try self.readFile(forResource: "cities", withExtension: "schema", subdirectory: "fixtures")
|
||||
return self.citiesSchema!
|
||||
}
|
||||
|
||||
return schema
|
||||
}
|
||||
|
||||
func readSeattleData() throws -> String {
|
||||
guard let data = self.seattleData else {
|
||||
self.seattleData = try self.readFile(forResource: "all_seattle", withExtension: "edn", subdirectory: "fixtures")
|
||||
return self.seattleData!
|
||||
}
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
func transactCitiesSchema(mentat: Mentat) throws -> TxReport {
|
||||
let vocab = try readCitiesSchema()
|
||||
let report = try mentat.transact(transaction: vocab)
|
||||
return report
|
||||
}
|
||||
|
||||
func transactSeattleData(mentat: Mentat) throws -> TxReport {
|
||||
let data = try readSeattleData()
|
||||
let report = try mentat.transact(transaction: data)
|
||||
return report
|
||||
}
|
||||
|
||||
func openAndInitializeCitiesStore() -> Mentat {
|
||||
guard let mentat = self.store else {
|
||||
let mentat = Mentat()
|
||||
let _ = try! self.transactCitiesSchema(mentat: mentat)
|
||||
let _ = try! self.transactSeattleData(mentat: mentat)
|
||||
self.store = mentat
|
||||
return mentat
|
||||
}
|
||||
|
||||
return mentat
|
||||
}
|
||||
|
||||
func populateWithTypesSchema(mentat: Mentat) -> (TxReport?, TxReport?) {
|
||||
do {
|
||||
let schema = """
|
||||
[
|
||||
[:db/add "b" :db/ident :foo/boolean]
|
||||
[:db/add "b" :db/valueType :db.type/boolean]
|
||||
[:db/add "b" :db/cardinality :db.cardinality/one]
|
||||
[:db/add "l" :db/ident :foo/long]
|
||||
[:db/add "l" :db/valueType :db.type/long]
|
||||
[:db/add "l" :db/cardinality :db.cardinality/one]
|
||||
[:db/add "r" :db/ident :foo/ref]
|
||||
[:db/add "r" :db/valueType :db.type/ref]
|
||||
[:db/add "r" :db/cardinality :db.cardinality/one]
|
||||
[:db/add "i" :db/ident :foo/instant]
|
||||
[:db/add "i" :db/valueType :db.type/instant]
|
||||
[:db/add "i" :db/cardinality :db.cardinality/one]
|
||||
[:db/add "d" :db/ident :foo/double]
|
||||
[:db/add "d" :db/valueType :db.type/double]
|
||||
[:db/add "d" :db/cardinality :db.cardinality/one]
|
||||
[:db/add "s" :db/ident :foo/string]
|
||||
[:db/add "s" :db/valueType :db.type/string]
|
||||
[:db/add "s" :db/cardinality :db.cardinality/one]
|
||||
[:db/add "k" :db/ident :foo/keyword]
|
||||
[:db/add "k" :db/valueType :db.type/keyword]
|
||||
[:db/add "k" :db/cardinality :db.cardinality/one]
|
||||
[:db/add "u" :db/ident :foo/uuid]
|
||||
[:db/add "u" :db/valueType :db.type/uuid]
|
||||
[:db/add "u" :db/cardinality :db.cardinality/one]
|
||||
]
|
||||
"""
|
||||
let report = try mentat.transact(transaction: schema)
|
||||
let stringEntid = report.entid(forTempId: "s")!
|
||||
|
||||
let data = """
|
||||
[
|
||||
[:db/add "a" :foo/boolean true]
|
||||
[:db/add "a" :foo/long 25]
|
||||
[:db/add "a" :foo/instant #inst "2017-01-01T11:00:00.000Z"]
|
||||
[:db/add "a" :foo/double 11.23]
|
||||
[:db/add "a" :foo/string "The higher we soar the smaller we appear to those who cannot fly."]
|
||||
[:db/add "a" :foo/keyword :foo/string]
|
||||
[:db/add "a" :foo/uuid #uuid "550e8400-e29b-41d4-a716-446655440000"]
|
||||
[:db/add "b" :foo/boolean false]
|
||||
[:db/add "b" :foo/ref \(stringEntid)]
|
||||
[:db/add "b" :foo/keyword :foo/string]
|
||||
[:db/add "b" :foo/long 50]
|
||||
[:db/add "b" :foo/instant #inst "2018-01-01T11:00:00.000Z"]
|
||||
[:db/add "b" :foo/double 22.46]
|
||||
[:db/add "b" :foo/string "Silence is worse; all truths that are kept silent become poisonous."]
|
||||
[:db/add "b" :foo/uuid #uuid "4cb3f828-752d-497a-90c9-b1fd516d5644"]
|
||||
]
|
||||
"""
|
||||
let dataReport = try mentat.transact(transaction: data)
|
||||
return (report, dataReport)
|
||||
} catch {
|
||||
assertionFailure(error.localizedDescription)
|
||||
}
|
||||
return (nil, nil)
|
||||
}
|
||||
|
||||
func test1TransactVocabulary() {
|
||||
do {
|
||||
let mentat = Mentat()
|
||||
let vocab = try readCitiesSchema()
|
||||
let report = try mentat.transact(transaction: vocab)
|
||||
XCTAssertNotNil(report)
|
||||
assert(report.txId > 0)
|
||||
} catch {
|
||||
assertionFailure(error.localizedDescription)
|
||||
}
|
||||
}
|
||||
|
||||
func test2TransactEntities() {
|
||||
do {
|
||||
let mentat = Mentat()
|
||||
let vocab = try readCitiesSchema()
|
||||
let _ = try mentat.transact(transaction: vocab)
|
||||
let data = try readSeattleData()
|
||||
let report = try mentat.transact(transaction: data)
|
||||
XCTAssertNotNil(report)
|
||||
assert(report.txId > 0)
|
||||
let entid = report.entid(forTempId: "a17592186045438")
|
||||
assert(entid == 65566)
|
||||
} catch {
|
||||
assertionFailure(error.localizedDescription)
|
||||
}
|
||||
}
|
||||
|
||||
func testQueryScalar() {
|
||||
let mentat = openAndInitializeCitiesStore()
|
||||
let query = "[:find ?n . :in ?name :where [(fulltext $ :community/name ?name) [[?e ?n]]]]"
|
||||
let expect = expectation(description: "Query is executed")
|
||||
XCTAssertNoThrow(try mentat.query(query: query).bind(varName: "?name", toString: "Wallingford").runScalar(callback: { scalarResult in
|
||||
guard let result = scalarResult?.asString() else {
|
||||
return assertionFailure("No String value received")
|
||||
}
|
||||
assert(result == "KOMO Communities - Wallingford")
|
||||
expect.fulfill()
|
||||
}))
|
||||
waitForExpectations(timeout: 1) { error in
|
||||
if let error = error {
|
||||
assertionFailure("waitForExpectationsWithTimeout errored: \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testQueryColl() {
|
||||
let mentat = openAndInitializeCitiesStore()
|
||||
let query = "[:find [?when ...] :where [_ :db/txInstant ?when] :order (asc ?when)]"
|
||||
let expect = expectation(description: "Query is executed")
|
||||
XCTAssertNoThrow(try mentat.query(query: query).runColl(callback: { collResult in
|
||||
guard let rows = collResult else {
|
||||
return assertionFailure("No results received")
|
||||
}
|
||||
// we are expecting 3 results
|
||||
for i in 0..<3 {
|
||||
let _ = rows.asDate(index: i)
|
||||
assert(true)
|
||||
}
|
||||
expect.fulfill()
|
||||
}))
|
||||
waitForExpectations(timeout: 1) { error in
|
||||
if let error = error {
|
||||
assertionFailure("waitForExpectationsWithTimeout errored: \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testQueryCollResultIterator() {
|
||||
let mentat = openAndInitializeCitiesStore()
|
||||
let query = "[:find [?when ...] :where [_ :db/txInstant ?when] :order (asc ?when)]"
|
||||
let expect = expectation(description: "Query is executed")
|
||||
XCTAssertNoThrow(try mentat.query(query: query).runColl(callback: { collResult in
|
||||
guard let rows = collResult else {
|
||||
return assertionFailure("No results received")
|
||||
}
|
||||
|
||||
rows.forEach({ (value) in
|
||||
assert(value.valueType.rawValue == 2)
|
||||
})
|
||||
expect.fulfill()
|
||||
}))
|
||||
waitForExpectations(timeout: 1) { error in
|
||||
if let error = error {
|
||||
assertionFailure("waitForExpectationsWithTimeout errored: \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testQueryTuple() {
|
||||
let mentat = openAndInitializeCitiesStore()
|
||||
let query = """
|
||||
[:find [?name ?cat]
|
||||
:where
|
||||
[?c :community/name ?name]
|
||||
[?c :community/type :community.type/website]
|
||||
[(fulltext $ :community/category "food") [[?c ?cat]]]]
|
||||
"""
|
||||
let expect = expectation(description: "Query is executed")
|
||||
XCTAssertNoThrow(try mentat.query(query: query).runTuple(callback: { tupleResult in
|
||||
guard let tuple = tupleResult else {
|
||||
return assertionFailure("expecting a result")
|
||||
}
|
||||
let name = tuple.asString(index: 0)
|
||||
let category = tuple.asString(index: 1)
|
||||
assert(name == "Community Harvest of Southwest Seattle")
|
||||
assert(category == "sustainable food")
|
||||
expect.fulfill()
|
||||
}))
|
||||
waitForExpectations(timeout: 1) { error in
|
||||
if let error = error {
|
||||
assertionFailure("waitForExpectationsWithTimeout errored: \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testQueryRel() {
|
||||
let mentat = openAndInitializeCitiesStore()
|
||||
let query = """
|
||||
[:find ?name ?cat
|
||||
:where
|
||||
[?c :community/name ?name]
|
||||
[?c :community/type :community.type/website]
|
||||
[(fulltext $ :community/category "food") [[?c ?cat]]]]
|
||||
"""
|
||||
let expect = expectation(description: "Query is executed")
|
||||
let expectedResults = [("InBallard", "food"),
|
||||
("Seattle Chinatown Guide", "food"),
|
||||
("Community Harvest of Southwest Seattle", "sustainable food"),
|
||||
("University District Food Bank", "food bank")]
|
||||
XCTAssertNoThrow(try mentat.query(query: query).run(callback: { relResult in
|
||||
guard let rows = relResult else {
|
||||
return assertionFailure("No results received")
|
||||
}
|
||||
|
||||
for (i, row) in rows.enumerated() {
|
||||
let (name, category) = expectedResults[i]
|
||||
assert( row.asString(index: 0) == name)
|
||||
assert(row.asString(index: 1) == category)
|
||||
}
|
||||
expect.fulfill()
|
||||
}))
|
||||
waitForExpectations(timeout: 1) { error in
|
||||
if let error = error {
|
||||
assertionFailure("waitForExpectationsWithTimeout errored: \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testQueryRelResultIterator() {
|
||||
let mentat = openAndInitializeCitiesStore()
|
||||
let query = """
|
||||
[:find ?name ?cat
|
||||
:where
|
||||
[?c :community/name ?name]
|
||||
[?c :community/type :community.type/website]
|
||||
[(fulltext $ :community/category "food") [[?c ?cat]]]]
|
||||
"""
|
||||
let expect = expectation(description: "Query is executed")
|
||||
let expectedResults = [("InBallard", "food"),
|
||||
("Seattle Chinatown Guide", "food"),
|
||||
("Community Harvest of Southwest Seattle", "sustainable food"),
|
||||
("University District Food Bank", "food bank")]
|
||||
XCTAssertNoThrow(try mentat.query(query: query).run(callback: { relResult in
|
||||
guard let rows = relResult else {
|
||||
return assertionFailure("No results received")
|
||||
}
|
||||
|
||||
var i = 0
|
||||
rows.forEach({ (row) in
|
||||
let (name, category) = expectedResults[i]
|
||||
i += 1
|
||||
assert(row.asString(index: 0) == name)
|
||||
assert(row.asString(index: 1) == category)
|
||||
})
|
||||
assert(i == 4)
|
||||
expect.fulfill()
|
||||
}))
|
||||
waitForExpectations(timeout: 1) { error in
|
||||
if let error = error {
|
||||
assertionFailure("waitForExpectationsWithTimeout errored: \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testBindLong() {
|
||||
let mentat = Mentat()
|
||||
let (_, report) = self.populateWithTypesSchema(mentat: mentat)
|
||||
let aEntid = report!.entid(forTempId: "a")
|
||||
let query = "[:find ?e . :in ?long :where [?e :foo/long ?long]]"
|
||||
let expect = expectation(description: "Query is executed")
|
||||
XCTAssertNoThrow(try mentat.query(query: query)
|
||||
.bind(varName: "?long", toLong: 25)
|
||||
.runScalar { value in
|
||||
XCTAssertNotNil(value)
|
||||
assert(value?.asEntid() == aEntid)
|
||||
expect.fulfill()
|
||||
})
|
||||
waitForExpectations(timeout: 1) { error in
|
||||
if let error = error {
|
||||
assertionFailure("waitForExpectationsWithTimeout errored: \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testBindRef() {
|
||||
let mentat = Mentat()
|
||||
let (_, report) = self.populateWithTypesSchema(mentat: mentat)
|
||||
let stringEntid = mentat.entidForAttribute(attribute: ":foo/string")
|
||||
let bEntid = report!.entid(forTempId: "b")
|
||||
let query = "[:find ?e . :in ?ref :where [?e :foo/ref ?ref]]"
|
||||
let expect = expectation(description: "Query is executed")
|
||||
XCTAssertNoThrow(try mentat.query(query: query)
|
||||
.bind(varName: "?ref", toReference: stringEntid)
|
||||
.runScalar { value in
|
||||
XCTAssertNotNil(value)
|
||||
assert(value?.asEntid() == bEntid)
|
||||
expect.fulfill()
|
||||
})
|
||||
waitForExpectations(timeout: 1) { error in
|
||||
if let error = error {
|
||||
assertionFailure("waitForExpectationsWithTimeout errored: \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testBindKwRef() {
|
||||
let mentat = Mentat()
|
||||
let (_, report) = self.populateWithTypesSchema(mentat: mentat)
|
||||
let bEntid = report!.entid(forTempId: "b")
|
||||
let query = "[:find ?e . :in ?ref :where [?e :foo/ref ?ref]]"
|
||||
let expect = expectation(description: "Query is executed")
|
||||
XCTAssertNoThrow(try mentat.query(query: query)
|
||||
.bind(varName: "?ref", toReference: ":foo/string")
|
||||
.runScalar { value in
|
||||
XCTAssertNotNil(value)
|
||||
assert(value?.asEntid() == bEntid)
|
||||
expect.fulfill()
|
||||
})
|
||||
waitForExpectations(timeout: 1) { error in
|
||||
if let error = error {
|
||||
assertionFailure("waitForExpectationsWithTimeout errored: \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testBindKw() {
|
||||
let mentat = Mentat()
|
||||
let (_, report) = self.populateWithTypesSchema(mentat: mentat)
|
||||
let aEntid = report!.entid(forTempId: "a")
|
||||
let query = "[:find ?e . :in ?kw :where [?e :foo/keyword ?kw]]"
|
||||
let expect = expectation(description: "Query is executed")
|
||||
XCTAssertNoThrow(try mentat.query(query: query)
|
||||
.bind(varName: "?kw", toKeyword: ":foo/string")
|
||||
.runScalar { value in
|
||||
XCTAssertNotNil(value)
|
||||
assert(value?.asEntid() == aEntid)
|
||||
expect.fulfill()
|
||||
})
|
||||
waitForExpectations(timeout: 1) { error in
|
||||
if let error = error {
|
||||
assertionFailure("waitForExpectationsWithTimeout errored: \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testBindDate() {
|
||||
let mentat = Mentat()
|
||||
let (_, report) = self.populateWithTypesSchema(mentat: mentat)
|
||||
let aEntid = report!.entid(forTempId: "a")
|
||||
let query = "[:find [?e ?d] :in ?now :where [?e :foo/instant ?d] [(< ?d ?now)]]"
|
||||
let expect = expectation(description: "Query is executed")
|
||||
|
||||
let formatter = DateFormatter()
|
||||
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZZZZZ"
|
||||
let boundDate = formatter.date(from: "2018-04-16T16:39:18+00:00")!
|
||||
|
||||
XCTAssertNoThrow(try mentat.query(query: query)
|
||||
.bind(varName: "?now", toDate: boundDate)
|
||||
.runTuple { row in
|
||||
XCTAssertNotNil(row)
|
||||
assert(row?.asEntid(index: 0) == aEntid)
|
||||
expect.fulfill()
|
||||
})
|
||||
waitForExpectations(timeout: 1) { error in
|
||||
if let error = error {
|
||||
assertionFailure("waitForExpectationsWithTimeout errored: \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func testBindString() {
|
||||
let mentat = openAndInitializeCitiesStore()
|
||||
let query = "[:find ?n . :in ?name :where [(fulltext $ :community/name ?name) [[?e ?n]]]]"
|
||||
let expect = expectation(description: "Query is executed")
|
||||
XCTAssertNoThrow(try mentat.query(query: query)
|
||||
.bind(varName: "?name", toString: "Wallingford")
|
||||
.runScalar(callback: { scalarResult in
|
||||
guard let result = scalarResult?.asString() else {
|
||||
return assertionFailure("No String value received")
|
||||
}
|
||||
assert(result == "KOMO Communities - Wallingford")
|
||||
expect.fulfill()
|
||||
}))
|
||||
waitForExpectations(timeout: 1) { error in
|
||||
if let error = error {
|
||||
assertionFailure("waitForExpectationsWithTimeout errored: \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testBindUuid() {
|
||||
let mentat = Mentat()
|
||||
let (_, report) = self.populateWithTypesSchema(mentat: mentat)
|
||||
let aEntid = report!.entid(forTempId: "a")
|
||||
let query = "[:find ?e . :in ?uuid :where [?e :foo/uuid ?uuid]]"
|
||||
let uuid = UUID(uuidString: "550e8400-e29b-41d4-a716-446655440000")!
|
||||
let expect = expectation(description: "Query is rund")
|
||||
XCTAssertNoThrow(try mentat.query(query: query)
|
||||
.bind(varName: "?uuid", toUuid: uuid)
|
||||
.runScalar { value in
|
||||
XCTAssertNotNil(value)
|
||||
assert(value?.asEntid() == aEntid)
|
||||
expect.fulfill()
|
||||
})
|
||||
waitForExpectations(timeout: 1) { error in
|
||||
if let error = error {
|
||||
assertionFailure("waitForExpectationsWithTimeout errored: \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testBindBoolean() {
|
||||
let mentat = Mentat()
|
||||
let (_, report) = self.populateWithTypesSchema(mentat: mentat)
|
||||
let aEntid = report!.entid(forTempId: "a")
|
||||
let query = "[:find ?e . :in ?bool :where [?e :foo/boolean ?bool]]"
|
||||
let expect = expectation(description: "Query is executed")
|
||||
XCTAssertNoThrow(try mentat.query(query: query)
|
||||
.bind(varName: "?bool", toBoolean: true)
|
||||
.runScalar { value in
|
||||
XCTAssertNotNil(value)
|
||||
assert(value?.asEntid() == aEntid)
|
||||
expect.fulfill()
|
||||
})
|
||||
waitForExpectations(timeout: 1) { error in
|
||||
if let error = error {
|
||||
assertionFailure("waitForExpectationsWithTimeout errored: \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testBindDouble() {
|
||||
let mentat = Mentat()
|
||||
let (_, report) = self.populateWithTypesSchema(mentat: mentat)
|
||||
let aEntid = report!.entid(forTempId: "a")
|
||||
let query = "[:find ?e . :in ?double :where [?e :foo/double ?double]]"
|
||||
let expect = expectation(description: "Query is executed")
|
||||
XCTAssertNoThrow(try mentat.query(query: query)
|
||||
.bind(varName: "?double", toDouble: 11.23)
|
||||
.runScalar { value in
|
||||
XCTAssertNotNil(value)
|
||||
assert(value?.asEntid() == aEntid)
|
||||
expect.fulfill()
|
||||
})
|
||||
waitForExpectations(timeout: 1) { error in
|
||||
if let error = error {
|
||||
assertionFailure("waitForExpectationsWithTimeout errored: \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testTypedValueAsLong() {
|
||||
let mentat = Mentat()
|
||||
let (_, report) = self.populateWithTypesSchema(mentat: mentat)
|
||||
let aEntid = report!.entid(forTempId: "a")!
|
||||
let query = "[:find ?v . :in ?e :where [?e :foo/long ?v]]"
|
||||
let expect = expectation(description: "Query is executed")
|
||||
XCTAssertNoThrow(try mentat.query(query: query)
|
||||
.bind(varName: "?e", toReference: aEntid)
|
||||
.runScalar { value in
|
||||
XCTAssertNotNil(value)
|
||||
assert(value?.asLong() == 25)
|
||||
assert(value?.asLong() == 25)
|
||||
expect.fulfill()
|
||||
})
|
||||
waitForExpectations(timeout: 1) { error in
|
||||
if let error = error {
|
||||
assertionFailure("waitForExpectationsWithTimeout errored: \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testTypedValueAsRef() {
|
||||
let mentat = Mentat()
|
||||
let (_, report) = self.populateWithTypesSchema(mentat: mentat)
|
||||
let aEntid = report!.entid(forTempId: "a")!
|
||||
let query = "[:find ?e . :where [?e :foo/long 25]]"
|
||||
let expect = expectation(description: "Query is executed")
|
||||
XCTAssertNoThrow(try mentat.query(query: query)
|
||||
.runScalar { value in
|
||||
XCTAssertNotNil(value)
|
||||
assert(value?.asEntid() == aEntid)
|
||||
assert(value?.asEntid() == aEntid)
|
||||
expect.fulfill()
|
||||
})
|
||||
waitForExpectations(timeout: 1) { error in
|
||||
if let error = error {
|
||||
assertionFailure("waitForExpectationsWithTimeout errored: \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testTypedValueAsKw() {
|
||||
let mentat = Mentat()
|
||||
let (_, report) = self.populateWithTypesSchema(mentat: mentat)
|
||||
let aEntid = report!.entid(forTempId: "a")!
|
||||
let query = "[:find ?v . :in ?e :where [?e :foo/keyword ?v]]"
|
||||
let expect = expectation(description: "Query is executed")
|
||||
XCTAssertNoThrow(try mentat.query(query: query)
|
||||
.bind(varName: "?e", toReference: aEntid)
|
||||
.runScalar { value in
|
||||
XCTAssertNotNil(value)
|
||||
assert(value?.asKeyword() == ":foo/string")
|
||||
assert(value?.asKeyword() == ":foo/string")
|
||||
expect.fulfill()
|
||||
})
|
||||
waitForExpectations(timeout: 1) { error in
|
||||
if let error = error {
|
||||
assertionFailure("waitForExpectationsWithTimeout errored: \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testTypedValueAsBoolean() {
|
||||
let mentat = Mentat()
|
||||
let (_, report) = self.populateWithTypesSchema(mentat: mentat)
|
||||
let aEntid = report!.entid(forTempId: "a")!
|
||||
let query = "[:find ?v . :in ?e :where [?e :foo/boolean ?v]]"
|
||||
let expect = expectation(description: "Query is executed")
|
||||
XCTAssertNoThrow(try mentat.query(query: query)
|
||||
.bind(varName: "?e", toReference: aEntid)
|
||||
.runScalar { value in
|
||||
XCTAssertNotNil(value)
|
||||
assert(value?.asBool() == true)
|
||||
assert(value?.asBool() == true)
|
||||
expect.fulfill()
|
||||
})
|
||||
waitForExpectations(timeout: 1) { error in
|
||||
if let error = error {
|
||||
assertionFailure("waitForExpectationsWithTimeout errored: \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testTypedValueAsDouble() {
|
||||
let mentat = Mentat()
|
||||
let (_, report) = self.populateWithTypesSchema(mentat: mentat)
|
||||
let aEntid = report!.entid(forTempId: "a")!
|
||||
let query = "[:find ?v . :in ?e :where [?e :foo/double ?v]]"
|
||||
let expect = expectation(description: "Query is executed")
|
||||
XCTAssertNoThrow(try mentat.query(query: query)
|
||||
.bind(varName: "?e", toReference: aEntid)
|
||||
.runScalar { value in
|
||||
XCTAssertNotNil(value)
|
||||
assert(value?.asDouble() == 11.23)
|
||||
assert(value?.asDouble() == 11.23)
|
||||
expect.fulfill()
|
||||
})
|
||||
waitForExpectations(timeout: 1) { error in
|
||||
if let error = error {
|
||||
assertionFailure("waitForExpectationsWithTimeout errored: \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testTypedValueAsDate() {
|
||||
let mentat = Mentat()
|
||||
let (_, report) = self.populateWithTypesSchema(mentat: mentat)
|
||||
let aEntid = report!.entid(forTempId: "a")!
|
||||
let query = "[:find ?v . :in ?e :where [?e :foo/instant ?v]]"
|
||||
let expect = expectation(description: "Query is executed")
|
||||
|
||||
let formatter = DateFormatter()
|
||||
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZZZZZ"
|
||||
let expectedDate = formatter.date(from: "2017-01-01T11:00:00+00:00")
|
||||
|
||||
XCTAssertNoThrow(try mentat.query(query: query)
|
||||
.bind(varName: "?e", toReference: aEntid)
|
||||
.runScalar { value in
|
||||
XCTAssertNotNil(value)
|
||||
assert(value?.asDate() == expectedDate)
|
||||
assert(value?.asDate() == expectedDate)
|
||||
expect.fulfill()
|
||||
})
|
||||
waitForExpectations(timeout: 1) { error in
|
||||
if let error = error {
|
||||
assertionFailure("waitForExpectationsWithTimeout errored: \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testTypedValueAsString() {
|
||||
let mentat = Mentat()
|
||||
let (_, report) = self.populateWithTypesSchema(mentat: mentat)
|
||||
let aEntid = report!.entid(forTempId: "a")!
|
||||
let query = "[:find ?v . :in ?e :where [?e :foo/string ?v]]"
|
||||
let expect = expectation(description: "Query is executed")
|
||||
XCTAssertNoThrow(try mentat.query(query: query)
|
||||
.bind(varName: "?e", toReference: aEntid)
|
||||
.runScalar { value in
|
||||
XCTAssertNotNil(value)
|
||||
assert(value?.asString() == "The higher we soar the smaller we appear to those who cannot fly.")
|
||||
assert(value?.asString() == "The higher we soar the smaller we appear to those who cannot fly.")
|
||||
expect.fulfill()
|
||||
})
|
||||
waitForExpectations(timeout: 1) { error in
|
||||
if let error = error {
|
||||
assertionFailure("waitForExpectationsWithTimeout errored: \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testTypedValueAsUuid() {
|
||||
let mentat = Mentat()
|
||||
let (_, report) = self.populateWithTypesSchema(mentat: mentat)
|
||||
let aEntid = report!.entid(forTempId: "a")!
|
||||
let query = "[:find ?v . :in ?e :where [?e :foo/uuid ?v]]"
|
||||
let expectedUuid = UUID(uuidString: "550e8400-e29b-41d4-a716-446655440000")!
|
||||
let expect = expectation(description: "Query is executed")
|
||||
XCTAssertNoThrow(try mentat.query(query: query)
|
||||
.bind(varName: "?e", toReference: aEntid)
|
||||
.runScalar { value in
|
||||
XCTAssertNotNil(value)
|
||||
assert(value?.asUUID() == expectedUuid)
|
||||
assert(value?.asUUID() == expectedUuid)
|
||||
expect.fulfill()
|
||||
})
|
||||
waitForExpectations(timeout: 1) { error in
|
||||
if let error = error {
|
||||
assertionFailure("waitForExpectationsWithTimeout errored: \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testValueForAttributeOfEntity() {
|
||||
let mentat = Mentat()
|
||||
let (_, report) = self.populateWithTypesSchema(mentat: mentat)
|
||||
let aEntid = report!.entid(forTempId: "a")!
|
||||
var value: TypedValue? = nil;
|
||||
XCTAssertNoThrow(value = try mentat.value(forAttribute: ":foo/long", ofEntity: aEntid))
|
||||
XCTAssertNotNil(value)
|
||||
assert(value?.asLong() == 25)
|
||||
}
|
||||
|
||||
func testEntidForAttribute() {
|
||||
let mentat = Mentat()
|
||||
let _ = self.populateWithTypesSchema(mentat: mentat)
|
||||
let entid = mentat.entidForAttribute(attribute: ":foo/long")
|
||||
assert(entid == 65540)
|
||||
}
|
||||
|
||||
func testMultipleQueries() {
|
||||
let mentat = Mentat()
|
||||
let _ = self.populateWithTypesSchema(mentat: mentat)
|
||||
let q1 = mentat.query(query: "[:find ?x :where [?x _ _]]")
|
||||
|
||||
let q1Expect = expectation(description: "Query 1 is executed")
|
||||
XCTAssertNoThrow(try q1.run { results in
|
||||
XCTAssertNotNil(results)
|
||||
q1Expect.fulfill()
|
||||
})
|
||||
|
||||
let q2 = mentat.query(query: "[:find ?x :where [_ _ ?x]]")
|
||||
let q2Expect = expectation(description: "Query 2 is executed")
|
||||
XCTAssertNoThrow(try q2.run { results in
|
||||
XCTAssertNotNil(results)
|
||||
q2Expect.fulfill()
|
||||
})
|
||||
|
||||
waitForExpectations(timeout: 1) { error in
|
||||
if let error = error {
|
||||
assertionFailure("waitForExpectationsWithTimeout errored: \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testNestedQueries() {
|
||||
let mentat = Mentat()
|
||||
let _ = self.populateWithTypesSchema(mentat: mentat)
|
||||
let q1 = mentat.query(query: "[:find ?x :where [?x _ _]]")
|
||||
let q2 = mentat.query(query: "[:find ?x :where [_ _ ?x]]")
|
||||
|
||||
let expect = expectation(description: "Query 1 is executed")
|
||||
XCTAssertNoThrow(try q1.run { results in
|
||||
XCTAssertNotNil(results)
|
||||
try? q2.run { results in
|
||||
XCTAssertNotNil(results)
|
||||
expect.fulfill()
|
||||
}
|
||||
})
|
||||
|
||||
waitForExpectations(timeout: 1) { error in
|
||||
if let error = error {
|
||||
assertionFailure("waitForExpectationsWithTimeout errored: \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// TODO: Add tests for transaction observation
|
||||
}
|
17
src/conn.rs
17
src/conn.rs
|
@ -94,7 +94,6 @@ use mentat_tolstoy::Syncer;
|
|||
use uuid::Uuid;
|
||||
|
||||
use entity_builder::{
|
||||
BuildTerms,
|
||||
InProgressBuilder,
|
||||
TermBuilder,
|
||||
};
|
||||
|
@ -647,10 +646,6 @@ impl Store {
|
|||
pub fn unregister_observer(&mut self, key: &String) {
|
||||
self.conn.unregister_observer(key);
|
||||
}
|
||||
|
||||
pub fn assert_datom<T>(&mut self, entid: T, attribute: Keyword, value: TypedValue) -> Result<()> where T: Into<KnownEntid> {
|
||||
self.conn.assert_datom(&mut self.sqlite, entid, attribute, value)
|
||||
}
|
||||
}
|
||||
|
||||
impl Queryable for Store {
|
||||
|
@ -973,18 +968,6 @@ impl Conn {
|
|||
pub fn unregister_observer(&mut self, key: &String) {
|
||||
self.tx_observer_service.lock().unwrap().deregister(key);
|
||||
}
|
||||
|
||||
// TODO: expose the entity builder over FFI and remove the need for this function entirely
|
||||
// It's really only here in order to keep the FFI layer as thin as possible.
|
||||
// Once the entity builder is exposed, we can perform all of these functions over FFI from the client.
|
||||
pub fn assert_datom<T>(&mut self, sqlite: &mut rusqlite::Connection, entid: T, attribute: Keyword, value: TypedValue) -> Result<()> where T: Into<KnownEntid> {
|
||||
let in_progress = self.begin_transaction(sqlite)?;
|
||||
let mut builder = in_progress.builder().describe(entid.into());
|
||||
builder.add_kw(&attribute, value)?;
|
||||
builder.commit()
|
||||
.map_err(|e| e.into())
|
||||
.and(Ok(()))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
@ -55,6 +55,7 @@ pub use mentat_query::{
|
|||
pub use mentat_db::{
|
||||
CORE_SCHEMA_VERSION,
|
||||
DB_SCHEMA_CORE,
|
||||
AttributeSet,
|
||||
TxObserver,
|
||||
TxReport,
|
||||
new_connection,
|
||||
|
|
|
@ -14,10 +14,12 @@ use std::collections::{
|
|||
};
|
||||
|
||||
use mentat_core::{
|
||||
DateTime,
|
||||
Entid,
|
||||
Keyword,
|
||||
Binding,
|
||||
TypedValue,
|
||||
Utc,
|
||||
ValueType,
|
||||
};
|
||||
|
||||
|
@ -37,15 +39,15 @@ use errors::{
|
|||
};
|
||||
|
||||
pub struct QueryBuilder<'a> {
|
||||
sql: String,
|
||||
query: String,
|
||||
values: BTreeMap<Variable, TypedValue>,
|
||||
types: BTreeMap<Variable, ValueType>,
|
||||
store: &'a mut Store,
|
||||
}
|
||||
|
||||
impl<'a> QueryBuilder<'a> {
|
||||
pub fn new<T>(store: &'a mut Store, sql: T) -> QueryBuilder where T: Into<String> {
|
||||
QueryBuilder { sql: sql.into(), values: BTreeMap::new(), types: BTreeMap::new(), store }
|
||||
pub fn new<T>(store: &'a mut Store, query: T) -> QueryBuilder where T: Into<String> {
|
||||
QueryBuilder { query: query.into(), values: BTreeMap::new(), types: BTreeMap::new(), store }
|
||||
}
|
||||
|
||||
pub fn bind_value<T>(&mut self, var: &str, value: T) -> &mut Self where T: Into<TypedValue> {
|
||||
|
@ -71,6 +73,12 @@ impl<'a> QueryBuilder<'a> {
|
|||
|
||||
pub fn bind_instant(&mut self, var: &str, value: i64) -> &mut Self {
|
||||
self.values.insert(Variable::from_valid_name(var), TypedValue::instant(value));
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
pub fn bind_date_time(&mut self, var: &str, value: DateTime<Utc>) -> &mut Self {
|
||||
self.values.insert(Variable::from_valid_name(var), TypedValue::Instant(value));
|
||||
self
|
||||
}
|
||||
|
||||
|
@ -84,7 +92,7 @@ impl<'a> QueryBuilder<'a> {
|
|||
let types = ::std::mem::replace(&mut self.types, Default::default());
|
||||
let query_inputs = QueryInputs::new(types, values)?;
|
||||
let read = self.store.begin_read()?;
|
||||
read.q_once(&self.sql, query_inputs)
|
||||
read.q_once(&self.query, query_inputs)
|
||||
}
|
||||
|
||||
pub fn execute_scalar(&mut self) -> Result<Option<Binding>> {
|
||||
|
@ -116,6 +124,11 @@ mod test {
|
|||
Store,
|
||||
};
|
||||
|
||||
use mentat_core::{
|
||||
DateTime,
|
||||
Utc,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn test_scalar_query() {
|
||||
let mut store = Store::open("").expect("store connection");
|
||||
|
|
Loading…
Reference in a new issue