mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
Feat/tauri (#1716)
* feat: support tauri desktop * chore: support call flowy sdk command * chore: switch to svelte * chore: gen js protobuf * chore: import js protobuf * chore: call flowy sdk handler * chore: update scipts * chore: create index.ts * chore: track files * chore: gen ts event * chore: replace application icon * chore: migrate to react * chore: fix wanrings Co-authored-by: nathan <nathan@appflowy.io>
This commit is contained in:
1
frontend/rust-lib/Cargo.lock
generated
1
frontend/rust-lib/Cargo.lock
generated
@ -1965,6 +1965,7 @@ dependencies = [
|
||||
"protobuf",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_repr",
|
||||
"serde_with",
|
||||
"thread-id",
|
||||
"tokio",
|
||||
|
@ -26,7 +26,8 @@ pub extern "C" fn init_sdk(path: *mut c_char) -> i64 {
|
||||
let path: &str = c_str.to_str().unwrap();
|
||||
|
||||
let server_config = get_client_server_configuration().unwrap();
|
||||
let config = FlowySDKConfig::new(path, "appflowy".to_string(), server_config).log_filter("info");
|
||||
let log_crates = vec!["flowy-ffi".to_string()];
|
||||
let config = FlowySDKConfig::new(path, "appflowy".to_string(), server_config).log_filter("info", log_crates);
|
||||
*FLOWY_SDK.write() = Some(FlowySDK::new(config));
|
||||
|
||||
0
|
||||
|
@ -33,7 +33,6 @@ impl std::convert::From<AFPluginEventResponse> for FFIResponse {
|
||||
let code = match resp.status_code {
|
||||
StatusCode::Ok => FFIStatusCode::Ok,
|
||||
StatusCode::Err => FFIStatusCode::Err,
|
||||
StatusCode::Internal => FFIStatusCode::Internal,
|
||||
};
|
||||
|
||||
// let msg = match resp.error {
|
||||
|
@ -46,4 +46,6 @@ proto_gen = [
|
||||
"protoc-bin-vendored",
|
||||
]
|
||||
dart_event = ["walkdir", "tera", "syn"]
|
||||
dart = ["proto_gen", "dart_event"]
|
||||
dart = ["proto_gen", "dart_event"]
|
||||
ts_event = ["walkdir", "tera", "syn"]
|
||||
ts = ["proto_gen", "ts_event"]
|
@ -1,5 +1,5 @@
|
||||
#![allow(clippy::module_inception)]
|
||||
mod ast;
|
||||
pub(crate) mod ast;
|
||||
mod dart_event;
|
||||
mod event_template;
|
||||
|
||||
|
@ -4,10 +4,13 @@ pub mod protobuf_file;
|
||||
#[cfg(feature = "dart_event")]
|
||||
pub mod dart_event;
|
||||
|
||||
#[cfg(any(feature = "proto_gen", feature = "dart_event"))]
|
||||
#[cfg(feature = "ts_event")]
|
||||
pub mod ts_event;
|
||||
|
||||
#[cfg(any(feature = "proto_gen", feature = "dart_event", feature = "ts_event"))]
|
||||
mod flowy_toml;
|
||||
|
||||
#[cfg(any(feature = "proto_gen", feature = "dart_event"))]
|
||||
#[cfg(any(feature = "proto_gen", feature = "dart_event", feature = "ts_event"))]
|
||||
pub mod util;
|
||||
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
|
@ -58,6 +58,15 @@ pub fn gen(crate_name: &str) {
|
||||
&protoc_bin_path,
|
||||
);
|
||||
|
||||
#[cfg(feature = "ts")]
|
||||
generate_ts_protobuf_files(
|
||||
crate_name,
|
||||
&proto_file_output_path,
|
||||
&proto_file_paths,
|
||||
&file_names,
|
||||
&protoc_bin_path,
|
||||
);
|
||||
|
||||
// 3. generate the protobuf files(Rust)
|
||||
generate_rust_protobuf_files(
|
||||
&protoc_bin_path,
|
||||
@ -83,6 +92,64 @@ fn generate_rust_protobuf_files(
|
||||
.expect("Running rust protoc failed.");
|
||||
}
|
||||
|
||||
#[cfg(feature = "ts")]
|
||||
fn generate_ts_protobuf_files(
|
||||
name: &str,
|
||||
proto_file_output_path: &str,
|
||||
paths: &[String],
|
||||
file_names: &Vec<String>,
|
||||
protoc_bin_path: &Path,
|
||||
) {
|
||||
if std::env::var("TAURI_PROTOBUF_PATH").is_err() {
|
||||
eprintln!("TAURI_PROTOBUF_PATH was not set, skip generate ts pb");
|
||||
return;
|
||||
}
|
||||
|
||||
let mut output = PathBuf::new();
|
||||
output.push(std::env::var("CARGO_MAKE_WORKING_DIRECTORY").unwrap());
|
||||
output.push(std::env::var("TAURI_PROTOBUF_PATH").unwrap());
|
||||
output.push("classes");
|
||||
output.push(name);
|
||||
|
||||
if !output.as_path().exists() {
|
||||
std::fs::create_dir_all(&output).unwrap();
|
||||
}
|
||||
let protoc_bin_path = protoc_bin_path.to_str().unwrap().to_owned();
|
||||
paths.iter().for_each(|path| {
|
||||
let result = cmd_lib::run_cmd! {
|
||||
${protoc_bin_path} --ts_out=${output} --proto_path=${proto_file_output_path} ${path}
|
||||
};
|
||||
|
||||
if result.is_err() {
|
||||
panic!("Generate dart pb file failed with: {}, {:?}", path, result)
|
||||
};
|
||||
});
|
||||
|
||||
let ts_index = path_string_with_component(&output, vec!["index.ts"]);
|
||||
match std::fs::OpenOptions::new()
|
||||
.create(true)
|
||||
.write(true)
|
||||
.append(false)
|
||||
.truncate(true)
|
||||
.open(&ts_index)
|
||||
{
|
||||
Ok(ref mut file) => {
|
||||
let mut export = String::new();
|
||||
export.push_str("// Auto-generated, do not edit \n");
|
||||
for file_name in file_names {
|
||||
let c = format!("export * from \"./{}\";\n", file_name);
|
||||
export.push_str(c.as_ref());
|
||||
}
|
||||
|
||||
file.write_all(export.as_bytes()).unwrap();
|
||||
File::flush(file).unwrap();
|
||||
}
|
||||
Err(err) => {
|
||||
panic!("Failed to open file: {}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "dart")]
|
||||
fn generate_dart_protobuf_files(
|
||||
name: &str,
|
||||
|
@ -0,0 +1,65 @@
|
||||
use crate::util::get_tera;
|
||||
use tera::Context;
|
||||
|
||||
pub struct EventTemplate {
|
||||
tera_context: Context,
|
||||
}
|
||||
|
||||
pub struct EventRenderContext {
|
||||
pub input_deserializer: Option<String>,
|
||||
pub output_deserializer: Option<String>,
|
||||
pub error_deserializer: String,
|
||||
pub event: String,
|
||||
pub event_ty: String,
|
||||
pub prefix: String,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
impl EventTemplate {
|
||||
pub fn new() -> Self {
|
||||
EventTemplate {
|
||||
tera_context: Context::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn render(&mut self, ctx: EventRenderContext, index: usize) -> Option<String> {
|
||||
self.tera_context.insert("index", &index);
|
||||
let event_func_name = format!("{}{}", ctx.event_ty, ctx.event);
|
||||
self.tera_context.insert("event_func_name", &event_func_name);
|
||||
self.tera_context
|
||||
.insert("event_name", &format!("{}.{}", ctx.prefix, ctx.event_ty));
|
||||
self.tera_context.insert("event", &ctx.event);
|
||||
|
||||
self.tera_context.insert("has_input", &ctx.input_deserializer.is_some());
|
||||
match ctx.input_deserializer {
|
||||
None => {}
|
||||
Some(ref input) => self
|
||||
.tera_context
|
||||
.insert("input_deserializer", &format!("{}.{}", ctx.prefix, input)),
|
||||
}
|
||||
|
||||
let has_output = ctx.output_deserializer.is_some();
|
||||
self.tera_context.insert("has_output", &has_output);
|
||||
|
||||
match ctx.output_deserializer {
|
||||
None => self.tera_context.insert("output_deserializer", "void"),
|
||||
Some(ref output) => self
|
||||
.tera_context
|
||||
.insert("output_deserializer", &format!("{}.{}", ctx.prefix, output)),
|
||||
}
|
||||
|
||||
self.tera_context.insert(
|
||||
"error_deserializer",
|
||||
&format!("{}.{}", ctx.prefix, ctx.error_deserializer),
|
||||
);
|
||||
|
||||
let tera = get_tera("ts_event");
|
||||
match tera.render("event_template.tera", &self.tera_context) {
|
||||
Ok(r) => Some(r),
|
||||
Err(e) => {
|
||||
log::error!("{:?}", e);
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
|
||||
{%- if has_input %}
|
||||
export async function {{ event_func_name }}(payload: {{ input_deserializer }}): Promise<Result<{{ output_deserializer }}, {{ error_deserializer }}>> {
|
||||
{%- else %}
|
||||
export async function {{ event_func_name }}(): Promise<Result<{{ output_deserializer }}, {{ error_deserializer }}>> {
|
||||
{%- endif %}
|
||||
{%- if has_input %}
|
||||
let args = {
|
||||
request: {
|
||||
ty: {{ event_name }}[{{ event_name }}.{{ event }}],
|
||||
payload: Array.from(payload.serializeBinary()),
|
||||
},
|
||||
};
|
||||
{%- else %}
|
||||
let args = {
|
||||
request: {
|
||||
ty: {{ event_name }}[{{ event_name }}.{{ event }}],
|
||||
payload: Array.from([]),
|
||||
},
|
||||
};
|
||||
{%- endif %}
|
||||
|
||||
let result: { code: number; payload: Uint8Array } = await invoke("invoke_request", args);
|
||||
if (result.code == 0) {
|
||||
{%- if has_output %}
|
||||
let object = {{ output_deserializer }}.deserializeBinary(result.payload);
|
||||
console.log("Success:" + JSON.stringify(object.toObject()))
|
||||
return Ok(object);
|
||||
{%- else %}
|
||||
return Ok.EMPTY;
|
||||
{%- endif %}
|
||||
} else {
|
||||
let error = {{ error_deserializer }}.deserializeBinary(result.payload);
|
||||
console.log("Error:" + JSON.stringify(error.toObject()))
|
||||
return Err(error);
|
||||
}
|
||||
}
|
202
frontend/rust-lib/flowy-codegen/src/ts_event/mod.rs
Normal file
202
frontend/rust-lib/flowy-codegen/src/ts_event/mod.rs
Normal file
@ -0,0 +1,202 @@
|
||||
mod event_template;
|
||||
|
||||
use crate::dart_event::ast::EventASTContext;
|
||||
use crate::flowy_toml::{parse_crate_config_from, CrateConfig};
|
||||
use crate::ts_event::event_template::{EventRenderContext, EventTemplate};
|
||||
use crate::util::{is_crate_dir, is_hidden, path_string_with_component, read_file};
|
||||
use flowy_ast::ASTResult;
|
||||
use std::collections::HashSet;
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
use std::path::PathBuf;
|
||||
use syn::Item;
|
||||
use walkdir::WalkDir;
|
||||
|
||||
pub fn gen(crate_name: &str) {
|
||||
if std::env::var("TAURI_PROTOBUF_PATH").is_err() {
|
||||
log::warn!("TAURI_PROTOBUF_PATH was not set, skip generate ts event");
|
||||
return;
|
||||
}
|
||||
|
||||
let crate_path = std::fs::canonicalize(".").unwrap().as_path().display().to_string();
|
||||
let event_crates = parse_ts_event_files(vec![crate_path]);
|
||||
let event_ast = event_crates.iter().flat_map(parse_event_crate).collect::<Vec<_>>();
|
||||
|
||||
let event_render_ctx = ast_to_event_render_ctx(event_ast.as_ref());
|
||||
let mut render_result = TS_HEADER.to_string();
|
||||
for (index, render_ctx) in event_render_ctx.into_iter().enumerate() {
|
||||
let mut event_template = EventTemplate::new();
|
||||
|
||||
if let Some(content) = event_template.render(render_ctx, index) {
|
||||
render_result.push_str(content.as_ref())
|
||||
}
|
||||
}
|
||||
render_result.push_str(TS_FOOTER);
|
||||
|
||||
let ts_event_folder: PathBuf = [
|
||||
&std::env::var("CARGO_MAKE_WORKING_DIRECTORY").unwrap(),
|
||||
&std::env::var("TAURI_PROTOBUF_PATH").unwrap(),
|
||||
"events",
|
||||
crate_name,
|
||||
]
|
||||
.iter()
|
||||
.collect();
|
||||
|
||||
if !ts_event_folder.as_path().exists() {
|
||||
std::fs::create_dir_all(ts_event_folder.as_path()).unwrap();
|
||||
}
|
||||
|
||||
let event_file = "event";
|
||||
let event_file_ext = "ts";
|
||||
let ts_event_file_path =
|
||||
path_string_with_component(&ts_event_folder, vec![&format!("{}.{}", event_file, event_file_ext)]);
|
||||
println!("cargo:rerun-if-changed={}", ts_event_file_path);
|
||||
|
||||
match std::fs::OpenOptions::new()
|
||||
.create(true)
|
||||
.write(true)
|
||||
.append(false)
|
||||
.truncate(true)
|
||||
.open(&ts_event_file_path)
|
||||
{
|
||||
Ok(ref mut file) => {
|
||||
file.write_all(render_result.as_bytes()).unwrap();
|
||||
File::flush(file).unwrap();
|
||||
}
|
||||
Err(err) => {
|
||||
panic!("Failed to open file: {}, {:?}", ts_event_file_path, err);
|
||||
}
|
||||
}
|
||||
|
||||
let ts_index = path_string_with_component(&ts_event_folder, vec!["index.ts"]);
|
||||
match std::fs::OpenOptions::new()
|
||||
.create(true)
|
||||
.write(true)
|
||||
.append(false)
|
||||
.truncate(true)
|
||||
.open(&ts_index)
|
||||
{
|
||||
Ok(ref mut file) => {
|
||||
let mut export = String::new();
|
||||
export.push_str("// Auto-generated, do not edit \n");
|
||||
export.push_str(&format!("export * from '../../classes/{}';\n", crate_name));
|
||||
export.push_str(&format!("export * from './{}';\n", event_file));
|
||||
file.write_all(export.as_bytes()).unwrap();
|
||||
File::flush(file).unwrap();
|
||||
}
|
||||
Err(err) => {
|
||||
panic!("Failed to open file: {}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TsEventCrate {
|
||||
crate_path: PathBuf,
|
||||
event_files: Vec<String>,
|
||||
}
|
||||
|
||||
impl TsEventCrate {
|
||||
pub fn from_config(config: &CrateConfig) -> Self {
|
||||
TsEventCrate {
|
||||
crate_path: config.crate_path.clone(),
|
||||
event_files: config.flowy_config.event_files.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_ts_event_files(crate_paths: Vec<String>) -> Vec<TsEventCrate> {
|
||||
let mut ts_event_crates: Vec<TsEventCrate> = vec![];
|
||||
crate_paths.iter().for_each(|path| {
|
||||
let crates = WalkDir::new(path)
|
||||
.into_iter()
|
||||
.filter_entry(|e| !is_hidden(e))
|
||||
.filter_map(|e| e.ok())
|
||||
.filter(is_crate_dir)
|
||||
.flat_map(|e| parse_crate_config_from(&e))
|
||||
.map(|crate_config| TsEventCrate::from_config(&crate_config))
|
||||
.collect::<Vec<TsEventCrate>>();
|
||||
ts_event_crates.extend(crates);
|
||||
});
|
||||
ts_event_crates
|
||||
}
|
||||
|
||||
pub fn parse_event_crate(event_crate: &TsEventCrate) -> Vec<EventASTContext> {
|
||||
event_crate
|
||||
.event_files
|
||||
.iter()
|
||||
.flat_map(|event_file| {
|
||||
let file_path = path_string_with_component(&event_crate.crate_path, vec![event_file.as_str()]);
|
||||
|
||||
let file_content = read_file(file_path.as_ref()).unwrap();
|
||||
let ast = syn::parse_file(file_content.as_ref()).expect("Unable to parse file");
|
||||
ast.items
|
||||
.iter()
|
||||
.flat_map(|item| match item {
|
||||
Item::Enum(item_enum) => {
|
||||
let ast_result = ASTResult::new();
|
||||
let attrs = flowy_ast::enum_from_ast(
|
||||
&ast_result,
|
||||
&item_enum.ident,
|
||||
&item_enum.variants,
|
||||
&item_enum.attrs,
|
||||
);
|
||||
ast_result.check().unwrap();
|
||||
attrs
|
||||
.iter()
|
||||
.filter(|attr| !attr.attrs.event_attrs.ignore)
|
||||
.enumerate()
|
||||
.map(|(_index, variant)| EventASTContext::from(&variant.attrs))
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
_ => vec![],
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
})
|
||||
.collect::<Vec<EventASTContext>>()
|
||||
}
|
||||
|
||||
pub fn ast_to_event_render_ctx(ast: &[EventASTContext]) -> Vec<EventRenderContext> {
|
||||
let mut import_objects = HashSet::new();
|
||||
ast.iter().for_each(|event_ast| {
|
||||
if let Some(input) = event_ast.event_input.as_ref() {
|
||||
import_objects.insert(input.get_ident().unwrap().to_string());
|
||||
}
|
||||
if let Some(output) = event_ast.event_output.as_ref() {
|
||||
import_objects.insert(output.get_ident().unwrap().to_string());
|
||||
}
|
||||
});
|
||||
|
||||
ast.iter()
|
||||
.map(|event_ast| {
|
||||
let input_deserializer = event_ast
|
||||
.event_input
|
||||
.as_ref()
|
||||
.map(|event_input| event_input.get_ident().unwrap().to_string());
|
||||
|
||||
let output_deserializer = event_ast
|
||||
.event_output
|
||||
.as_ref()
|
||||
.map(|event_output| event_output.get_ident().unwrap().to_string());
|
||||
|
||||
EventRenderContext {
|
||||
input_deserializer,
|
||||
output_deserializer,
|
||||
error_deserializer: event_ast.event_error.to_string(),
|
||||
event: event_ast.event.to_string(),
|
||||
event_ty: event_ast.event_ty.to_string(),
|
||||
prefix: "pb".to_string(),
|
||||
}
|
||||
})
|
||||
.collect::<Vec<EventRenderContext>>()
|
||||
}
|
||||
|
||||
const TS_HEADER: &str = r#"
|
||||
/// Auto generate. Do not edit
|
||||
import { Ok, Err, Result } from "ts-results";
|
||||
import { invoke } from "@tauri-apps/api/tauri";
|
||||
import * as pb from "../../classes";
|
||||
"#;
|
||||
|
||||
const TS_FOOTER: &str = r#"
|
||||
"#;
|
@ -18,7 +18,7 @@ flowy-document = { path = "../flowy-document", default-features = false }
|
||||
flowy-revision = { path = "../flowy-revision" }
|
||||
flowy-task = { path = "../flowy-task" }
|
||||
|
||||
tracing = { version = "0.1" }
|
||||
tracing = { version = "0.1", features = ["log"] }
|
||||
futures-core = { version = "0.3", default-features = false }
|
||||
bytes = "1.0"
|
||||
tokio = { version = "1", features = ["rt"] }
|
||||
@ -39,6 +39,13 @@ dart = [
|
||||
"flowy-grid/dart",
|
||||
"flowy-document/dart",
|
||||
]
|
||||
ts = [
|
||||
"flowy-user/ts",
|
||||
"flowy-net/ts",
|
||||
"flowy-folder/ts",
|
||||
"flowy-grid/ts",
|
||||
"flowy-document/ts",
|
||||
]
|
||||
rev-sqlite = [
|
||||
"flowy-database",
|
||||
"flowy-user/rev-sqlite",
|
||||
|
@ -59,7 +59,7 @@ impl FlowySDKConfig {
|
||||
FlowySDKConfig {
|
||||
name,
|
||||
root: root.to_owned(),
|
||||
log_filter: crate_log_filter("info".to_owned()),
|
||||
log_filter: create_log_filter("info".to_owned(), vec![]),
|
||||
server_config,
|
||||
document: DocumentConfig::default(),
|
||||
}
|
||||
@ -70,15 +70,18 @@ impl FlowySDKConfig {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn log_filter(mut self, level: &str) -> Self {
|
||||
self.log_filter = crate_log_filter(level.to_owned());
|
||||
pub fn log_filter(mut self, level: &str, with_crates: Vec<String>) -> Self {
|
||||
self.log_filter = create_log_filter(level.to_owned(), with_crates);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
fn crate_log_filter(level: String) -> String {
|
||||
fn create_log_filter(level: String, with_crates: Vec<String>) -> String {
|
||||
let level = std::env::var("RUST_LOG").unwrap_or(level);
|
||||
let mut filters = vec![];
|
||||
let mut filters = with_crates
|
||||
.into_iter()
|
||||
.map(|crate_name| format!("{}={}", crate_name, level))
|
||||
.collect::<Vec<String>>();
|
||||
filters.push(format!("flowy_core={}", level));
|
||||
filters.push(format!("flowy_folder={}", level));
|
||||
filters.push(format!("flowy_user={}", level));
|
||||
@ -330,7 +333,7 @@ fn init_log(config: &FlowySDKConfig) {
|
||||
if !INIT_LOG.load(Ordering::SeqCst) {
|
||||
INIT_LOG.store(true, Ordering::SeqCst);
|
||||
|
||||
let _ = lib_log::Builder::new("flowy-client", &config.root)
|
||||
let _ = lib_log::Builder::new("AppFlowy-Client", &config.root)
|
||||
.env_filter(&config.log_filter)
|
||||
.build();
|
||||
}
|
||||
|
@ -61,4 +61,5 @@ sync = []
|
||||
cloud_sync = ["sync"]
|
||||
rev-sqlite = ["flowy-database"]
|
||||
flowy_unit_test = ["lib-ot/flowy_unit_test", "flowy-revision/flowy_unit_test"]
|
||||
dart = ["flowy-codegen/dart", "dart-notify/dart"]
|
||||
dart = ["flowy-codegen/dart", "dart-notify/dart"]
|
||||
ts = ["flowy-codegen/ts"]
|
||||
|
@ -4,4 +4,7 @@ fn main() {
|
||||
|
||||
#[cfg(feature = "dart")]
|
||||
flowy_codegen::dart_event::gen(crate_name);
|
||||
|
||||
#[cfg(feature = "ts")]
|
||||
flowy_codegen::ts_event::gen(crate_name);
|
||||
}
|
||||
|
@ -28,6 +28,7 @@ serde = ["serde_json"]
|
||||
http_server = ["http-flowy"]
|
||||
db = ["flowy-database", "r2d2"]
|
||||
dart = ["flowy-codegen/dart"]
|
||||
ts = ["flowy-codegen/ts"]
|
||||
|
||||
[build-dependencies]
|
||||
flowy-codegen = { path = "../flowy-codegen", features = ["proto_gen"]}
|
||||
|
@ -52,4 +52,5 @@ sync = []
|
||||
cloud_sync = ["sync"]
|
||||
rev-sqlite = ["flowy-database", "flowy-folder/rev-sqlite"]
|
||||
flowy_unit_test = ["lib-ot/flowy_unit_test", "flowy-revision/flowy_unit_test"]
|
||||
dart = ["flowy-codegen/dart", "dart-notify/dart"]
|
||||
dart = ["flowy-codegen/dart", "dart-notify/dart"]
|
||||
ts = ["flowy-codegen/ts"]
|
||||
|
@ -4,4 +4,7 @@ fn main() {
|
||||
|
||||
#[cfg(feature = "dart")]
|
||||
flowy_codegen::dart_event::gen(crate_name);
|
||||
|
||||
#[cfg(feature = "ts")]
|
||||
flowy_codegen::ts_event::gen(crate_name);
|
||||
}
|
||||
|
@ -59,4 +59,5 @@ flowy-codegen = { path = "../flowy-codegen"}
|
||||
default = []
|
||||
rev-sqlite = ["flowy-database"]
|
||||
dart = ["flowy-codegen/dart", "dart-notify/dart"]
|
||||
ts = ["flowy-codegen/ts"]
|
||||
flowy_unit_test = ["flowy-revision/flowy_unit_test"]
|
@ -4,4 +4,7 @@ fn main() {
|
||||
|
||||
#[cfg(feature = "dart")]
|
||||
flowy_codegen::dart_event::gen(crate_name);
|
||||
|
||||
#[cfg(feature = "ts")]
|
||||
flowy_codegen::ts_event::gen(crate_name);
|
||||
}
|
||||
|
@ -101,7 +101,7 @@ pub(crate) async fn delete_all_sorts_handler(
|
||||
) -> Result<(), FlowyError> {
|
||||
let grid_id: GridIdPB = data.into_inner();
|
||||
let editor = manager.open_grid(grid_id.as_ref()).await?;
|
||||
let _ = editor.delete_all_sorts(grid_id.as_ref()).await?;
|
||||
editor.delete_all_sorts(grid_id.as_ref()).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -16,8 +16,8 @@ impl DateFilterPB {
|
||||
_ => {}
|
||||
}
|
||||
|
||||
let cell_time = NaiveDateTime::from_timestamp(timestamp, 0);
|
||||
let cell_date = cell_time.date();
|
||||
let cell_time = NaiveDateTime::from_timestamp_opt(timestamp, 0);
|
||||
let cell_date = cell_time.map(|time| time.date());
|
||||
match self.timestamp {
|
||||
None => {
|
||||
if self.start.is_none() {
|
||||
@ -28,17 +28,17 @@ impl DateFilterPB {
|
||||
return true;
|
||||
}
|
||||
|
||||
let start_time = NaiveDateTime::from_timestamp(*self.start.as_ref().unwrap(), 0);
|
||||
let start_date = start_time.date();
|
||||
let start_time = NaiveDateTime::from_timestamp_opt(*self.start.as_ref().unwrap(), 0);
|
||||
let start_date = start_time.map(|time| time.date());
|
||||
|
||||
let end_time = NaiveDateTime::from_timestamp(*self.end.as_ref().unwrap(), 0);
|
||||
let end_date = end_time.date();
|
||||
let end_time = NaiveDateTime::from_timestamp_opt(*self.end.as_ref().unwrap(), 0);
|
||||
let end_date = end_time.map(|time| time.date());
|
||||
|
||||
cell_date >= start_date && cell_date <= end_date
|
||||
}
|
||||
Some(timestamp) => {
|
||||
let expected_timestamp = NaiveDateTime::from_timestamp(timestamp, 0);
|
||||
let expected_date = expected_timestamp.date();
|
||||
let expected_timestamp = NaiveDateTime::from_timestamp_opt(timestamp, 0);
|
||||
let expected_date = expected_timestamp.map(|time| time.date());
|
||||
|
||||
// We assume that the cell_timestamp doesn't contain hours, just day.
|
||||
match self.condition {
|
||||
|
@ -54,7 +54,11 @@ impl DateTypeOptionPB {
|
||||
|
||||
fn today_desc_from_timestamp<T: Into<i64>>(&self, timestamp: T) -> DateCellDataPB {
|
||||
let timestamp = timestamp.into();
|
||||
let native = chrono::NaiveDateTime::from_timestamp(timestamp, 0);
|
||||
let native = chrono::NaiveDateTime::from_timestamp_opt(timestamp, 0);
|
||||
if native.is_none() {
|
||||
return DateCellDataPB::default();
|
||||
}
|
||||
let native = native.unwrap();
|
||||
if native.timestamp() == 0 {
|
||||
return DateCellDataPB::default();
|
||||
}
|
||||
@ -163,10 +167,13 @@ impl CellDataChangeset for DateTypeOptionPB {
|
||||
Some(date_timestamp) => match (self.include_time, changeset.time) {
|
||||
(true, Some(time)) => {
|
||||
let time = Some(time.trim().to_uppercase());
|
||||
let native = NaiveDateTime::from_timestamp(date_timestamp, 0);
|
||||
|
||||
let utc = self.utc_date_time_from_native(native);
|
||||
self.timestamp_from_utc_with_time(&utc, &time)?
|
||||
let native = NaiveDateTime::from_timestamp_opt(date_timestamp, 0);
|
||||
if let Some(native) = native {
|
||||
let utc = self.utc_date_time_from_native(native);
|
||||
self.timestamp_from_utc_with_time(&utc, &time)?
|
||||
} else {
|
||||
date_timestamp
|
||||
}
|
||||
}
|
||||
_ => date_timestamp,
|
||||
},
|
||||
|
@ -107,7 +107,7 @@ impl NumberTypeOptionPB {
|
||||
}
|
||||
} else {
|
||||
let draw_numer_string = NUM_REGEX.replace_all(s, "");
|
||||
let strnum = match draw_numer_string.matches(".").count() {
|
||||
let strnum = match draw_numer_string.matches('.').count() {
|
||||
0 | 1 => draw_numer_string.to_string(),
|
||||
_ => match EXTRACT_NUM_REGEX.captures(&draw_numer_string) {
|
||||
Ok(captures) => match captures {
|
||||
|
@ -376,8 +376,7 @@ impl GridViewRevisionEditor {
|
||||
|
||||
pub async fn get_view_setting(&self) -> GridSettingPB {
|
||||
let field_revs = self.delegate.get_field_revs(None).await;
|
||||
let grid_setting = make_grid_setting(&*self.pad.read().await, &field_revs);
|
||||
grid_setting
|
||||
make_grid_setting(&*self.pad.read().await, &field_revs)
|
||||
}
|
||||
|
||||
pub async fn get_all_view_sorts(&self) -> Vec<Arc<SortRevision>> {
|
||||
@ -448,12 +447,11 @@ impl GridViewRevisionEditor {
|
||||
pub async fn delete_all_view_sorts(&self) -> FlowyResult<()> {
|
||||
let all_sorts = self.get_all_view_sorts().await;
|
||||
self.sort_controller.write().await.delete_all_sorts().await;
|
||||
let _ = self
|
||||
.modify(|pad| {
|
||||
let changeset = pad.delete_all_sorts()?;
|
||||
Ok(changeset)
|
||||
})
|
||||
.await?;
|
||||
self.modify(|pad| {
|
||||
let changeset = pad.delete_all_sorts()?;
|
||||
Ok(changeset)
|
||||
})
|
||||
.await?;
|
||||
|
||||
let mut notification = SortChangesetNotificationPB::new(self.view_id.clone());
|
||||
notification.delete_sorts = all_sorts.into_iter().map(|sort| SortPB::from(sort.as_ref())).collect();
|
||||
|
@ -47,5 +47,11 @@ dart = [
|
||||
"flowy-error/dart",
|
||||
]
|
||||
|
||||
ts = [
|
||||
"flowy-codegen/ts",
|
||||
"flowy-user/ts",
|
||||
"flowy-error/ts",
|
||||
]
|
||||
|
||||
[build-dependencies]
|
||||
flowy-codegen = { path = "../flowy-codegen"}
|
||||
|
@ -4,4 +4,7 @@ fn main() {
|
||||
|
||||
#[cfg(feature = "dart")]
|
||||
flowy_codegen::dart_event::gen(crate_name);
|
||||
|
||||
#[cfg(feature = "ts")]
|
||||
flowy_codegen::ts_event::gen(crate_name);
|
||||
}
|
||||
|
@ -75,11 +75,11 @@ where
|
||||
break;
|
||||
}
|
||||
}
|
||||
return if new_operations.is_empty() {
|
||||
if new_operations.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(new_operations)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pair_rev_id_from_revision_pbs(revisions: &[Revision]) -> (i64, i64) {
|
||||
|
@ -38,7 +38,7 @@ impl FlowySDKTest {
|
||||
let server_config = get_client_server_configuration().unwrap();
|
||||
let config = FlowySDKConfig::new(&root_dir(), nanoid!(6), server_config)
|
||||
.with_document_version(document_version)
|
||||
.log_filter("info");
|
||||
.log_filter("info", vec![]);
|
||||
let sdk = std::thread::spawn(|| FlowySDK::new(config)).join().unwrap();
|
||||
std::mem::forget(sdk.dispatcher());
|
||||
Self { inner: sdk }
|
||||
|
@ -47,6 +47,7 @@ rand = "0.8.5"
|
||||
[features]
|
||||
rev-sqlite = ["flowy-database"]
|
||||
dart = ["flowy-codegen/dart", "dart-notify/dart"]
|
||||
ts = ["flowy-codegen/ts"]
|
||||
|
||||
[build-dependencies]
|
||||
flowy-codegen = { path = "../flowy-codegen"}
|
||||
|
@ -4,4 +4,7 @@ fn main() {
|
||||
|
||||
#[cfg(feature = "dart")]
|
||||
flowy_codegen::dart_event::gen(crate_name);
|
||||
|
||||
#[cfg(feature = "ts")]
|
||||
flowy_codegen::ts_event::gen(crate_name);
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ dyn-clone = "1.0"
|
||||
derivative = "2.2.0"
|
||||
serde_json = {version = "1.0", optional = true }
|
||||
serde = { version = "1.0", features = ["derive"], optional = true }
|
||||
serde_repr = { version = "0.1", optional = true }
|
||||
dashmap = "5"
|
||||
|
||||
#optional crate
|
||||
@ -37,5 +38,5 @@ futures-util = "0.3.15"
|
||||
|
||||
[features]
|
||||
default = ["use_protobuf"]
|
||||
use_serde = ["bincode", "serde_json", "serde"]
|
||||
use_serde = ["bincode", "serde_json", "serde", "serde_repr"]
|
||||
use_protobuf= ["protobuf"]
|
||||
|
@ -24,18 +24,18 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "use_serde")]
|
||||
impl<T> ToBytes for T
|
||||
where
|
||||
T: serde::Serialize,
|
||||
{
|
||||
fn into_bytes(self) -> Result<Bytes, DispatchError> {
|
||||
match serde_json::to_string(&self.0) {
|
||||
Ok(s) => Ok(Bytes::from(s)),
|
||||
Err(e) => Err(InternalError::SerializeToBytes(format!("{:?}", e)).into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
// #[cfg(feature = "use_serde")]
|
||||
// impl<T> ToBytes for T
|
||||
// where
|
||||
// T: serde::Serialize,
|
||||
// {
|
||||
// fn into_bytes(self) -> Result<Bytes, DispatchError> {
|
||||
// match serde_json::to_string(&self.0) {
|
||||
// Ok(s) => Ok(Bytes::from(s)),
|
||||
// Err(e) => Err(InternalError::SerializeToBytes(format!("{:?}", e)).into()),
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// From bytes
|
||||
|
||||
@ -65,18 +65,18 @@ where
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "use_serde")]
|
||||
impl<T> AFPluginFromBytes for T
|
||||
where
|
||||
T: serde::de::DeserializeOwned + 'static,
|
||||
{
|
||||
fn parse_from_bytes(bytes: Bytes) -> Result<Self, String> {
|
||||
let s = String::from_utf8_lossy(&bytes);
|
||||
|
||||
match serde_json::from_str::<T>(s.as_ref()) {
|
||||
Ok(data) => Ok(data),
|
||||
Err(e) => Err(format!("{:?}", e)),
|
||||
}
|
||||
}
|
||||
}
|
||||
//
|
||||
// #[cfg(feature = "use_serde")]
|
||||
// impl<T> AFPluginFromBytes for T
|
||||
// where
|
||||
// T: serde::de::DeserializeOwned + 'static,
|
||||
// {
|
||||
// fn parse_from_bytes(bytes: Bytes) -> Result<Self, String> {
|
||||
// let s = String::from_utf8_lossy(&bytes);
|
||||
//
|
||||
// match serde_json::from_str::<T>(s.as_ref()) {
|
||||
// Ok(data) => Ok(data),
|
||||
// Err(e) => Err(format!("{:?}", e)),
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
@ -122,8 +122,7 @@ impl fmt::Display for InternalError {
|
||||
|
||||
impl Error for InternalError {
|
||||
fn as_response(&self) -> AFPluginEventResponse {
|
||||
let error = format!("{}", self).into_bytes();
|
||||
ResponseBuilder::Internal().data(error).build()
|
||||
ResponseBuilder::Err().data(self.to_string()).build()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -37,7 +37,7 @@ pub(crate) fn as_plugin_map(plugins: Vec<AFPlugin>) -> AFPluginMap {
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Hash, Debug, Clone)]
|
||||
pub struct AFPluginEvent(String);
|
||||
pub struct AFPluginEvent(pub String);
|
||||
|
||||
impl<T: Display + Eq + Hash + Debug + Clone> std::convert::From<T> for AFPluginEvent {
|
||||
fn from(t: T) -> Self {
|
||||
|
@ -11,6 +11,15 @@ pub enum Payload {
|
||||
Bytes(Bytes),
|
||||
}
|
||||
|
||||
impl Payload {
|
||||
pub fn to_vec(self) -> Vec<u8> {
|
||||
match self {
|
||||
Payload::None => vec![],
|
||||
Payload::Bytes(bytes) => bytes.to_vec(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for Payload {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
format_payload_print(self, f)
|
||||
|
@ -39,5 +39,4 @@ impl ResponseBuilder {
|
||||
|
||||
static_response!(Ok, StatusCode::Ok);
|
||||
static_response!(Err, StatusCode::Err);
|
||||
static_response!(Internal, StatusCode::Internal);
|
||||
}
|
||||
|
@ -9,11 +9,11 @@ use derivative::*;
|
||||
use std::{convert::TryFrom, fmt, fmt::Formatter};
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "use_serde", derive(serde::Serialize))]
|
||||
#[cfg_attr(feature = "use_serde", derive(serde_repr::Serialize_repr))]
|
||||
#[repr(u8)]
|
||||
pub enum StatusCode {
|
||||
Ok = 0,
|
||||
Err = 1,
|
||||
Internal = 2,
|
||||
}
|
||||
|
||||
// serde user guide: https://serde.rs/field-attrs.html
|
||||
@ -43,7 +43,7 @@ impl AFPluginEventResponse {
|
||||
let data = <AFPluginData<T>>::try_from(self.payload)?;
|
||||
Ok(Ok(data.into_inner()))
|
||||
}
|
||||
StatusCode::Err | StatusCode::Internal => {
|
||||
StatusCode::Err => {
|
||||
let err = <AFPluginData<E>>::try_from(self.payload)?;
|
||||
Ok(Err(err.into_inner()))
|
||||
}
|
||||
|
Reference in New Issue
Block a user