export flutter protobuf model through protobuf.dart

This commit is contained in:
appflowy 2021-07-06 11:04:06 +08:00
parent 751d1dd3d3
commit 2fca817136
14 changed files with 208 additions and 142 deletions

View File

@ -1,3 +1,4 @@
// Auto-generated, do not edit
export 'protobuf/sign_in.pbjson.pb.dart'; export 'protobuf/sign_in.pbjson.pb.dart';
export 'protobuf/sign_in.pb.pb.dart'; export 'protobuf/sign_in.pb.pb.dart';
export 'protobuf/ffi_response.pbjson.pb.dart'; export 'protobuf/ffi_response.pbjson.pb.dart';

8
rust-lib/.idea/.gitignore vendored Normal file
View File

@ -0,0 +1,8 @@
# Default ignored files
/shelf/
/workspace.xml
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml
# Editor-based HTTP Client requests
/httpRequests/

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/rust-lib.iml" filepath="$PROJECT_DIR$/.idea/rust-lib.iml" />
</modules>
</component>
</project>

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="CPP_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/dart-ffi/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/flowy-ast/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/flowy-derive/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/flowy-derive/tests" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/flowy-log/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/flowy-sdk/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/flowy-sdk/tests" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/flowy-sys/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/flowy-sys/tests" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/flowy-user/src" isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/target" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

6
rust-lib/.idea/vcs.xml Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
</component>
</project>

View File

@ -1,3 +1,4 @@
// Auto-generated, do not edit
mod ffi_response; mod ffi_response;
pub use ffi_response::*; pub use ffi_response::*;

View File

@ -1,3 +1,4 @@
// Auto-generated, do not edit
mod sign_up; mod sign_up;
pub use sign_up::*; pub use sign_up::*;

View File

