add tracing & test post data to dart

This commit is contained in:
appflowy 2021-07-03 14:14:10 +08:00
parent 1cfcdab124
commit 77aa18d737
34 changed files with 366 additions and 270 deletions

3
.gitignore vendored
View File

@ -8,6 +8,3 @@ Cargo.lock
# These are backup files generated by rustfmt # These are backup files generated by rustfmt
**/*.rs.bk **/*.rs.bk
/rust-lib/flowy-derive

View File

@ -524,14 +524,6 @@
</list> </list>
</value> </value>
</entry> </entry>
<entry key="protobuf">
<value>
<list>
<option value="$PROJECT_DIR$/../../flutter/.pub-cache/hosted/pub.dartlang.org/protobuf-2.0.0/lib" />
<option value="$PROJECT_DIR$/../../flutter/.pub-cache/hosted/pub.dartlang.org/protobuf-1.1.0/lib" />
</list>
</value>
</entry>
<entry key="provider"> <entry key="provider">
<value> <value>
<list> <list>
@ -841,8 +833,6 @@
<root url="file://$PROJECT_DIR$/../../flutter/.pub-cache/hosted/pub.dartlang.org/plugin_platform_interface-2.0.0/lib" /> <root url="file://$PROJECT_DIR$/../../flutter/.pub-cache/hosted/pub.dartlang.org/plugin_platform_interface-2.0.0/lib" />
<root url="file://$PROJECT_DIR$/../../flutter/.pub-cache/hosted/pub.dartlang.org/pool-1.5.0/lib" /> <root url="file://$PROJECT_DIR$/../../flutter/.pub-cache/hosted/pub.dartlang.org/pool-1.5.0/lib" />
<root url="file://$PROJECT_DIR$/../../flutter/.pub-cache/hosted/pub.dartlang.org/process-4.2.1/lib" /> <root url="file://$PROJECT_DIR$/../../flutter/.pub-cache/hosted/pub.dartlang.org/process-4.2.1/lib" />
<root url="file://$PROJECT_DIR$/../../flutter/.pub-cache/hosted/pub.dartlang.org/protobuf-1.1.0/lib" />
<root url="file://$PROJECT_DIR$/../../flutter/.pub-cache/hosted/pub.dartlang.org/protobuf-2.0.0/lib" />
<root url="file://$PROJECT_DIR$/../../flutter/.pub-cache/hosted/pub.dartlang.org/provider-5.0.0/lib" /> <root url="file://$PROJECT_DIR$/../../flutter/.pub-cache/hosted/pub.dartlang.org/provider-5.0.0/lib" />
<root url="file://$PROJECT_DIR$/../../flutter/.pub-cache/hosted/pub.dartlang.org/pub_semver-2.0.0/lib" /> <root url="file://$PROJECT_DIR$/../../flutter/.pub-cache/hosted/pub.dartlang.org/pub_semver-2.0.0/lib" />
<root url="file://$PROJECT_DIR$/../../flutter/.pub-cache/hosted/pub.dartlang.org/pubspec_parse-1.0.0/lib" /> <root url="file://$PROJECT_DIR$/../../flutter/.pub-cache/hosted/pub.dartlang.org/pubspec_parse-1.0.0/lib" />

View File

