use crate::proto_gen::ast::parse_crate_protobuf; use crate::proto_gen::proto_info::ProtobufCrateContext; use crate::proto_gen::template::write_derive_meta; use crate::proto_gen::util::*; use crate::proto_gen::ProtoFile; use std::fs::File; use std::{fs::OpenOptions, io::Write}; pub(crate) struct ProtoGenerator(); impl ProtoGenerator { pub(crate) fn gen(root: &str) -> Vec { let crate_contexts = parse_crate_protobuf(vec![root.to_owned()]); write_proto_files(&crate_contexts); write_rust_crate_mod_file(&crate_contexts); for crate_info in &crate_contexts { let _ = crate_info.protobuf_crate.create_output_dir(); let _ = crate_info.protobuf_crate.proto_output_dir(); crate_info.create_crate_mod_file(); } let cache = ProtoCache::from_crate_contexts(&crate_contexts); let cache_str = serde_json::to_string(&cache).unwrap(); let protobuf_dart = format!("{}/.proto_cache", root); match std::fs::OpenOptions::new() .create(true) .write(true) .append(false) .truncate(true) .open(&protobuf_dart) { Ok(ref mut file) => { file.write_all(cache_str.as_bytes()).unwrap(); File::flush(file).unwrap(); } Err(err) => { panic!("Failed to open file: {}", err); } } crate_contexts } } fn write_proto_files(crate_contexts: &[ProtobufCrateContext]) { for context in crate_contexts { let dir = context.protobuf_crate.proto_output_dir(); context.files.iter().for_each(|info| { let proto_file_path = format!("{}/{}.proto", dir, &info.file_name); save_content_to_file_with_diff_prompt(&info.generated_content, proto_file_path.as_ref()); }); } } fn write_rust_crate_mod_file(crate_contexts: &[ProtobufCrateContext]) { for context in crate_contexts { let mod_path = context.protobuf_crate.proto_model_mod_file(); match OpenOptions::new() .create(true) .write(true) .append(false) .truncate(true) .open(&mod_path) { Ok(ref mut file) => { let mut mod_file_content = String::new(); mod_file_content.push_str("#![cfg_attr(rustfmt, rustfmt::skip)]\n"); mod_file_content.push_str("// Auto-generated, do not edit\n"); walk_dir( context.protobuf_crate.proto_output_dir().as_ref(), |e| !e.file_type().is_dir(), |_, name| { let c = format!("\nmod {};\npub use {}::*;\n", &name, &name); mod_file_content.push_str(c.as_ref()); }, ); file.write_all(mod_file_content.as_bytes()).unwrap(); } Err(err) => { panic!("Failed to open file: {}", err); } } } } #[derive(serde::Serialize)] pub struct ProtoCache { pub structs: Vec, pub enums: Vec, } impl ProtoCache { fn from_crate_contexts(crate_contexts: &[ProtobufCrateContext]) -> Self { let proto_files = crate_contexts .iter() .map(|ref crate_info| &crate_info.files) .flatten() .collect::>(); let structs: Vec = proto_files.iter().map(|info| info.structs.clone()).flatten().collect(); let enums: Vec = proto_files.iter().map(|info| info.enums.clone()).flatten().collect(); Self { structs, enums } } }