@ -1,13 +1,17 @@
use crate::proto::crate_info::*;
use crate::proto::helper::*; use crate::proto::helper::*;
use crate::proto::template::{EnumTemplate, StructTemplate}; use crate::proto::template::{EnumTemplate, StructTemplate};
use crate::util::*; use crate::util::*;
use fancy_regex::Regex;
use flowy_ast::*; use flowy_ast::*;
use lazy_static::lazy_static;
use std::{fs::File, io::Read, path::Path};
use syn::Item; use syn::Item;
use walkdir::WalkDir; use walkdir::WalkDir;
pub fn parse_crate_protobuf(root: &str) -> Vec<CrateProtoInfo> { pub fn parse_crate_protobuf(root: &str) -> Vec<CrateProtoInfo> {
let domains_info = get_crate_domain_directory(root); let crate_infos = parse_crate_info_from_path(root);
domains_info crate_infos
.into_iter() .into_iter()
.map(|crate_info| { .map(|crate_info| {
let proto_output_dir = crate_info.proto_file_output_dir(); let proto_output_dir = crate_info.proto_file_output_dir();
@ -45,8 +49,6 @@ fn parse_files_protobuf(proto_crate_path: &str, proto_output_dir: &str) -> Vec<F
let ast = let ast =
syn::parse_file(read_file(&path).unwrap().as_ref()).expect("Unable to parse file"); syn::parse_file(read_file(&path).unwrap().as_ref()).expect("Unable to parse file");
let structs = get_ast_structs(&ast); let structs = get_ast_structs(&ast);
// println!("😁 {} - {}", path, file_name);
let proto_file_path = format!("{}/{}.proto", &proto_output_dir, &file_name); let proto_file_path = format!("{}/{}.proto", &proto_output_dir, &file_name);
let mut proto_file_content = parse_or_init_proto_file(proto_file_path.as_ref()); let mut proto_file_content = parse_or_init_proto_file(proto_file_path.as_ref());
@ -154,10 +156,6 @@ pub struct Struct<'a> {
pub fields: Vec<ASTField<'a>>, pub fields: Vec<ASTField<'a>>,
} }
use fancy_regex::Regex;
use lazy_static::lazy_static;
use std::{fs::File, io::Read, path::Path};
lazy_static! { lazy_static! {
static ref SYNTAX_REGEX: Regex = Regex::new("syntax.*;").unwrap(); static ref SYNTAX_REGEX: Regex = Regex::new("syntax.*;").unwrap();
static ref IMPORT_REGEX: Regex = Regex::new("(import\\s).*;").unwrap(); static ref IMPORT_REGEX: Regex = Regex::new("(import\\s).*;").unwrap();

View File

@ -0,0 +1,132 @@
use crate::config::FlowyConfig;
use crate::proto::helper::*;
use std::fs::OpenOptions;
use std::io::Write;
use walkdir::WalkDir;
#[derive(Clone)]
pub struct CrateInfo {
pub crate_folder_name: String,
pub proto_crate_paths: Vec<String>,
pub crate_path: String,
}
pub struct CrateProtoInfo {
pub files: Vec<FileProtoInfo>,
pub inner: CrateInfo,
}
impl CrateInfo {
fn protobuf_crate_name(&self) -> String {
format!("{}/src/protobuf", self.crate_path)
}
pub fn proto_file_output_dir(&self) -> String {
let dir = format!("{}/proto", self.protobuf_crate_name());
create_dir_if_not_exist(dir.as_ref());
dir
}
pub fn proto_struct_output_dir(&self) -> String {
let dir = format!("{}/model", self.protobuf_crate_name());
create_dir_if_not_exist(dir.as_ref());
dir
}
pub fn proto_model_mod_file(&self) -> String {
format!("{}/mod.rs", self.proto_struct_output_dir())
}
}
impl CrateProtoInfo {
pub fn from_crate_info(inner: CrateInfo, files: Vec<FileProtoInfo>) -> Self {
Self { files, inner }
}
pub fn create_crate_mod_file(&self) {
// mod model;
// pub use model::*;
let mod_file_path = format!("{}/mod.rs", self.inner.protobuf_crate_name());
let content = r#"
mod model;
pub use model::*;
"#;
match OpenOptions::new()
.create(true)
.write(true)
.append(false)
.truncate(true)
.open(&mod_file_path)
{
Ok(ref mut file) => {
file.write_all(content.as_bytes()).unwrap();
}
Err(err) => {
panic!("Failed to open protobuf mod file: {}", err);
}
}
}
}
#[derive(Debug)]
pub struct FileProtoInfo {
pub file_name: String,
pub structs: Vec<String>,
pub enums: Vec<String>,
pub generated_content: String,
}
pub fn parse_crate_info_from_path(root: &str) -> Vec<CrateInfo> {
WalkDir::new(root)
.into_iter()
.filter_entry(|e| !is_hidden(e))
.filter_map(|e| e.ok())
.filter(|e| is_crate_dir(e))
.flat_map(|e| {
// Assert e.path().parent() will be the crate dir
let path = e.path().parent().unwrap();
let crate_path = path.to_str().unwrap().to_string();
let crate_folder_name = path.file_stem().unwrap().to_str().unwrap().to_string();
let flowy_config_file = format!("{}/Flowy.toml", crate_path);
if std::path::Path::new(&flowy_config_file).exists() {
let config = FlowyConfig::from_toml_file(flowy_config_file.as_ref());
let crate_path = path.to_str().unwrap().to_string();
let proto_crate_paths = config
.proto_crates
.iter()
.map(|name| format!("{}/{}", crate_path, name))
.collect::<Vec<String>>();
Some(CrateInfo {
crate_folder_name,
proto_crate_paths,
crate_path,
})
} else {
None
}
})
.collect::<Vec<CrateInfo>>()
}
pub struct FlutterProtobufInfo {
package_path: String,
}
impl FlutterProtobufInfo {
pub fn new(root: &str) -> Self {
FlutterProtobufInfo {
package_path: root.to_owned(),
}
}
pub fn model_dir(&self) -> String {
let model_dir = format!("{}/protobuf", self.package_path);
create_dir_if_not_exist(model_dir.as_ref());
model_dir
}
pub fn mod_file_path(&self) -> String {
let mod_file_path = format!("{}/protobuf.dart", self.package_path);
mod_file_path
}
}

View File

@ -1,113 +1,3 @@
use crate::config::*;
use std::fs::OpenOptions;
use std::io::Write;
use walkdir::WalkDir;
#[derive(Clone)]
pub struct CrateInfo {
pub crate_folder_name: String,
pub proto_crate_paths: Vec<String>,
pub crate_path: String,
}
pub struct CrateProtoInfo {
pub files: Vec<FileProtoInfo>,
pub inner: CrateInfo,
}
impl CrateInfo {
fn protobuf_crate_name(&self) -> String {
format!("{}/src/protobuf", self.crate_path)
}
pub fn proto_file_output_dir(&self) -> String {
let dir = format!("{}/proto", self.protobuf_crate_name());
create_dir_if_not_exist(dir.as_ref());
dir
}
pub fn proto_struct_output_dir(&self) -> String {
let dir = format!("{}/model", self.protobuf_crate_name());
create_dir_if_not_exist(dir.as_ref());
dir
}
pub fn proto_model_mod_file(&self) -> String {
format!("{}/mod.rs", self.proto_struct_output_dir())
}
}
impl CrateProtoInfo {
pub fn from_crate_info(inner: CrateInfo, files: Vec<FileProtoInfo>) -> Self {
Self { files, inner }
}
pub fn create_crate_mod_file(&self) {
// mod model;
// pub use model::*;
let mod_file_path = format!("{}/mod.rs", self.inner.protobuf_crate_name());
let content = r#"
mod model;
pub use model::*;
"#;
match OpenOptions::new()
.create(true)
.write(true)
.append(false)
.truncate(true)
.open(&mod_file_path)
{
Ok(ref mut file) => {
file.write_all(content.as_bytes()).unwrap();
}
Err(err) => {
panic!("Failed to open protobuf mod file: {}", err);
}
}
}
}
#[derive(Debug)]
pub struct FileProtoInfo {
pub file_name: String,
pub structs: Vec<String>,
pub enums: Vec<String>,
pub generated_content: String,
}
pub fn get_crate_domain_directory(root: &str) -> Vec<CrateInfo> {
WalkDir::new(root)
.into_iter()
.filter_entry(|e| !is_hidden(e))
.filter_map(|e| e.ok())
.filter(|e| is_crate_dir(e))
.flat_map(|e| {
// Assert e.path().parent() will be the crate dir
let path = e.path().parent().unwrap();
let crate_path = path.to_str().unwrap().to_string();
let crate_folder_name = path.file_stem().unwrap().to_str().unwrap().to_string();
let flowy_config_file = format!("{}/Flowy.toml", crate_path);
if std::path::Path::new(&flowy_config_file).exists() {
let config = FlowyConfig::from_toml_file(flowy_config_file.as_ref());
let crate_path = path.to_str().unwrap().to_string();
let proto_crate_paths = config
.proto_crates
.iter()
.map(|name| format!("{}/{}", crate_path, name))
.collect::<Vec<String>>();
Some(CrateInfo {
crate_folder_name,
proto_crate_paths,
crate_path,
})
} else {
None
}
})
.collect::<Vec<CrateInfo>>()
}
pub fn is_crate_dir(e: &walkdir::DirEntry) -> bool { pub fn is_crate_dir(e: &walkdir::DirEntry) -> bool {
let cargo = e.path().file_stem().unwrap().to_str().unwrap().to_string(); let cargo = e.path().file_stem().unwrap().to_str().unwrap().to_string();
cargo == "Cargo".to_string() cargo == "Cargo".to_string()

View File

@ -1,5 +1,6 @@
mod ast; mod ast;
mod builder; mod builder;
mod crate_info;
mod helper; mod helper;
mod proto_gen; mod proto_gen;
mod template; mod template;

View File

@ -1,10 +1,8 @@
use crate::proto::ast::*; use crate::proto::ast::*;
use crate::proto::crate_info::*;
use crate::proto::helper::*; use crate::proto::helper::*;
use crate::{proto::template::*, util::*}; use crate::{proto::template::*, util::*};
use std::{fs::OpenOptions, io::Write}; use std::{fs::OpenOptions, io::Write};
use std::fs::File;
use walkdir::WalkDir; use walkdir::WalkDir;
pub struct ProtoGen { pub struct ProtoGen {
@ -18,15 +16,15 @@ impl ProtoGen {
let crate_proto_infos = parse_crate_protobuf(self.rust_source_dir.as_ref()); let crate_proto_infos = parse_crate_protobuf(self.rust_source_dir.as_ref());
write_proto_files(&crate_proto_infos); write_proto_files(&crate_proto_infos);
// FIXME: ignore unchanged file to reduce time cost
run_rust_protoc(&crate_proto_infos); run_rust_protoc(&crate_proto_infos);
write_rust_crate_mod_file(&crate_proto_infos); write_rust_crate_mod_file(&crate_proto_infos);
let package_root = self.flutter_package_lib.as_ref();
let model_dir = format!("{}/protobuf", package_root);
run_flutter_protoc(&crate_proto_infos, model_dir.as_ref());
write_flutter_crate_mod_file(package_root, model_dir.as_ref());
write_derive_meta(&crate_proto_infos, self.derive_meta_dir.as_ref()); write_derive_meta(&crate_proto_infos, self.derive_meta_dir.as_ref());
// FIXME: ignore unchanged file to reduce time cost
let flutter_package = FlutterProtobufInfo::new(self.flutter_package_lib.as_ref());
run_flutter_protoc(&crate_proto_infos, &flutter_package);
write_flutter_protobuf_package_mod_file(&flutter_package);
} }
} }
@ -74,8 +72,9 @@ fn write_rust_crate_mod_file(crate_infos: &Vec<CrateProtoInfo>) {
} }
} }
fn write_flutter_crate_mod_file(package_root: &str, model_dir: &str) { fn write_flutter_protobuf_package_mod_file(package_info: &FlutterProtobufInfo) {
let mod_path = format!("{}/protobuf.dart", package_root); let mod_path = package_info.mod_file_path();
let model_dir = package_info.model_dir();
match OpenOptions::new() match OpenOptions::new()
.create(true) .create(true)
.write(true) .write(true)
@ -87,7 +86,7 @@ fn write_flutter_crate_mod_file(package_root: &str, model_dir: &str) {
let mut mod_file_content = String::new(); let mut mod_file_content = String::new();
mod_file_content.push_str("// Auto-generated, do not edit \n"); mod_file_content.push_str("// Auto-generated, do not edit \n");
walk_dir( walk_dir(
model_dir, model_dir.as_ref(),
|e| e.file_type().is_dir() == false, |e| e.file_type().is_dir() == false,
|_, name| { |_, name| {
let c = format!("export 'protobuf/{}.pb.dart';\n", &name); let c = format!("export 'protobuf/{}.pb.dart';\n", &name);
@ -125,8 +124,8 @@ fn run_rust_protoc(crate_infos: &Vec<CrateProtoInfo>) {
} }
} }
fn run_flutter_protoc(crate_infos: &Vec<CrateProtoInfo>, model_dir: &str) { fn run_flutter_protoc(crate_infos: &Vec<CrateProtoInfo>, package_info: &FlutterProtobufInfo) {
create_dir_if_not_exist(model_dir.as_ref()); let model_dir = package_info.model_dir();
for crate_info in crate_infos { for crate_info in crate_infos {
let proto_path = crate_info.inner.proto_file_output_dir(); let proto_path = crate_info.inner.proto_file_output_dir();
walk_dir( walk_dir(

View File

@ -1,4 +1,4 @@
use crate::proto::helper::{CrateProtoInfo, FileProtoInfo}; use crate::proto::crate_info::{CrateProtoInfo, FileProtoInfo};
use crate::util::{get_tera, read_file}; use crate::util::{get_tera, read_file};
use std::fs::OpenOptions; use std::fs::OpenOptions;
use std::io::Write; use std::io::Write;

View File

@ -40,15 +40,16 @@ pub fn save_content_to_file_with_diff_prompt(content: &str, output_file: &str, f
}; };
if new_content != old_content { if new_content != old_content {
print_diff(old_content.clone(), new_content.clone()); print_diff(old_content.clone(), new_content.clone());
if force_write { write_to_file()
write_to_file() // if force_write {
} else { // write_to_file()
if Confirm::new().with_prompt("Override?").interact().unwrap() { // } else {
write_to_file() // if Confirm::new().with_prompt("Override?").interact().unwrap() {
} else { // write_to_file()
log::info!("never mind then :("); // } else {
} // log::info!("never mind then :(");
} // }
// }
} }
} else { } else {
match OpenOptions::new() match OpenOptions::new()