@ -9,6 +9,7 @@
"request": "launch", "request": "launch",
"program": "${workspaceRoot}/lib/main.dart", "program": "${workspaceRoot}/lib/main.dart",
"type": "dart", "type": "dart",
// "preLaunchTask": "build rust sdk"
}, },
{ {
"name": "app_flowy (profile mode)", "name": "app_flowy (profile mode)",

View File

@ -25,7 +25,7 @@ class App {
resolveDependencies(env); resolveDependencies(env);
// add task // add task
// getIt<AppLauncher>().addTask(RustSDKInitTask()); getIt<AppLauncher>().addTask(RustSDKInitTask());
getIt<AppLauncher>().addTask(AppWidgetTask()); getIt<AppLauncher>().addTask(AppWidgetTask());
// execute the tasks // execute the tasks

View File

@ -92,13 +92,6 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "6.1.1" version: "6.1.1"
fixnum:
dependency: transitive
description:
name: fixnum
url: "https://pub.dartlang.org"
source: hosted
version: "0.10.11"
flowy_logger: flowy_logger:
dependency: transitive dependency: transitive
description: description:
@ -106,13 +99,6 @@ packages:
relative: true relative: true
source: path source: path
version: "0.0.1" version: "0.0.1"
flowy_protobuf:
dependency: transitive
description:
path: "../../flowy_protobuf"
relative: true
source: path
version: "0.0.1"
flowy_sdk: flowy_sdk:
dependency: "direct main" dependency: "direct main"
description: description:
@ -215,13 +201,6 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "4.2.1" version: "4.2.1"
protobuf:
dependency: transitive
description:
name: protobuf
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0"
sky_engine: sky_engine:
dependency: transitive dependency: transitive
description: flutter description: flutter

View File

@ -1,3 +1,4 @@
import 'dart:convert';
import 'dart:ffi'; import 'dart:ffi';
// ignore: import_of_legacy_library_into_null_safe // ignore: import_of_legacy_library_into_null_safe
import 'package:isolates/isolates.dart'; import 'package:isolates/isolates.dart';
@ -5,7 +6,7 @@ import 'package:isolates/isolates.dart';
import 'package:isolates/ports.dart'; import 'package:isolates/ports.dart';
import 'package:ffi/ffi.dart'; import 'package:ffi/ffi.dart';
import 'package:flowy_protobuf/model/grpc.pb.dart'; // ignore: unused_import
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'dart:async'; import 'dart:async';
import 'dart:typed_data'; import 'dart:typed_data';
@ -22,9 +23,25 @@ class FFIAdaptorException implements Exception {
FFIAdaptorException(this.type); FFIAdaptorException(this.type);
} }
class FFICommand {
final String event;
final Uint8List payload;
FFICommand(this.event, this.payload);
Map<String, dynamic> toJson() => {
'event': event,
'payload': payload,
};
}
class FFIAdaptor { class FFIAdaptor {
static Completer<Uint8List> asyncRequest(RequestPacket request) { static Completer<Uint8List> asyncRequest() {
Uint8List bytes = request.writeToBuffer(); // final command = FFICommand(
// "AuthCheck", Uint8List.fromList(utf8.encode("this is payload")));
final command = FFICommand("AuthCheck", Uint8List(0));
Uint8List bytes = Uint8List.fromList(utf8.encode(jsonEncode(command)));
assert(bytes.isEmpty == false); assert(bytes.isEmpty == false);
if (bytes.isEmpty) { if (bytes.isEmpty) {
@ -43,4 +60,3 @@ class FFIAdaptor {
return completer; return completer;
} }
} }

View File

@ -45,25 +45,24 @@ typedef _invoke_async_Dart = void Function(
int len, int len,
); );
/// C function `command_sync`. /// C function `command_sync`.
Pointer<Uint8> sync_command( Pointer<Uint8> sync_command(
Pointer<Uint8> input, Pointer<Uint8> input,
int len, int len,
) { ) {
return _invoke_sync(input, len); return _invoke_sync(input, len);
} }
final _invoke_sync_Dart _invoke_sync = final _invoke_sync_Dart _invoke_sync =
_dl.lookupFunction<_invoke_sync_C, _invoke_sync_Dart>('sync_command'); _dl.lookupFunction<_invoke_sync_C, _invoke_sync_Dart>('sync_command');
typedef _invoke_sync_C = Pointer<Uint8> Function( typedef _invoke_sync_C = Pointer<Uint8> Function(
Pointer<Uint8> input, Pointer<Uint8> input,
Uint64 len, Uint64 len,
); );
typedef _invoke_sync_Dart = Pointer<Uint8> Function( typedef _invoke_sync_Dart = Pointer<Uint8> Function(
Pointer<Uint8> input, Pointer<Uint8> input,
int len, int len,
); );
/// C function `init_sdk`. /// C function `init_sdk`.
int init_sdk( int init_sdk(
@ -111,11 +110,12 @@ typedef _store_dart_post_cobject_Dart = void Function(
bool is_tester() { bool is_tester() {
if (Foundation.kDebugMode) { if (Foundation.kDebugMode) {
// ignore: unnecessary_null_comparison // ignore: unnecessary_null_comparison
if (Platform.executable == null) { // if (Platform.executable.isEmpty) {
return false; // return false;
} else { // } else {
return Platform.executable.contains("tester"); // return Platform.executable.contains("tester");
} // }
return false;
} else { } else {
return false; return false;
} }

View File

@ -4,6 +4,7 @@ import 'dart:io';
import 'dart:async'; import 'dart:async';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'dart:ffi'; import 'dart:ffi';
import 'ffi/adaptor.dart';
import 'ffi/ffi.dart' as ffi; import 'ffi/ffi.dart' as ffi;
import 'package:ffi/ffi.dart'; import 'package:ffi/ffi.dart';
@ -16,12 +17,13 @@ class FlowySDK {
const FlowySDK(); const FlowySDK();
void dispose() { void dispose() {}
}
Future<void> init(Directory sdkDir) async { Future<void> init(Directory sdkDir) async {
ffi.store_dart_post_cobject(NativeApi.postCObject); ffi.store_dart_post_cobject(NativeApi.postCObject);
ffi.init_sdk(sdkDir.path.toNativeUtf8()); ffi.init_sdk(sdkDir.path.toNativeUtf8());
final resp = await FFIAdaptor.asyncRequest();
print(resp);
} }
} }

View File

@ -8,4 +8,6 @@ int64_t init_sdk(char *path);
void async_command(int64_t port, const uint8_t *input, uintptr_t len); void async_command(int64_t port, const uint8_t *input, uintptr_t len);
const uint8_t *sync_command(const uint8_t *input, uintptr_t len);
void link_me_please(void); void link_me_please(void);

View File

@ -204,13 +204,6 @@ packages:
relative: true relative: true
source: path source: path
version: "0.0.1" version: "0.0.1"
flowy_protobuf:
dependency: "direct main"
description:
path: "../flowy_protobuf"
relative: true
source: path
version: "0.0.1"
flutter: flutter:
dependency: "direct main" dependency: "direct main"
description: flutter description: flutter
@ -354,13 +347,6 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.5.0" version: "1.5.0"
protobuf:
dependency: transitive
description:
name: protobuf
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0"
pub_semver: pub_semver:
dependency: transitive dependency: transitive
description: description:

View File

@ -16,8 +16,6 @@ dependencies:
isolates: ^3.0.3+8 isolates: ^3.0.3+8
flowy_logger: flowy_logger:
path: ../flowy_logger path: ../flowy_logger
flowy_protobuf:
path: ../flowy_protobuf
infra: infra:
path: ../infra path: ../infra
dartz: '0.10.0-nullsafety.2' dartz: '0.10.0-nullsafety.2'

View File

@ -232,13 +232,6 @@ packages:
relative: true relative: true
source: path source: path
version: "0.0.1" version: "0.0.1"
flowy_protobuf:
dependency: "direct main"
description:
path: "packages/flowy_protobuf"
relative: true
source: path
version: "0.0.1"
flowy_sdk: flowy_sdk:
dependency: "direct main" dependency: "direct main"
description: description:
@ -501,13 +494,6 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "4.2.1" version: "4.2.1"
protobuf:
dependency: transitive
description:
name: protobuf
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.0"
provider: provider:
dependency: transitive dependency: transitive
description: description:

View File

@ -31,8 +31,6 @@ dependencies:
sdk: flutter sdk: flutter
flowy_sdk: flowy_sdk:
path: packages/flowy_sdk path: packages/flowy_sdk
flowy_protobuf:
path: packages/flowy_protobuf
flowy_style: flowy_style:
path: packages/flowy_style path: packages/flowy_style

View File

@ -20,7 +20,7 @@ byteorder = {version = "1.3.4"}
ffi-support = {version = "0.4.2"} ffi-support = {version = "0.4.2"}
protobuf = {version = "2.20.0"} protobuf = {version = "2.20.0"}
lazy_static = {version = "1.4.0"} lazy_static = {version = "1.4.0"}
tokio = { version = "1", features = ["sync"] } tokio = { version = "1", features = ["rt", "rt-multi-thread"] }
log = "0.4.14" log = "0.4.14"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde_json = {version = "1.0"} serde_json = {version = "1.0"}

View File

@ -3,50 +3,50 @@ mod c;
use crate::c::forget_rust; use crate::c::forget_rust;
use flowy_sdk::*; use flowy_sdk::*;
use flowy_sys::prelude::*; use flowy_sys::prelude::*;
use lazy_static::lazy_static;
use std::{cell::RefCell, ffi::CStr, future::Future, os::raw::c_char}; use std::{cell::RefCell, ffi::CStr, future::Future, os::raw::c_char};
lazy_static! {
pub static ref FFI_RUNTIME: tokio::runtime::Runtime =
tokio::runtime::Builder::new_current_thread()
.thread_name("flowy-dart-ffi")
.build()
.unwrap();
}
#[no_mangle] #[no_mangle]
pub extern "C" fn init_sdk(path: *mut c_char) -> i64 { pub extern "C" fn init_sdk(path: *mut c_char) -> i64 {
let c_str: &CStr = unsafe { CStr::from_ptr(path) }; let c_str: &CStr = unsafe { CStr::from_ptr(path) };
let path: &str = c_str.to_str().unwrap(); let path: &str = c_str.to_str().unwrap();
FlowySDK::init_log(); FlowySDK::init_log(path);
FlowySDK::init(path); FlowySDK::init(path);
return 1; return 1;
} }
#[derive(serde::Deserialize)]
pub struct FFICommand {
event: String,
payload: Vec<u8>,
}
impl FFICommand {
pub fn from_bytes(bytes: Vec<u8>) -> Self {
let command: FFICommand = serde_json::from_slice(&bytes).unwrap();
command
}
pub fn from_u8_pointer(pointer: *const u8, len: usize) -> Self {
let bytes = unsafe { std::slice::from_raw_parts(pointer, len) }.to_vec();
FFICommand::from_bytes(bytes)
}
}
#[no_mangle] #[no_mangle]
pub extern "C" fn async_command(port: i64, input: *const u8, len: usize) { pub extern "C" fn async_command(port: i64, input: *const u8, len: usize) {
let FFICommand { event, payload } = FFICommand::from_u8_pointer(input, len); let FFICommand { event, payload } = FFICommand::from_u8_pointer(input, len);
log::info!("Event: {:?}", event); let mut request = DispatchRequest::new(event);
log::trace!(
let mut request = DispatchRequest::new(port, event).callback(|_, resp| { "[FFI]: {} Async Event: {:?} with {} port",
log::info!("async resp: {:?}", resp); &request.id,
}); &request.event,
port
);
if !payload.is_empty() { if !payload.is_empty() {
request = request.payload(Payload::Bytes(payload)); request = request.payload(Payload::Bytes(payload));
} }
async_send(request); request = request.callback(Box::new(move |resp: EventResponse| {
spawn_future(async { vec![] }, 123); let bytes = match resp.data {
ResponseData::Bytes(bytes) => bytes,
ResponseData::None => vec![],
};
log::trace!("[FFI]: Post data to dart through {} port", port);
Box::pin(spawn_future(async { bytes }, port))
}));
let _ = EventDispatch::async_send(request);
} }
#[no_mangle] #[no_mangle]
@ -56,19 +56,36 @@ pub extern "C" fn sync_command(input: *const u8, len: usize) -> *const u8 { unim
#[no_mangle] #[no_mangle]
pub extern "C" fn link_me_please() {} pub extern "C" fn link_me_please() {}
#[derive(serde::Deserialize)]
pub struct FFICommand {
event: String,
payload: Vec<u8>,
}
impl FFICommand {
pub fn from_u8_pointer(pointer: *const u8, len: usize) -> Self {
let bytes = unsafe { std::slice::from_raw_parts(pointer, len) }.to_vec();
let command: FFICommand = serde_json::from_slice(&bytes).unwrap();
command
}
}
#[inline(always)] #[inline(always)]
fn spawn_future<F>(future: F, port: i64) async fn spawn_future<F>(future: F, port: i64)
where where
F: Future<Output = Vec<u8>> + Send + 'static, F: Future<Output = Vec<u8>> + Send + 'static,
{ {
let isolate = allo_isolate::Isolate::new(port); let isolate = allo_isolate::Isolate::new(port);
isolate.catch_unwind(future); match isolate.catch_unwind(future).await {
Ok(success) => {
// if let Err(e) = isolate.catch_unwind(future) { log::trace!("[FFI]: Post data to dart success");
// if let Some(msg) = e.downcast_ref::<&str>() { },
// log::error!("🔥 {:?}", msg); Err(e) => {
// } else { if let Some(msg) = e.downcast_ref::<&str>() {
// log::error!("no info provided for that panic 😡"); log::error!("[FFI]: ❌ {:?}", msg);
// } } else {
// } log::error!("[FFI]: allo_isolate post panic");
}
},
}
} }

View File

@ -262,7 +262,7 @@ pub struct ASTEnumAttrVariant {
} }
impl ASTEnumAttrVariant { impl ASTEnumAttrVariant {
pub fn from_ast(cx: &Ctxt, variant: &syn::Variant) -> Self { pub fn from_ast(_cx: &Ctxt, variant: &syn::Variant) -> Self {
let name = variant.ident.to_string(); let name = variant.ident.to_string();
let mut value = String::new(); let mut value = String::new();
if variant.discriminant.is_some() { if variant.discriminant.is_some() {

View File

@ -1,5 +1,5 @@
use crate::Ctxt;
use quote::format_ident;
use syn::{self, AngleBracketedGenericArguments, PathSegment}; use syn::{self, AngleBracketedGenericArguments, PathSegment};
#[derive(Eq, PartialEq, Debug)] #[derive(Eq, PartialEq, Debug)]

View File

@ -6,8 +6,9 @@ edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
tracing = { version = "0.1" } #tracing = { version = "0.1" }
tracing-log = { version = "0.1.1", features = ["env_logger"]} tracing = { version = "0.1", features = ["max_level_debug", "release_max_level_warn"] }
tracing-log = { version = "0.1.1"}
tracing-futures = "0.2.4" tracing-futures = "0.2.4"
tracing-subscriber = { version = "0.2.12", features = ["registry", "env-filter"] } tracing-subscriber = { version = "0.2.12", features = ["registry", "env-filter"] }
tracing-bunyan-formatter = "0.2.2" tracing-bunyan-formatter = "0.2.2"

View File

@ -1,37 +1,96 @@
use tracing::subscriber::set_global_default; use log::LevelFilter;
use std::path::Path;
use tracing::{subscriber::set_global_default, Level};
use tracing_appender::rolling::RollingFileAppender;
use tracing_bunyan_formatter::{BunyanFormattingLayer, JsonStorageLayer}; use tracing_bunyan_formatter::{BunyanFormattingLayer, JsonStorageLayer};
use tracing_log::LogTracer; use tracing_log::LogTracer;
use tracing_subscriber::{layer::SubscriberExt, EnvFilter}; use tracing_subscriber::{
layer::{Layered, SubscriberExt},
EnvFilter,
};
pub fn init_log(name: &str, env_filter: &str) -> std::result::Result<(), String> { pub struct FlowyLogBuilder {
let env_filter = name: String,
EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new(env_filter.to_owned())); env_filter: String,
let formatting_layer = BunyanFormattingLayer::new(name.to_owned(), std::io::stdout); directory: String,
file_appender: RollingFileAppender,
}
let subscriber = tracing_subscriber::fmt() impl FlowyLogBuilder {
.with_target(false) pub fn new(name: &str, directory: impl AsRef<Path>) -> Self {
.with_writer(std::io::stdout) let directory = directory.as_ref().to_str().unwrap().to_owned();
.with_thread_ids(false) let local_file_name = format!("{}.log", name);
.with_target(false) let file_appender = tracing_appender::rolling::hourly(directory.clone(), local_file_name);
.compact() FlowyLogBuilder {
.finish() name: name.to_owned(),
.with(env_filter) env_filter: "Info".to_owned(),
.with(JsonStorageLayer) directory,
.with(formatting_layer); file_appender,
}
}
let _ = LogTracer::init().map_err(|e| format!("{:?}", e))?; pub fn env_filter(mut self, env_filter: &str) -> Self {
let _ = set_global_default(subscriber).map_err(|e| format!("{:?}", e))?; self.env_filter = env_filter.to_owned();
Ok(()) self
}
pub fn build(self) -> std::result::Result<(), String> {
let env_filter = EnvFilter::new(self.env_filter);
let (non_blocking, _guard) = tracing_appender::non_blocking(self.file_appender);
let formatting_layer = BunyanFormattingLayer::new(self.name, std::io::stdout);
let mut subscriber = tracing_subscriber::fmt()
.with_target(false)
.with_max_level(tracing::Level::TRACE)
.with_writer(std::io::stdout)
.with_thread_ids(false)
.with_target(false)
// .with_writer(non_blocking)
.compact()
.finish()
.with(env_filter)
.with(JsonStorageLayer)
.with(formatting_layer);
let _ = LogTracer::builder()
.with_max_level(LevelFilter::Trace)
.init()
.map_err(|e| format!("{:?}", e))
.unwrap();
let _ = set_global_default(subscriber).map_err(|e| format!("{:?}", e))?;
Ok(())
}
}
pub fn init_log(name: &str, directory: &str, env_filter: &str) -> std::result::Result<(), String> {
FlowyLogBuilder::new(name, directory)
.env_filter(env_filter)
.build()
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
#[derive(Debug)]
struct Position {
x: f32,
y: f32,
}
#[test] #[test]
fn test_log() { fn test_log() {
init_log("flowy-log", "info").unwrap(); init_log("flowy", ".", "Debug").unwrap();
tracing::info!("😁 Tracing info log"); tracing::info!("😁 Tracing info log");
log::info!("😁 bridge 'log' to 'tracing'");
let pos = Position {
x: 3.234,
y: -1.223,
};
tracing::debug!(?pos.x, ?pos.y);
log::debug!("😁 bridge 'log' to 'tracing'");
} }
} }

12
rust-lib/flowy-sdk/.gitignore vendored Normal file
View File

@ -0,0 +1,12 @@
# Generated by Cargo
# will have compiled files and executables
/target/
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
Cargo.lock
# These are backup files generated by rustfmt
**/*.rs.bk
**/temp/

View File

@ -6,10 +6,10 @@ edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
flowy-sys = { path = "../flowy-sys" } flowy-sys = { path = "../flowy-sys", features = ["use_tracing"]}
flowy-log = { path = "../flowy-log" } flowy-log = { path = "../flowy-log" }
flowy-user = { path = "../flowy-user" } flowy-user = { path = "../flowy-user" }
tracing = { version = "0.1" }
log = "0.4.14" log = "0.4.14"
[dev-dependencies] [dev-dependencies]

View File

@ -6,19 +6,17 @@ use module::build_modules;
pub struct FlowySDK {} pub struct FlowySDK {}
impl FlowySDK { impl FlowySDK {
pub fn init_log() { flowy_log::init_log("flowy", "Debug").unwrap(); } pub fn init_log(directory: &str) { flowy_log::init_log("flowy", directory, "Debug").unwrap(); }
pub fn init(path: &str) { pub fn init(path: &str) {
log::info!("🔥 System start running"); log::info!("🔥 Start running");
log::debug!("🔥 Root path: {}", path); tracing::info!("🔥 Root path: {}", path);
EventDispatch::construct(|| build_modules()); EventDispatch::construct(|| build_modules());
} }
} }
pub async fn async_send(data: DispatchRequest<i64>) -> Result<EventResponse, SystemError> { pub async fn async_send(request: DispatchRequest) -> EventResponse {
EventDispatch::async_send(data).await EventDispatch::async_send(request).await
} }
pub fn sync_send(data: DispatchRequest<i64>) -> Result<EventResponse, SystemError> { pub fn sync_send(request: DispatchRequest) -> EventResponse { EventDispatch::sync_send(request) }
EventDispatch::sync_send(data)
}

View File

@ -3,32 +3,42 @@ pub use flowy_sdk::*;
use flowy_sys::prelude::*; use flowy_sys::prelude::*;
use std::{ use std::{
fmt::{Debug, Display}, fmt::{Debug, Display},
fs,
hash::Hash, hash::Hash,
sync::Once, sync::Once,
}; };
static INIT: Once = Once::new(); static INIT: Once = Once::new();
#[allow(dead_code)] pub fn init_sdk() {
let root_dir = root_dir();
pub fn init_system() {
INIT.call_once(|| { INIT.call_once(|| {
FlowySDK::init_log(); FlowySDK::init_log(&root_dir);
}); });
FlowySDK::init(&root_dir);
FlowySDK::init("123");
} }
pub struct FlowySDKTester { fn root_dir() -> String {
request: DispatchRequest<i64>, let mut path = fs::canonicalize(".").unwrap();
path.push("tests/temp/flowy/");
let path_str = path.to_str().unwrap().to_string();
if !std::path::Path::new(&path).exists() {
std::fs::create_dir_all(path).unwrap();
}
path_str
} }
impl FlowySDKTester { pub struct EventTester {
request: DispatchRequest,
}
impl EventTester {
pub fn new<E>(event: E) -> Self pub fn new<E>(event: E) -> Self
where where
E: Eq + Hash + Debug + Clone + Display, E: Eq + Hash + Debug + Clone + Display,
{ {
Self { Self {
request: DispatchRequest::new(1, event), request: DispatchRequest::new(event),
} }
} }
@ -52,16 +62,18 @@ impl FlowySDKTester {
self self
} }
#[allow(dead_code)]
pub async fn async_send(self) -> EventResponse { pub async fn async_send(self) -> EventResponse {
init_system(); init_sdk();
let resp = async_send(self.request).await.unwrap(); let resp = async_send(self.request).await;
dbg!(&resp); dbg!(&resp);
resp resp
} }
#[allow(dead_code)]
pub fn sync_send(self) -> EventResponse { pub fn sync_send(self) -> EventResponse {
init_system(); init_sdk();
let resp = sync_send(self.request).unwrap(); let resp = sync_send(self.request);
dbg!(&resp); dbg!(&resp);
resp resp
} }

View File

@ -4,19 +4,17 @@ use flowy_user::prelude::*;
use tokio::time::{sleep, Duration}; use tokio::time::{sleep, Duration};
#[test] #[test]
#[should_panic]
fn auth_check_no_payload() { fn auth_check_no_payload() {
let callback = |_, resp: EventResponse| { let resp = EventTester::new(AuthCheck).sync_send();
assert_eq!(resp.status, StatusCode::Err); assert_eq!(resp.status, StatusCode::Ok);
};
let resp = FlowySDKTester::new(AuthCheck).sync_send();
} }
#[tokio::test] #[tokio::test]
async fn auth_check_with_user_name_email_payload() { async fn auth_check_with_user_name_email_payload() {
let user_data = UserData::new("jack".to_owned(), "helloworld@gmail.com".to_owned()); let user_data = UserData::new("jack".to_owned(), "helloworld@gmail.com".to_owned());
FlowySDKTester::new(AuthCheck) EventTester::new(AuthCheck)
.bytes_payload(user_data) .bytes_payload(user_data)
.sync_send(); .sync_send();
} }

View File

@ -28,6 +28,7 @@ serde = { version = "1.0", features = ["derive"] }
#optional crate #optional crate
bincode = { version = "1.3", optional = true} bincode = { version = "1.3", optional = true}
protobuf = {version = "2.24.1", optional = true} protobuf = {version = "2.24.1", optional = true}
tracing = { version = "0.1", optional = true}
[dev-dependencies] [dev-dependencies]
tokio = { version = "1", features = ["full"] } tokio = { version = "1", features = ["full"] }
@ -36,3 +37,4 @@ futures-util = "0.3.15"
[features] [features]
use_serde = ["bincode"] use_serde = ["bincode"]
use_protobuf= ["protobuf"] use_protobuf= ["protobuf"]
use_tracing= ["tracing"]

View File

@ -8,11 +8,19 @@ use crate::{
}; };
use derivative::*; use derivative::*;
use futures_core::future::BoxFuture; use futures_core::future::BoxFuture;
use futures_util::task::Context;
use lazy_static::lazy_static; use lazy_static::lazy_static;
use pin_project::pin_project;
use std::{ use std::{
fmt::{Debug, Display}, fmt::{Debug, Display},
future::Future,
hash::Hash, hash::Hash,
sync::RwLock, sync::RwLock,
thread::JoinHandle,
};
use tokio::{
macros::support::{Pin, Poll},
task::JoinError,
}; };
lazy_static! { lazy_static! {
@ -30,9 +38,9 @@ impl EventDispatch {
F: FnOnce() -> Vec<Module>, F: FnOnce() -> Vec<Module>,
{ {
let modules = module_factory(); let modules = module_factory();
log::debug!("{}", module_info(&modules));
let module_map = as_module_map(modules); let module_map = as_module_map(modules);
let runtime = tokio_default_runtime().unwrap(); let runtime = tokio_default_runtime().unwrap();
let dispatch = EventDispatch { let dispatch = EventDispatch {
module_map, module_map,
runtime, runtime,
@ -41,115 +49,133 @@ impl EventDispatch {
*(EVENT_DISPATCH.write().unwrap()) = Some(dispatch); *(EVENT_DISPATCH.write().unwrap()) = Some(dispatch);
} }
pub async fn async_send<T>(request: DispatchRequest<T>) -> Result<EventResponse, SystemError> pub fn async_send(request: DispatchRequest) -> DispatchFuture {
where
T: 'static + Debug + Send + Sync,
{
match EVENT_DISPATCH.read() { match EVENT_DISPATCH.read() {
Ok(dispatch) => { Ok(dispatch) => {
let dispatch = dispatch.as_ref().unwrap(); let dispatch = dispatch.as_ref().unwrap();
let module_map = dispatch.module_map.clone(); let module_map = dispatch.module_map.clone();
let service = Box::new(DispatchService { module_map }); let service = Box::new(DispatchService { module_map });
dispatch log::trace!("{}: dispatch {:?} to runtime", &request.id, &request.event);
.runtime let join_handle = dispatch.runtime.spawn(async move {
.spawn(async move { service.call(request).await }) service
.await .call(request)
.unwrap_or_else(|e| { .await
let msg = format!("{:?}", e); .unwrap_or_else(|e| InternalError::new(format!("{:?}", e)).as_response())
Ok(InternalError::new(msg).as_response()) });
})
DispatchFuture {
fut: Box::pin(async move {
join_handle.await.unwrap_or_else(|e| {
InternalError::new(format!("Dispatch join error: {:?}", e))
.as_response()
})
}),
}
}, },
Err(e) => { Err(e) => {
let msg = format!("{:?}", e); let msg = format!("Dispatch runtime error: {:?}", e);
Err(InternalError::new(msg).into()) log::trace!("{}", msg);
DispatchFuture {
fut: Box::pin(async { InternalError::new(msg).as_response() }),
}
}, },
} }
} }
pub fn sync_send<T>(request: DispatchRequest<T>) -> Result<EventResponse, SystemError> pub fn sync_send(request: DispatchRequest) -> EventResponse {
where
T: 'static + Debug + Send + Sync,
{
futures::executor::block_on(async { EventDispatch::async_send(request).await }) futures::executor::block_on(async { EventDispatch::async_send(request).await })
} }
} }
pub type BoxStreamCallback<T> = Box<dyn FnOnce(T, EventResponse) + 'static + Send + Sync>; #[pin_project]
pub struct DispatchFuture {
#[pin]
fut: BoxFuture<'static, EventResponse>,
}
impl Future for DispatchFuture {
type Output = EventResponse;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.as_mut().project();
loop {
return Poll::Ready(futures_core::ready!(this.fut.poll(cx)));
}
}
}
pub type BoxFutureCallback =
Box<dyn FnOnce(EventResponse) -> BoxFuture<'static, ()> + 'static + Send + Sync>;
#[derive(Derivative)] #[derive(Derivative)]
#[derivative(Debug)] #[derivative(Debug)]
pub struct DispatchRequest<T> pub struct DispatchRequest {
where pub id: String,
T: 'static + Debug,
{
pub config: T,
pub event: Event, pub event: Event,
pub payload: Option<Payload>, pub payload: Payload,
#[derivative(Debug = "ignore")] #[derivative(Debug = "ignore")]
pub callback: Option<BoxStreamCallback<T>>, pub callback: Option<BoxFutureCallback>,
} }
impl<T> DispatchRequest<T> impl DispatchRequest {
where pub fn new<E>(event: E) -> Self
T: 'static + Debug,
{
pub fn new<E>(config: T, event: E) -> Self
where where
E: Eq + Hash + Debug + Clone + Display, E: Eq + Hash + Debug + Clone + Display,
{ {
Self { Self {
config, payload: Payload::None,
payload: None,
event: event.into(), event: event.into(),
id: uuid::Uuid::new_v4().to_string(),
callback: None, callback: None,
} }
} }
pub fn payload(mut self, payload: Payload) -> Self { pub fn payload(mut self, payload: Payload) -> Self {
self.payload = Some(payload); self.payload = payload;
self self
} }
pub fn callback<F>(mut self, callback: F) -> Self pub fn callback(mut self, callback: BoxFutureCallback) -> Self {
where self.callback = Some(callback);
F: FnOnce(T, EventResponse) + 'static + Send + Sync,
{
self.callback = Some(Box::new(callback));
self self
} }
pub(crate) fn into_parts(self) -> (ModuleRequest, Option<BoxFutureCallback>) {
let DispatchRequest {
event,
payload,
id,
callback,
} = self;
(ModuleRequest::new(event.clone(), id, payload), callback)
}
} }
pub(crate) struct DispatchService { pub(crate) struct DispatchService {
pub(crate) module_map: ModuleMap, pub(crate) module_map: ModuleMap,
} }
impl<T> Service<DispatchRequest<T>> for DispatchService impl Service<DispatchRequest> for DispatchService {
where
T: 'static + Debug + Send + Sync,
{
type Response = EventResponse; type Response = EventResponse;
type Error = SystemError; type Error = SystemError;
type Future = BoxFuture<'static, Result<Self::Response, Self::Error>>; type Future = BoxFuture<'static, Result<Self::Response, Self::Error>>;
fn call(&self, dispatch_request: DispatchRequest<T>) -> Self::Future { fn call(&self, dispatch_request: DispatchRequest) -> Self::Future {
let module_map = self.module_map.clone(); let module_map = self.module_map.clone();
let DispatchRequest { let (request, callback) = dispatch_request.into_parts();
config,
event,
payload,
callback,
} = dispatch_request;
let mut request = ModuleRequest::new(event.clone());
if let Some(payload) = payload {
request = request.payload(payload);
};
Box::pin(async move { Box::pin(async move {
let result = { let result = {
match module_map.get(&event) { match module_map.get(&request.event()) {
Some(module) => { Some(module) => {
let fut = module.new_service(()); let fut = module.new_service(());
log::trace!(
"{}: handle event: {:?} by {}",
request.id(),
request.event(),
module.name
);
let service_fut = fut.await?.call(request); let service_fut = fut.await?.call(request);
service_fut.await service_fut.await
}, },
@ -158,17 +184,27 @@ where
"Can not find the module to handle the request:{:?}", "Can not find the module to handle the request:{:?}",
request request
); );
log::trace!("{}", msg);
Err(InternalError::new(msg).into()) Err(InternalError::new(msg).into())
}, },
} }
}; };
let response = result.unwrap_or_else(|e| e.into()); let response = result.unwrap_or_else(|e| e.into());
log::trace!("Dispatch result: {:?}", response);
if let Some(callback) = callback { if let Some(callback) = callback {
callback(config, response.clone()); callback(response.clone()).await;
} }
Ok(response) Ok(response)
}) })
} }
} }
fn module_info(modules: &Vec<Module>) -> String {
let mut info = format!("{} modules loaded\n", modules.len());
for module in modules {
info.push_str(&format!("-> {} loaded \n", module.name));
}
info
}

View File

@ -1,5 +1,6 @@
use std::{ use std::{
collections::HashMap, collections::HashMap,
fmt,
fmt::{Debug, Display}, fmt::{Debug, Display},
future::Future, future::Future,
hash::Hash, hash::Hash,
@ -53,7 +54,7 @@ impl<T: Display + Eq + Hash + Debug + Clone> std::convert::From<T> for Event {
pub type EventServiceFactory = BoxServiceFactory<(), ServiceRequest, ServiceResponse, SystemError>; pub type EventServiceFactory = BoxServiceFactory<(), ServiceRequest, ServiceResponse, SystemError>;
pub struct Module { pub struct Module {
name: String, pub name: String,
data: DataContainer, data: DataContainer,
service_map: Arc<HashMap<Event, EventServiceFactory>>, service_map: Arc<HashMap<Event, EventServiceFactory>>,
} }
@ -112,26 +113,27 @@ pub struct ModuleRequest {
} }
impl ModuleRequest { impl ModuleRequest {
pub fn new<E>(event: E) -> Self pub fn new<E>(event: E, id: String, payload: Payload) -> Self
where where
E: Into<Event>, E: Into<Event>,
{ {
Self { Self {
inner: EventRequest::new(event), inner: EventRequest::new(event, id),
payload: Payload::None, payload,
} }
} }
pub fn payload(mut self, payload: Payload) -> Self {
self.payload = payload;
self
}
pub(crate) fn id(&self) -> &str { &self.inner.id } pub(crate) fn id(&self) -> &str { &self.inner.id }
pub(crate) fn event(&self) -> &Event { &self.inner.event } pub(crate) fn event(&self) -> &Event { &self.inner.event }
} }
impl std::fmt::Display for ModuleRequest {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}:{:?}", self.inner.id, self.inner.event)
}
}
impl std::convert::Into<ServiceRequest> for ModuleRequest { impl std::convert::Into<ServiceRequest> for ModuleRequest {
fn into(self) -> ServiceRequest { ServiceRequest::new(self.inner, self.payload) } fn into(self) -> ServiceRequest { ServiceRequest::new(self.inner, self.payload) }
} }
@ -162,8 +164,9 @@ impl Service<ModuleRequest> for ModuleService {
type Error = SystemError; type Error = SystemError;
type Future = BoxFuture<'static, Result<Self::Response, Self::Error>>; type Future = BoxFuture<'static, Result<Self::Response, Self::Error>>;
// #[cfg_attr(feature = "use_tracing", xxx)]
#[tracing::instrument(name = "Module Service", level = "debug", skip(self))]
fn call(&self, request: ModuleRequest) -> Self::Future { fn call(&self, request: ModuleRequest) -> Self::Future {
log::trace!("Call module service for request {}", &request.id());
match self.service_map.get(&request.event()) { match self.service_map.get(&request.event()) {
Some(factory) => { Some(factory) => {
let service_fut = factory.new_service(()); let service_fut = factory.new_service(());

View File

@ -7,7 +7,6 @@ use crate::{
util::ready::{ready, Ready}, util::ready::{ready, Ready},
}; };
use futures_core::ready; use futures_core::ready;
use std::{ use std::{
fmt::Debug, fmt::Debug,
@ -23,12 +22,12 @@ pub struct EventRequest {
} }
impl EventRequest { impl EventRequest {
pub fn new<E>(event: E) -> EventRequest pub fn new<E>(event: E, id: String) -> EventRequest
where where
E: Into<Event>, E: Into<Event>,
{ {
Self { Self {
id: uuid::Uuid::new_v4().to_string(), id,
event: event.into(), event: event.into(),
} }
} }
@ -63,11 +62,8 @@ impl FromRequest for String {
} }
fn unexpected_none_payload(request: &EventRequest) -> SystemError { fn unexpected_none_payload(request: &EventRequest) -> SystemError {
log::warn!( log::warn!("{:?} expected payload", &request.event);
"Event: {:?} expected payload but payload is empty", InternalError::new("Expected payload").into()
&request.event
);
InternalError::new("Expected payload but payload is empty").into()
} }
#[doc(hidden)] #[doc(hidden)]

View File

@ -1,8 +1,9 @@
pub use builder::*; pub use builder::*;
pub use data::*;
pub use responder::*; pub use responder::*;
pub use response::*; pub use response::*;
mod builder; mod builder;
pub mod data; mod data;
mod responder; mod responder;
mod response; mod response;

View File

@ -6,7 +6,7 @@ pub mod ready;
pub(crate) fn tokio_default_runtime() -> io::Result<tokio::runtime::Runtime> { pub(crate) fn tokio_default_runtime() -> io::Result<tokio::runtime::Runtime> {
runtime::Builder::new_multi_thread() runtime::Builder::new_multi_thread()
.thread_name("flowy-sys") .thread_name("flowy-rt")
.enable_io() .enable_io()
.enable_time() .enable_time()
.on_thread_start(move || { .on_thread_start(move || {

View File

@ -10,14 +10,13 @@ pub fn setup_env() {
}); });
} }
pub async fn async_send(data: DispatchRequest<i64>) -> Result<EventResponse, SystemError> { pub async fn async_send(request: DispatchRequest) -> EventResponse {
EventDispatch::async_send(data).await EventDispatch::async_send(request).await
} }
pub fn init_system<F>(module_factory: F) pub fn init_dispatch<F>(module_factory: F)
where where
F: FnOnce() -> Vec<Module>, F: FnOnce() -> Vec<Module>,
{ {
let system = EventDispatch::new(module_factory); EventDispatch::construct(module_factory);
EventDispatch::set_current(system);
} }

View File

@ -7,9 +7,9 @@ pub async fn hello() -> String { "say hello".to_string() }
async fn test_init() { async fn test_init() {
setup_env(); setup_env();
let event = "1"; let event = "1";
init_system(|| vec![Module::new().event(event, hello)]); init_dispatch(|| vec![Module::new().event(event, hello)]);
let request = DispatchRequest::new(1, event); let request = DispatchRequest::new(event);
let resp = async_send(request).await.unwrap(); let resp = async_send(request).await;
log::info!("sync resp: {:?}", resp); dbg!(&resp);
} }

View File

@ -3,6 +3,7 @@ use flowy_sys::prelude::*;
pub fn create() -> Module { pub fn create() -> Module {
Module::new() Module::new()
.name("Flowy-User")
.event(AuthCheck, user_check) .event(AuthCheck, user_check)
.event(SignIn, user_check) .event(SignIn, user_check)
.event(SignUp, user_check) .event(SignUp, user_check)

6
scripts/build_sdk.sh Executable file
View File

@ -0,0 +1,6 @@
#!/bin/sh
#!/usr/bin/env fish
echo 'Start building rust sdk'
rustup show
cargo make desktop