mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
config event template
This commit is contained in:
parent
f1a229002f
commit
569da533a1
10
.idea/inspectionProfiles/Project_Default.xml
Normal file
10
.idea/inspectionProfiles/Project_Default.xml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<component name="InspectionProjectProfileManager">
|
||||||
|
<profile version="1.0">
|
||||||
|
<option name="myName" value="Project Default" />
|
||||||
|
<inspection_tool class="DuplicatedCode" enabled="true" level="WEAK WARNING" enabled_by_default="true">
|
||||||
|
<Languages>
|
||||||
|
<language minSize="46" name="Rust" />
|
||||||
|
</Languages>
|
||||||
|
</inspection_tool>
|
||||||
|
</profile>
|
||||||
|
</component>
|
@ -1 +1,2 @@
|
|||||||
proto_crates = ["src/model"]
|
proto_crates = ["src/model"]
|
||||||
|
event_files = []
|
@ -255,14 +255,22 @@ pub enum Default {
|
|||||||
Path(syn::ExprPath),
|
Path(syn::ExprPath),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct EventAttrs {
|
||||||
|
input: Option<syn::Path>,
|
||||||
|
output: Option<syn::Path>,
|
||||||
|
pub ignore: bool,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct ASTEnumAttrVariant {
|
pub struct ASTEnumAttrVariant {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub value: String,
|
pub value: String,
|
||||||
|
pub event_attrs: EventAttrs,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ASTEnumAttrVariant {
|
impl ASTEnumAttrVariant {
|
||||||
pub fn from_ast(_cx: &Ctxt, variant: &syn::Variant) -> Self {
|
pub fn from_ast(ctxt: &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() {
|
||||||
@ -278,8 +286,70 @@ impl ASTEnumAttrVariant {
|
|||||||
_ => {},
|
_ => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ASTEnumAttrVariant { name, value }
|
let mut event_attrs = EventAttrs {
|
||||||
|
input: None,
|
||||||
|
output: None,
|
||||||
|
ignore: false,
|
||||||
|
};
|
||||||
|
variant.attrs.iter().for_each(|attr| match get_meta_items(ctxt, attr) {
|
||||||
|
Ok(meta_items) => {
|
||||||
|
for meta_item in meta_items {
|
||||||
|
match &meta_item {
|
||||||
|
Meta(NameValue(name_value)) => {
|
||||||
|
if name_value.path == EVENT_INPUT {
|
||||||
|
if let syn::Lit::Str(s) = &name_value.lit {
|
||||||
|
let input_type = parse_lit_str(s)
|
||||||
|
.map_err(|_| {
|
||||||
|
ctxt.error_spanned_by(
|
||||||
|
s,
|
||||||
|
format!("failed to parse request deserializer {:?}", s.value()),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
event_attrs.input = Some(input_type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if name_value.path == EVENT_OUTPUT {
|
||||||
|
if let syn::Lit::Str(s) = &name_value.lit {
|
||||||
|
let output_type = parse_lit_str(s)
|
||||||
|
.map_err(|_| {
|
||||||
|
ctxt.error_spanned_by(
|
||||||
|
s,
|
||||||
|
format!("failed to parse response deserializer {:?}", s.value()),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
event_attrs.output = Some(output_type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Meta(Path(word)) => {
|
||||||
|
if word == EVENT_IGNORE && attr.path == EVENT {
|
||||||
|
event_attrs.ignore = true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Lit(s) => {
|
||||||
|
ctxt.error_spanned_by(s, "unexpected type in cqrs container attribute");
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
ctxt.error_spanned_by(meta_item, "unexpected type in cqrs container attribute");
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(_) => {},
|
||||||
|
});
|
||||||
|
ASTEnumAttrVariant {
|
||||||
|
name,
|
||||||
|
value,
|
||||||
|
event_attrs,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn event_input(&self) -> Option<syn::Path> { self.event_attrs.input.clone() }
|
||||||
|
|
||||||
|
pub fn event_output(&self) -> Option<syn::Path> { self.event_attrs.output.clone() }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_meta_items(cx: &Ctxt, attr: &syn::Attribute) -> Result<Vec<syn::NestedMeta>, ()> {
|
pub fn get_meta_items(cx: &Ctxt, attr: &syn::Attribute) -> Result<Vec<syn::NestedMeta>, ()> {
|
||||||
|
35
rust-lib/flowy-ast/src/event_ast.rs
Normal file
35
rust-lib/flowy-ast/src/event_ast.rs
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
use crate::ASTEnumAttrVariant;
|
||||||
|
|
||||||
|
pub struct EventASTContext {
|
||||||
|
pub event: syn::Ident,
|
||||||
|
pub event_ty: syn::Ident,
|
||||||
|
pub event_request_struct: syn::Ident,
|
||||||
|
pub event_input: Option<syn::Path>,
|
||||||
|
pub event_output: Option<syn::Path>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EventASTContext {
|
||||||
|
pub fn from(variant: &ASTEnumAttrVariant) -> EventASTContext {
|
||||||
|
let command_name = variant.name.clone();
|
||||||
|
if command_name.is_empty() {
|
||||||
|
panic!("Invalid command name: {}", variant.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
let event = format_ident!("{}", &command_name);
|
||||||
|
let splits = command_name.split("_").collect::<Vec<&str>>();
|
||||||
|
|
||||||
|
let event_ty = format_ident!("UserEvent");
|
||||||
|
let event_request_struct = format_ident!("{}Event", &splits.join(""));
|
||||||
|
|
||||||
|
let event_input = variant.event_input();
|
||||||
|
let event_output = variant.event_output();
|
||||||
|
|
||||||
|
EventASTContext {
|
||||||
|
event,
|
||||||
|
event_ty,
|
||||||
|
event_request_struct,
|
||||||
|
event_input,
|
||||||
|
event_output,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -7,9 +7,10 @@ extern crate quote;
|
|||||||
mod ast;
|
mod ast;
|
||||||
mod attr;
|
mod attr;
|
||||||
mod ctxt;
|
mod ctxt;
|
||||||
|
|
||||||
|
pub mod event_ast;
|
||||||
pub mod symbol;
|
pub mod symbol;
|
||||||
pub mod ty_ext;
|
pub mod ty_ext;
|
||||||
|
|
||||||
pub use self::{symbol::*, ty_ext::*};
|
pub use self::{symbol::*, ty_ext::*};
|
||||||
pub use ast::*;
|
pub use ast::*;
|
||||||
pub use attr::*;
|
pub use attr::*;
|
||||||
|
@ -14,6 +14,11 @@ pub const SKIP_SERIALIZING: Symbol = Symbol("skip_serializing"); //#[pb(skip_ser
|
|||||||
pub const PB_STRUCT: Symbol = Symbol("struct"); //#[pb(struct="some struct")]
|
pub const PB_STRUCT: Symbol = Symbol("struct"); //#[pb(struct="some struct")]
|
||||||
pub const PB_ENUM: Symbol = Symbol("enum"); //#[pb(enum="some enum")]
|
pub const PB_ENUM: Symbol = Symbol("enum"); //#[pb(enum="some enum")]
|
||||||
|
|
||||||
|
pub const EVENT_INPUT: Symbol = Symbol("input");
|
||||||
|
pub const EVENT_OUTPUT: Symbol = Symbol("output");
|
||||||
|
pub const EVENT_IGNORE: Symbol = Symbol("ignore");
|
||||||
|
pub const EVENT: Symbol = Symbol("event");
|
||||||
|
|
||||||
impl PartialEq<Symbol> for Ident {
|
impl PartialEq<Symbol> for Ident {
|
||||||
fn eq(&self, word: &Symbol) -> bool { self == word.0 }
|
fn eq(&self, word: &Symbol) -> bool { self == word.0 }
|
||||||
}
|
}
|
||||||
|
@ -1,2 +1,3 @@
|
|||||||
|
|
||||||
proto_crates = ["src/domain"]
|
proto_crates = ["src/domain"]
|
||||||
|
event_files = ["src/module.rs"]
|
@ -3,6 +3,7 @@ use std::fs;
|
|||||||
#[derive(serde::Deserialize)]
|
#[derive(serde::Deserialize)]
|
||||||
pub struct FlowyConfig {
|
pub struct FlowyConfig {
|
||||||
pub proto_crates: Vec<String>,
|
pub proto_crates: Vec<String>,
|
||||||
|
pub event_files: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FlowyConfig {
|
impl FlowyConfig {
|
||||||
|
122
scripts/flowy-tool/src/dart_event/dart_event.rs
Normal file
122
scripts/flowy-tool/src/dart_event/dart_event.rs
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
use super::event_template::*;
|
||||||
|
|
||||||
|
use crate::util::*;
|
||||||
|
use flowy_ast::{event_ast::*, *};
|
||||||
|
use syn::Item;
|
||||||
|
use walkdir::WalkDir;
|
||||||
|
|
||||||
|
pub struct DartEventCodeGen {
|
||||||
|
pub rust_source: String,
|
||||||
|
pub output_dir: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DartEventCodeGen {
|
||||||
|
pub fn gen(&self) {
|
||||||
|
let event_crates = parse_dart_event_files(self.rust_source.as_ref());
|
||||||
|
let event_ast = event_crates
|
||||||
|
.iter()
|
||||||
|
.map(|event_crate| parse_event_crate(event_crate))
|
||||||
|
.flatten()
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let event_render_ctx = ast_to_event_render_ctx(event_ast.as_ref());
|
||||||
|
|
||||||
|
let mut render_result = String::new();
|
||||||
|
for (index, render_ctx) in event_render_ctx.into_iter().enumerate() {
|
||||||
|
let mut event_template = EventTemplate::new();
|
||||||
|
|
||||||
|
match event_template.render(render_ctx, index) {
|
||||||
|
Some(content) => render_result.push_str(content.as_ref()),
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
save_content_to_file_with_diff_prompt(
|
||||||
|
render_result.as_ref(),
|
||||||
|
self.output_dir.as_str(),
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct DartEventCrate {
|
||||||
|
crate_path: String,
|
||||||
|
crate_name: String,
|
||||||
|
event_files: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DartEventCrate {
|
||||||
|
pub fn from_config(config: &CrateConfig) -> Self {
|
||||||
|
DartEventCrate {
|
||||||
|
crate_path: config.crate_path.clone(),
|
||||||
|
crate_name: config.folder_name.clone(),
|
||||||
|
event_files: config.flowy_config.event_files.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_dart_event_files(root: &str) -> Vec<DartEventCrate> {
|
||||||
|
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| parse_crate_config_from(&e))
|
||||||
|
.map(|crate_config| DartEventCrate::from_config(&crate_config))
|
||||||
|
.collect::<Vec<DartEventCrate>>()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_event_crate(event_crate: &DartEventCrate) -> Vec<EventASTContext> {
|
||||||
|
event_crate
|
||||||
|
.event_files
|
||||||
|
.iter()
|
||||||
|
.map(|event_file| {
|
||||||
|
let file_path = format!("{}/{}", event_crate.crate_path, event_file);
|
||||||
|
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()
|
||||||
|
.map(|item| match item {
|
||||||
|
Item::Enum(item_enum) => {
|
||||||
|
let ctxt = Ctxt::new();
|
||||||
|
let attrs = flowy_ast::enum_from_ast(&ctxt, &item_enum.variants);
|
||||||
|
ctxt.check().unwrap();
|
||||||
|
attrs
|
||||||
|
.iter()
|
||||||
|
.filter(|attr| attr.attrs.event_attrs.ignore == false)
|
||||||
|
.enumerate()
|
||||||
|
.map(|(_index, attr)| EventASTContext::from(&attr.attrs))
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
}
|
||||||
|
_ => vec![],
|
||||||
|
})
|
||||||
|
.flatten()
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
})
|
||||||
|
.flatten()
|
||||||
|
.collect::<Vec<EventASTContext>>()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ast_to_event_render_ctx(ast: &Vec<EventASTContext>) -> Vec<EventRenderContext> {
|
||||||
|
ast.iter()
|
||||||
|
.map(|event_ast| EventRenderContext {
|
||||||
|
input_deserializer: event_ast
|
||||||
|
.event_input
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.get_ident()
|
||||||
|
.unwrap()
|
||||||
|
.to_string(),
|
||||||
|
output_deserializer: event_ast
|
||||||
|
.event_output
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.get_ident()
|
||||||
|
.unwrap()
|
||||||
|
.to_string(),
|
||||||
|
event: event_ast.event.to_string(),
|
||||||
|
event_ty: event_ast.event_ty.to_string(),
|
||||||
|
})
|
||||||
|
.collect::<Vec<EventRenderContext>>()
|
||||||
|
}
|
25
scripts/flowy-tool/src/dart_event/event_template.rs
Normal file
25
scripts/flowy-tool/src/dart_event/event_template.rs
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
use tera::Context;
|
||||||
|
|
||||||
|
pub struct EventTemplate {
|
||||||
|
tera_context: Context,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct EventRenderContext {
|
||||||
|
pub input_deserializer: String,
|
||||||
|
pub output_deserializer: String,
|
||||||
|
pub event: String,
|
||||||
|
pub event_ty: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
impl EventTemplate {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
return EventTemplate {
|
||||||
|
tera_context: Context::new(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render(&mut self, _render_context: EventRenderContext, _index: usize) -> Option<String> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
4
scripts/flowy-tool/src/dart_event/mod.rs
Normal file
4
scripts/flowy-tool/src/dart_event/mod.rs
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
mod dart_event;
|
||||||
|
mod event_template;
|
||||||
|
|
||||||
|
pub use dart_event::*;
|
@ -1,4 +1,5 @@
|
|||||||
mod config;
|
mod config;
|
||||||
|
mod dart_event;
|
||||||
mod proto;
|
mod proto;
|
||||||
mod util;
|
mod util;
|
||||||
|
|
||||||
@ -22,6 +23,17 @@ fn main() {
|
|||||||
.build()
|
.build()
|
||||||
.gen();
|
.gen();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(ref matches) = matches.subcommand_matches("dart-event") {
|
||||||
|
let rust_source = matches.value_of("rust_source").unwrap().to_string();
|
||||||
|
let output_dir = matches.value_of("output").unwrap().to_string();
|
||||||
|
|
||||||
|
let code_gen = dart_event::DartEventCodeGen {
|
||||||
|
rust_source,
|
||||||
|
output_dir,
|
||||||
|
};
|
||||||
|
code_gen.gen();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn app<'a, 'b>() -> App<'a, 'b> {
|
pub fn app<'a, 'b>() -> App<'a, 'b> {
|
||||||
@ -49,6 +61,21 @@ pub fn app<'a, 'b>() -> App<'a, 'b> {
|
|||||||
.long("flutter_package_lib")
|
.long("flutter_package_lib")
|
||||||
.value_name("DIRECTORY"),
|
.value_name("DIRECTORY"),
|
||||||
),
|
),
|
||||||
|
)
|
||||||
|
.subcommand(
|
||||||
|
App::new("dart-event")
|
||||||
|
.about("Generate the codes that sending events from rust ast")
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("rust_source")
|
||||||
|
.long("rust_source")
|
||||||
|
.value_name("DIRECTORY")
|
||||||
|
.help("Directory of the cargo workspace"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("output")
|
||||||
|
.long("output")
|
||||||
|
.value_name("DIRECTORY"),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
app
|
app
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
use crate::proto::crate_info::*;
|
use crate::proto::proto_info::*;
|
||||||
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 fancy_regex::Regex;
|
||||||
@ -16,19 +15,19 @@ pub fn parse_crate_protobuf(root: &str) -> Vec<CrateProtoInfo> {
|
|||||||
.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();
|
||||||
let files = crate_info
|
let files = crate_info
|
||||||
.proto_crate_paths
|
.proto_paths
|
||||||
.iter()
|
.iter()
|
||||||
.map(|proto_crate_path| parse_files_protobuf(proto_crate_path, &proto_output_dir))
|
.map(|proto_crate_path| parse_files_protobuf(proto_crate_path, &proto_output_dir))
|
||||||
.flatten()
|
.flatten()
|
||||||
.collect::<Vec<FileProtoInfo>>();
|
.collect::<Vec<ProtoFile>>();
|
||||||
|
|
||||||
CrateProtoInfo::from_crate_info(crate_info, files)
|
CrateProtoInfo::from_crate_info(crate_info, files)
|
||||||
})
|
})
|
||||||
.collect::<Vec<CrateProtoInfo>>()
|
.collect::<Vec<CrateProtoInfo>>()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_files_protobuf(proto_crate_path: &str, proto_output_dir: &str) -> Vec<FileProtoInfo> {
|
fn parse_files_protobuf(proto_crate_path: &str, proto_output_dir: &str) -> Vec<ProtoFile> {
|
||||||
let mut gen_proto_vec: Vec<FileProtoInfo> = vec![];
|
let mut gen_proto_vec: Vec<ProtoFile> = vec![];
|
||||||
// file_stem https://doc.rust-lang.org/std/path/struct.Path.html#method.file_stem
|
// file_stem https://doc.rust-lang.org/std/path/struct.Path.html#method.file_stem
|
||||||
for (path, file_name) in WalkDir::new(proto_crate_path)
|
for (path, file_name) in WalkDir::new(proto_crate_path)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
@ -78,7 +77,7 @@ fn parse_files_protobuf(proto_crate_path: &str, proto_output_dir: &str) -> Vec<F
|
|||||||
});
|
});
|
||||||
|
|
||||||
if !enums.is_empty() || !structs.is_empty() {
|
if !enums.is_empty() || !structs.is_empty() {
|
||||||
let info = FileProtoInfo {
|
let info = ProtoFile {
|
||||||
file_name: file_name.clone(),
|
file_name: file_name.clone(),
|
||||||
structs: structs.iter().map(|s| s.name.clone()).collect(),
|
structs: structs.iter().map(|s| s.name.clone()).collect(),
|
||||||
enums: enums.iter().map(|e| e.name.clone()).collect(),
|
enums: enums.iter().map(|e| e.name.clone()).collect(),
|
||||||
|
@ -1,26 +0,0 @@
|
|||||||
pub fn is_crate_dir(e: &walkdir::DirEntry) -> bool {
|
|
||||||
let cargo = e.path().file_stem().unwrap().to_str().unwrap().to_string();
|
|
||||||
cargo == "Cargo".to_string()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_proto_file(e: &walkdir::DirEntry) -> bool {
|
|
||||||
if e.path().extension().is_none() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
let ext = e.path().extension().unwrap().to_str().unwrap().to_string();
|
|
||||||
ext == "proto".to_string()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_hidden(entry: &walkdir::DirEntry) -> bool {
|
|
||||||
entry
|
|
||||||
.file_name()
|
|
||||||
.to_str()
|
|
||||||
.map(|s| s.starts_with("."))
|
|
||||||
.unwrap_or(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn create_dir_if_not_exist(dir: &str) {
|
|
||||||
if !std::path::Path::new(&dir).exists() {
|
|
||||||
std::fs::create_dir_all(&dir).unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,8 +1,7 @@
|
|||||||
mod ast;
|
mod ast;
|
||||||
mod builder;
|
mod builder;
|
||||||
mod crate_info;
|
|
||||||
mod helper;
|
|
||||||
mod proto_gen;
|
mod proto_gen;
|
||||||
|
mod proto_info;
|
||||||
mod template;
|
mod template;
|
||||||
|
|
||||||
pub use builder::*;
|
pub use builder::*;
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
use crate::proto::ast::*;
|
use crate::proto::ast::*;
|
||||||
use crate::proto::crate_info::*;
|
use crate::proto::proto_info::*;
|
||||||
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 walkdir::WalkDir;
|
|
||||||
|
|
||||||
pub struct ProtoGen {
|
pub struct ProtoGen {
|
||||||
pub(crate) rust_source_dir: String,
|
pub(crate) rust_source_dir: String,
|
||||||
@ -77,7 +75,7 @@ fn write_flutter_protobuf_package_mod_file(
|
|||||||
package_info: &FlutterProtobufInfo,
|
package_info: &FlutterProtobufInfo,
|
||||||
) {
|
) {
|
||||||
let mod_path = package_info.mod_file_path();
|
let mod_path = package_info.mod_file_path();
|
||||||
let model_dir = package_info.model_dir();
|
let _model_dir = package_info.model_dir();
|
||||||
match OpenOptions::new()
|
match OpenOptions::new()
|
||||||
.create(true)
|
.create(true)
|
||||||
.write(true)
|
.write(true)
|
||||||
@ -90,7 +88,7 @@ fn write_flutter_protobuf_package_mod_file(
|
|||||||
mod_file_content.push_str("// Auto-generated, do not edit \n");
|
mod_file_content.push_str("// Auto-generated, do not edit \n");
|
||||||
|
|
||||||
for crate_info in crate_infos {
|
for crate_info in crate_infos {
|
||||||
let mod_path = crate_info.inner.proto_model_mod_file();
|
let _mod_path = crate_info.inner.proto_model_mod_file();
|
||||||
walk_dir(
|
walk_dir(
|
||||||
crate_info.inner.proto_file_output_dir().as_ref(),
|
crate_info.inner.proto_file_output_dir().as_ref(),
|
||||||
|e| e.file_type().is_dir() == false,
|
|e| e.file_type().is_dir() == false,
|
||||||
@ -151,23 +149,3 @@ fn run_flutter_protoc(crate_infos: &Vec<CrateProtoInfo>, package_info: &FlutterP
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn walk_dir<F1, F2>(dir: &str, filter: F2, mut path_and_name: F1)
|
|
||||||
where
|
|
||||||
F1: FnMut(String, String),
|
|
||||||
F2: Fn(&walkdir::DirEntry) -> bool,
|
|
||||||
{
|
|
||||||
for (path, name) in WalkDir::new(dir)
|
|
||||||
.into_iter()
|
|
||||||
.filter_map(|e| e.ok())
|
|
||||||
.filter(|e| filter(e))
|
|
||||||
.map(|e| {
|
|
||||||
(
|
|
||||||
e.path().to_str().unwrap().to_string(),
|
|
||||||
e.path().file_stem().unwrap().to_str().unwrap().to_string(),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
{
|
|
||||||
path_and_name(path, name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,45 +1,15 @@
|
|||||||
use crate::config::FlowyConfig;
|
use crate::util::*;
|
||||||
use crate::proto::helper::*;
|
|
||||||
use std::fs::OpenOptions;
|
use std::fs::OpenOptions;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use walkdir::WalkDir;
|
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 struct CrateProtoInfo {
|
||||||
pub files: Vec<FileProtoInfo>,
|
pub files: Vec<ProtoFile>,
|
||||||
pub inner: CrateInfo,
|
pub inner: ProtobufCrate,
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
impl CrateProtoInfo {
|
||||||
pub fn from_crate_info(inner: CrateInfo, files: Vec<FileProtoInfo>) -> Self {
|
pub fn from_crate_info(inner: ProtobufCrate, files: Vec<ProtoFile>) -> Self {
|
||||||
Self { files, inner }
|
Self { files, inner }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,45 +38,61 @@ pub use model::*;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct ProtobufCrate {
|
||||||
|
pub folder_name: String,
|
||||||
|
pub proto_paths: Vec<String>,
|
||||||
|
pub crate_path: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ProtobufCrate {
|
||||||
|
pub fn from_config(config: CrateConfig) -> Self {
|
||||||
|
let proto_paths = config.proto_paths();
|
||||||
|
ProtobufCrate {
|
||||||
|
folder_name: config.folder_name,
|
||||||
|
proto_paths,
|
||||||
|
crate_path: config.crate_path,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct FileProtoInfo {
|
pub struct ProtoFile {
|
||||||
pub file_name: String,
|
pub file_name: String,
|
||||||
pub structs: Vec<String>,
|
pub structs: Vec<String>,
|
||||||
pub enums: Vec<String>,
|
pub enums: Vec<String>,
|
||||||
pub generated_content: String,
|
pub generated_content: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_crate_info_from_path(root: &str) -> Vec<CrateInfo> {
|
pub fn parse_crate_info_from_path(root: &str) -> Vec<ProtobufCrate> {
|
||||||
WalkDir::new(root)
|
WalkDir::new(root)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_entry(|e| !is_hidden(e))
|
.filter_entry(|e| !is_hidden(e))
|
||||||
.filter_map(|e| e.ok())
|
.filter_map(|e| e.ok())
|
||||||
.filter(|e| is_crate_dir(e))
|
.filter(|e| is_crate_dir(e))
|
||||||
.flat_map(|e| {
|
.flat_map(|e| parse_crate_config_from(&e))
|
||||||
// Assert e.path().parent() will be the crate dir
|
.map(|crate_config| ProtobufCrate::from_config(crate_config))
|
||||||
let path = e.path().parent().unwrap();
|
.collect::<Vec<ProtobufCrate>>()
|
||||||
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 {
|
pub struct FlutterProtobufInfo {
|
@ -1,4 +1,4 @@
|
|||||||
use crate::proto::crate_info::{CrateProtoInfo, FileProtoInfo};
|
use crate::proto::proto_info::{CrateProtoInfo, ProtoFile};
|
||||||
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;
|
||||||
@ -40,7 +40,7 @@ pub fn write_derive_meta(crate_infos: &Vec<CrateProtoInfo>, derive_meta_dir: &st
|
|||||||
.iter()
|
.iter()
|
||||||
.map(|ref crate_info| &crate_info.files)
|
.map(|ref crate_info| &crate_info.files)
|
||||||
.flatten()
|
.flatten()
|
||||||
.collect::<Vec<&FileProtoInfo>>();
|
.collect::<Vec<&ProtoFile>>();
|
||||||
|
|
||||||
let structs: Vec<String> = file_proto_infos
|
let structs: Vec<String> = file_proto_infos
|
||||||
.iter()
|
.iter()
|
||||||
|
38
scripts/flowy-tool/src/util/crate_config.rs
Normal file
38
scripts/flowy-tool/src/util/crate_config.rs
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
use crate::config::FlowyConfig;
|
||||||
|
|
||||||
|
pub struct CrateConfig {
|
||||||
|
pub(crate) crate_path: String,
|
||||||
|
pub(crate) folder_name: String,
|
||||||
|
pub(crate) flowy_config: FlowyConfig,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CrateConfig {
|
||||||
|
pub fn proto_paths(&self) -> Vec<String> {
|
||||||
|
let proto_paths = self
|
||||||
|
.flowy_config
|
||||||
|
.proto_crates
|
||||||
|
.iter()
|
||||||
|
.map(|name| format!("{}/{}", self.crate_path, name))
|
||||||
|
.collect::<Vec<String>>();
|
||||||
|
proto_paths
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_crate_config_from(entry: &walkdir::DirEntry) -> Option<CrateConfig> {
|
||||||
|
let path = entry.path().parent().unwrap();
|
||||||
|
let crate_path = path.to_str().unwrap().to_string();
|
||||||
|
let folder_name = path.file_stem().unwrap().to_str().unwrap().to_string();
|
||||||
|
let config_path = format!("{}/Flowy.toml", crate_path);
|
||||||
|
|
||||||
|
if std::path::Path::new(&config_path).exists() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let flowy_config = FlowyConfig::from_toml_file(config_path.as_ref());
|
||||||
|
|
||||||
|
Some(CrateConfig {
|
||||||
|
crate_path,
|
||||||
|
folder_name,
|
||||||
|
flowy_config,
|
||||||
|
})
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
use console::Style;
|
use console::Style;
|
||||||
use dialoguer::Confirm;
|
|
||||||
use similar::{ChangeTag, TextDiff};
|
use similar::{ChangeTag, TextDiff};
|
||||||
use std::{
|
use std::{
|
||||||
fs::{File, OpenOptions},
|
fs::{File, OpenOptions},
|
||||||
@ -7,6 +7,7 @@ use std::{
|
|||||||
path::Path,
|
path::Path,
|
||||||
};
|
};
|
||||||
use tera::Tera;
|
use tera::Tera;
|
||||||
|
use walkdir::WalkDir;
|
||||||
|
|
||||||
pub fn read_file(path: &str) -> Option<String> {
|
pub fn read_file(path: &str) -> Option<String> {
|
||||||
let mut file = File::open(path).expect("Unable to open file");
|
let mut file = File::open(path).expect("Unable to open file");
|
||||||
@ -20,7 +21,7 @@ pub fn read_file(path: &str) -> Option<String> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn save_content_to_file_with_diff_prompt(content: &str, output_file: &str, force_write: bool) {
|
pub fn save_content_to_file_with_diff_prompt(content: &str, output_file: &str, _force_write: bool) {
|
||||||
if Path::new(output_file).exists() {
|
if Path::new(output_file).exists() {
|
||||||
let old_content = read_file(output_file).unwrap();
|
let old_content = read_file(output_file).unwrap();
|
||||||
let new_content = content.to_owned();
|
let new_content = content.to_owned();
|
||||||
@ -106,3 +107,50 @@ pub fn get_tera(directory: &str) -> Tera {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_crate_dir(e: &walkdir::DirEntry) -> bool {
|
||||||
|
let cargo = e.path().file_stem().unwrap().to_str().unwrap().to_string();
|
||||||
|
cargo == "Cargo".to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_proto_file(e: &walkdir::DirEntry) -> bool {
|
||||||
|
if e.path().extension().is_none() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let ext = e.path().extension().unwrap().to_str().unwrap().to_string();
|
||||||
|
ext == "proto".to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_hidden(entry: &walkdir::DirEntry) -> bool {
|
||||||
|
entry
|
||||||
|
.file_name()
|
||||||
|
.to_str()
|
||||||
|
.map(|s| s.starts_with("."))
|
||||||
|
.unwrap_or(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_dir_if_not_exist(dir: &str) {
|
||||||
|
if !std::path::Path::new(&dir).exists() {
|
||||||
|
std::fs::create_dir_all(&dir).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn walk_dir<F1, F2>(dir: &str, filter: F2, mut path_and_name: F1)
|
||||||
|
where
|
||||||
|
F1: FnMut(String, String),
|
||||||
|
F2: Fn(&walkdir::DirEntry) -> bool,
|
||||||
|
{
|
||||||
|
for (path, name) in WalkDir::new(dir)
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|e| e.ok())
|
||||||
|
.filter(|e| filter(e))
|
||||||
|
.map(|e| {
|
||||||
|
(
|
||||||
|
e.path().to_str().unwrap().to_string(),
|
||||||
|
e.path().file_stem().unwrap().to_str().unwrap().to_string(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
{
|
||||||
|
path_and_name(path, name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
mod crate_config;
|
||||||
mod file;
|
mod file;
|
||||||
|
|
||||||
|
pub use crate_config::*;
|
||||||
pub use file::*;
|
pub use file::*;
|
||||||
|
Loading…
Reference in New Issue
Block a user