Skip to content

Commit

Permalink
feat: init workspace & delete history (#548)
Browse files Browse the repository at this point in the history
* feat: add binding for init workspace

* feat: add range diff & covered check

* feat: add delete history identify

* chore: bump jni version

* fix: clippy

* feat: add export workspace api
  • Loading branch information
darkskygit authored Oct 17, 2023
1 parent 614eca0 commit a5629df
Show file tree
Hide file tree
Showing 19 changed files with 458 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,25 @@ import java.io.File
import java.util.*
import com.toeverything.jwst.Workspace
import kotlin.jvm.optionals.getOrNull
import kotlin.random.Random

fun <T> Optional<T>.unwrap(): T? = orElse(null)

fun String.hexStringToByteArray(): ByteArray {
return this.chunked(2)
.map { it.toInt(16).toByte() }
.toByteArray()
}

fun getStaticWorkspace(): String {
return "010895E2C0E01D0027010A73706163653A6D6574610570616765730027010C73706163653A626C6F636B73047465737401280095E2C0E01D010B7379733A666C61766F757201770474657374270095E2C0E01D010C7379733A6368696C6472656E00280095E2C0E01D010B7379733A63726561746564017B4278B38B757B900028010D73706163653A757064617465640474657374017B4278B38B757B9000280095E2C0E01D010970726F703A74657374017703616263A895E2C0E01D05017B4278B38B757B90000195E2C0E01D010501"
}

fun getRandomId(): String {
val chars = "abcdefghijklmnopqrstuvwxyz0123456789"
return (1..8).map { chars[Random.nextInt(chars.length)] }.joinToString("")
}

class MainActivity : AppCompatActivity() {

@RequiresApi(Build.VERSION_CODES.TIRAMISU)
Expand All @@ -24,6 +40,10 @@ class MainActivity : AppCompatActivity() {
val database = File(filesDir, "jwst.db")
val storage = Storage(database.absolutePath, "ws://10.0.2.2:3000/collaboration", "debug")

storage.initWorkspace(getRandomId(), getStaticWorkspace().hexStringToByteArray())
val text = storage.getWorkspace("test1").get().get("test").get().get("test").get()
Log.i("jwst", "text: $text")

storage.getWorkspace("test").unwrap()?.let { workspace ->
setupWorkspace(workspace)

Expand Down Expand Up @@ -60,7 +80,6 @@ class MainActivity : AppCompatActivity() {
Log.i("jwst", searchResult3)

while (true) {

Log.i("jwst", " getting root")
workspace.get("root").unwrap()?.let { block ->
block.get("test").ifPresent { value ->
Expand Down
4 changes: 2 additions & 2 deletions apps/android/build.gradle
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
id 'com.android.application' version '8.1.0' apply false
id 'com.android.library' version '8.1.0' apply false
id 'com.android.application' version '8.1.2' apply false
id 'com.android.library' version '8.1.2' apply false
id 'org.jetbrains.kotlin.android' version '1.8.10' apply false
}
18 changes: 12 additions & 6 deletions apps/swift/OctoBaseSwift/RustXcframework.xcframework/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,24 @@
<key>AvailableLibraries</key>
<array>
<dict>
<key>BinaryPath</key>
<string>liboctobase.a</string>
<key>HeadersPath</key>
<string>Headers</string>
<key>LibraryIdentifier</key>
<string>ios-arm64-simulator</string>
<string>macos-arm64</string>
<key>LibraryPath</key>
<string>liboctobase.a</string>
<key>SupportedArchitectures</key>
<array>
<string>arm64</string>
</array>
<key>SupportedPlatform</key>
<string>ios</string>
<key>SupportedPlatformVariant</key>
<string>simulator</string>
<string>macos</string>
</dict>
<dict>
<key>BinaryPath</key>
<string>liboctobase.a</string>
<key>HeadersPath</key>
<string>Headers</string>
<key>LibraryIdentifier</key>
Expand All @@ -35,18 +37,22 @@
<string>ios</string>
</dict>
<dict>
<key>BinaryPath</key>
<string>liboctobase.a</string>
<key>HeadersPath</key>
<string>Headers</string>
<key>LibraryIdentifier</key>
<string>macos-arm64</string>
<string>ios-arm64-simulator</string>
<key>LibraryPath</key>
<string>liboctobase.a</string>
<key>SupportedArchitectures</key>
<array>
<string>arm64</string>
</array>
<key>SupportedPlatform</key>
<string>macos</string>
<string>ios</string>
<key>SupportedPlatformVariant</key>
<string>simulator</string>
</dict>
</array>
<key>CFBundlePackageType</key>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,8 @@ bool __swift_bridge__$Storage$is_connected(void* self);
bool __swift_bridge__$Storage$is_finished(void* self);
bool __swift_bridge__$Storage$is_error(void* self);
void* __swift_bridge__$Storage$get_sync_state(void* self);
bool __swift_bridge__$Storage$init(void* self, void* workspace_id, void* data);
void* __swift_bridge__$Storage$export(void* self, void* workspace_id);
void* __swift_bridge__$Storage$connect(void* self, void* workspace_id, void* remote);
void* __swift_bridge__$Storage$get_last_synced(void* self);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,8 @@ bool __swift_bridge__$Storage$is_connected(void* self);
bool __swift_bridge__$Storage$is_finished(void* self);
bool __swift_bridge__$Storage$is_error(void* self);
void* __swift_bridge__$Storage$get_sync_state(void* self);
bool __swift_bridge__$Storage$init(void* self, void* workspace_id, void* data);
void* __swift_bridge__$Storage$export(void* self, void* workspace_id);
void* __swift_bridge__$Storage$connect(void* self, void* workspace_id, void* remote);
void* __swift_bridge__$Storage$get_last_synced(void* self);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,8 @@ bool __swift_bridge__$Storage$is_connected(void* self);
bool __swift_bridge__$Storage$is_finished(void* self);
bool __swift_bridge__$Storage$is_error(void* self);
void* __swift_bridge__$Storage$get_sync_state(void* self);
bool __swift_bridge__$Storage$init(void* self, void* workspace_id, void* data);
void* __swift_bridge__$Storage$export(void* self, void* workspace_id);
void* __swift_bridge__$Storage$connect(void* self, void* workspace_id, void* remote);
void* __swift_bridge__$Storage$get_last_synced(void* self);

Expand Down
8 changes: 8 additions & 0 deletions apps/swift/OctoBaseSwift/Sources/OctoBase/jwst-swift.swift
Original file line number Diff line number Diff line change
Expand Up @@ -578,6 +578,14 @@ public class StorageRefMut: StorageRef {
}
}
extension StorageRefMut {
public func init<GenericIntoRustString: IntoRustString>(_ workspace_id: GenericIntoRustString, _ data: RustVec<UInt8>) -> Bool {
__swift_bridge__$Storage$init(ptr, { let rustString = workspace_id.intoRustString(); rustString.isOwned = false; return rustString.ptr }(), { let val = data; val.isOwned = false; return val.ptr }())
}

public func export<GenericIntoRustString: IntoRustString>(_ workspace_id: GenericIntoRustString) -> Optional<RustVec<UInt8>> {
{ let val = __swift_bridge__$Storage$export(ptr, { let rustString = workspace_id.intoRustString(); rustString.isOwned = false; return rustString.ptr }()); if val != nil { return RustVec(ptr: val!) } else { return nil } }()
}

public func connect<GenericIntoRustString: IntoRustString>(_ workspace_id: GenericIntoRustString, _ remote: GenericIntoRustString) -> Optional<Workspace> {
{ let val = __swift_bridge__$Storage$connect(ptr, { let rustString = workspace_id.intoRustString(); rustString.isOwned = false; return rustString.ptr }(), { let rustString = remote.intoRustString(); rustString.isOwned = false; return rustString.ptr }()); if val != nil { return Workspace(ptr: val!) } else { return nil } }()
}
Expand Down
2 changes: 1 addition & 1 deletion libs/jwst-binding/jwst-jni/android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ publishing {
release(MavenPublication) {
groupId = 'com.toeverything'
artifactId = 'octobase'
version = '0.2.6'
version = '0.2.7'

afterEvaluate {
from components.release
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,26 @@ class Storage constructor(path: String, private val remote: String = "", private

val error get() = this.storage.error()

fun initWorkspace(id: String, data: ByteArray): Result<Unit> {
val success = this.storage.init(id,data)
return if (success) {
Result.success(Unit)
} else {
val error = this.storage.error().orElse("Unknown error")
Result.failure(Exception(error))
}
}

fun exportWorkspace(id: String): Result<ByteArray> {
val data = this.storage.export(id)
return if (data.isNotEmpty()) {
Result.success(data)
} else {
val error = this.storage.error().orElse("Unknown error")
Result.failure(Exception(error))
}
}

fun getWorkspace(id: String): Optional<Workspace> {
return this.storage.connect(id, this.remote.takeIf { it.isNotEmpty() }?.let { "$it/$id" } ?: "").map { Workspace(it) }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,20 @@ public final boolean is_error() {
}
private static native @NonNull String do_get_sync_state(long self);

public final boolean init(@NonNull String workspace_id, @NonNull byte [] data) {
boolean ret = do_init(mNativeObj, workspace_id, data);

return ret;
}
private static native boolean do_init(long self, @NonNull String workspace_id, byte [] data);

public final byte [] export(@NonNull String workspace_id) {
byte [] ret = do_export(mNativeObj, workspace_id);

return ret;
}
private static native byte [] do_export(long self, @NonNull String workspace_id);

public final @NonNull java.util.Optional<Workspace> connect(@NonNull String workspace_id, @NonNull String remote) {
long ret = do_connect(mNativeObj, workspace_id, remote);
java.util.Optional<Workspace> convRet;
Expand Down
31 changes: 30 additions & 1 deletion libs/jwst-binding/jwst-jni/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,32 @@ fn main() {
.chain(["use jni_sys::*;"].iter())
.chain(
[
r#"foreign_typemap!(
($p:r_type) Vec<u8> => jbyteArray {
let slice = &($p)[..];
let slice = unsafe { std::mem::transmute::<&[u8], &[i8]>(slice) };
let raw = JavaByteArray::from_slice_to_raw(slice, env);
$out = raw;
};
($p:f_type) => "jbyteArray";
);
foreign_typemap!(
($p:r_type) Vec<u8> => jbyteArray {
let slice = &($p)[..];
let slice = unsafe { std::mem::transmute::<&[u8], &[i8]>(slice) };
let raw = JavaByteArray::from_slice_to_raw(slice, env);
$out = raw;
};
($p:f_type) => "jbyteArray";
($p:r_type) &'a [u8] <= jbyteArray {
let arr = JavaByteArray::new(env, $p);
let slice = arr.to_slice();
let slice = unsafe { std::mem::transmute::<&[i8], &[u8]>(slice) };
$out = slice;
};
($p:f_type) <= "jbyteArray";
);"#,
r#"foreign_class!(
class JwstStorage {
self_type JwstStorage;
Expand All @@ -30,6 +56,8 @@ fn main() {
fn JwstStorage::is_finished(&self) -> bool;
fn JwstStorage::is_error(&self) -> bool;
fn JwstStorage::get_sync_state(&self) -> String;
fn JwstStorage::init(&mut self, workspace_id: String, data: &[u8]) -> bool; alias init;
fn JwstStorage::export(&mut self, workspace_id: String) -> Vec<u8>; alias export;
fn JwstStorage::connect(&mut self, workspace_id: String, remote: String) -> Option<Workspace>; alias connect;
fn JwstStorage::get_last_synced(&self) ->Vec<i64>;
}
Expand Down Expand Up @@ -82,7 +110,7 @@ foreign_class!(
.join("\n");
fs::write(&in_temp, &template).unwrap();

let template_changed = fs::read_to_string(in_src).unwrap() != template;
let template_changed = fs::read_to_string(&in_src).unwrap() != template;

if template_changed || !in_temp.with_extension("").exists() || !jni_dir.exists() {
// delete the lib folder then create it again to prevent obsolete files
Expand All @@ -104,6 +132,7 @@ foreign_class!(
!= fs::read_to_string(in_temp.with_extension("")).unwrap()
{
fs::copy(in_temp.with_extension("out"), in_temp.with_extension("")).unwrap();
fs::copy(in_temp.with_extension("out"), in_src).unwrap();
}
}
}
28 changes: 28 additions & 0 deletions libs/jwst-binding/jwst-jni/src/java_glue.rs.in
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,32 @@
use crate::*;

use jni_sys::*;
foreign_typemap!(
($p:r_type) Vec<u8> => jbyteArray {
let slice = &($p)[..];
let slice = unsafe { std::mem::transmute::<&[u8], &[i8]>(slice) };
let raw = JavaByteArray::from_slice_to_raw(slice, env);
$out = raw;
};
($p:f_type) => "jbyteArray";
);

foreign_typemap!(
($p:r_type) Vec<u8> => jbyteArray {
let slice = &($p)[..];
let slice = unsafe { std::mem::transmute::<&[u8], &[i8]>(slice) };
let raw = JavaByteArray::from_slice_to_raw(slice, env);
$out = raw;
};
($p:f_type) => "jbyteArray";
($p:r_type) &'a [u8] <= jbyteArray {
let arr = JavaByteArray::new(env, $p);
let slice = arr.to_slice();
let slice = unsafe { std::mem::transmute::<&[i8], &[u8]>(slice) };
$out = slice;
};
($p:f_type) <= "jbyteArray";
);
foreign_class!(
class JwstStorage {
self_type JwstStorage;
Expand All @@ -13,6 +39,8 @@ foreign_class!(
fn JwstStorage::is_finished(&self) -> bool;
fn JwstStorage::is_error(&self) -> bool;
fn JwstStorage::get_sync_state(&self) -> String;
fn JwstStorage::init(&mut self, workspace_id: String, data: &[u8]) -> bool; alias init;
fn JwstStorage::export(&mut self, workspace_id: String) -> Vec<u8>; alias export;
fn JwstStorage::connect(&mut self, workspace_id: String, remote: String) -> Option<Workspace>; alias connect;
fn JwstStorage::get_last_synced(&self) ->Vec<i64>;
}
Expand Down
48 changes: 48 additions & 0 deletions libs/jwst-binding/jwst-jni/src/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,54 @@ impl JwstStorage {
}
}

pub fn init(&mut self, workspace_id: String, data: &[u8]) -> bool {
match self.init_workspace(workspace_id, data) {
Ok(_) => true,
Err(e) => {
let error = format!("Failed to init workspace: {:?}", e);
error!("{}", error);
self.error = Some(error);
false
}
}
}

fn init_workspace(&self, workspace_id: String, data: &[u8]) -> JwstStorageResult {
let rt = Arc::new(
Builder::new_multi_thread()
.worker_threads(1)
.enable_all()
.thread_name("jwst-jni-init")
.build()
.map_err(JwstStorageError::SyncThread)?,
);
rt.block_on(self.storage.init_workspace(workspace_id, data.to_vec()))
}

pub fn export(&mut self, workspace_id: String) -> Vec<u8> {
match self.export_workspace(workspace_id) {
Ok(data) => data,
Err(e) => {
let error = format!("Failed to export workspace: {:?}", e);
error!("{}", error);
self.error = Some(error);
vec![]
}
}
}

fn export_workspace(&self, workspace_id: String) -> JwstStorageResult<Vec<u8>> {
let rt = Arc::new(
Builder::new_multi_thread()
.worker_threads(1)
.enable_all()
.thread_name("jwst-jni-export")
.build()
.map_err(JwstStorageError::SyncThread)?,
);
rt.block_on(self.storage.export_workspace(workspace_id))
}

pub fn connect(&mut self, workspace_id: String, remote: String) -> Option<Workspace> {
match self.sync(workspace_id, remote) {
Ok(workspace) => Some(workspace),
Expand Down
4 changes: 4 additions & 0 deletions libs/jwst-binding/jwst-swift/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,10 @@ mod ffi {

fn get_sync_state(self: &Storage) -> String;

fn init(self: &mut Storage, workspace_id: String, data: Vec<u8>) -> bool;

fn export(self: &mut Storage, workspace_id: String) -> Option<Vec<u8>>;

fn connect(self: &mut Storage, workspace_id: String, remote: String) -> Option<Workspace>;

fn get_last_synced(self: &Storage) -> Vec<i64>;
Expand Down
Loading

1 comment on commit a5629df

@vercel
Copy link

@vercel vercel bot commented on a5629df Oct 17, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.