diff --git a/.gitignore b/.gitignore index 305adf30..1ded98ed 100644 --- a/.gitignore +++ b/.gitignore @@ -6,7 +6,7 @@ Cargo.lock .idea *.o example/protocols/**/*.rs -!example/protocols/**/mod.rs +!example/protocols/mod.rs example-prost/protocols/**/*.rs !example-prost/protocols/**/mod.rs src/ttrpc.rs diff --git a/compiler/src/codegen.rs b/compiler/src/codegen.rs index 4a4560a1..c70552ae 100644 --- a/compiler/src/codegen.rs +++ b/compiler/src/codegen.rs @@ -743,7 +743,8 @@ pub fn gen( /// Generates ttrpc service files and writes them to `out_dir`. /// /// When [`Customize::gen_mod`](crate::Customize::gen_mod) is enabled, this function also creates -/// or updates `mod.rs` without discarding existing module declarations. +/// or updates `mod.rs` without discarding existing module declarations. Generated indexes opt out +/// of rustfmt, and existing comments and attributes retain their order. /// /// # Errors /// @@ -762,13 +763,18 @@ pub fn gen_and_write( if customize.gen_mod { let file_path = out_dir.join("mod.rs"); - let mut set = HashSet::new(); - let mut lines = Vec::new(); + let generated = "// @generated"; + let rustfmt_skip = "#![cfg_attr(rustfmt, rustfmt::skip)]"; + let mut lines = vec![generated.to_owned(), rustfmt_skip.to_owned()]; + let mut set: HashSet<_> = lines.iter().cloned().collect(); // Preserve existing order so comments and attributes stay with their modules. if let Ok(file) = File::open(&file_path) { let reader = io::BufReader::new(file); for line in reader.lines() { let line = line?; + if line == generated || line == rustfmt_skip { + continue; + } set.insert(line.clone()); lines.push(line); } @@ -842,7 +848,7 @@ mod tests { use super::*; #[test] - fn generated_mod_rs_is_stable_and_preserves_existing_lines() { + fn generated_mod_rs_skips_rustfmt_and_preserves_existing_lines() { let descriptors: Vec<_> = ["zeta.proto", "alpha.proto"] .iter() .map(|&name| FileDescriptorProto { @@ -858,9 +864,26 @@ mod tests { gen_mod: true, ..Default::default() }; - let existing = "// @generated\n\n// Keep this module gated.\n#[cfg(unix)]\npub mod custom;\n\n#[cfg(unix)]\npub mod another;\npub mod alpha_ttrpc;\n"; - - for initial in [None, Some(existing)] { + let existing = "\n// Keep this module gated.\n#[cfg(unix)]\npub mod custom;\n\n#[cfg(unix)]\npub mod another;\npub mod alpha_ttrpc;\n"; + let protobuf_modules = "\npub mod alpha;\npub mod zeta;\n"; + let header = "// @generated\n#![cfg_attr(rustfmt, rustfmt::skip)]\n"; + let existing_index = format!("// @generated\n{existing}"); + let protobuf_index = format!("// @generated\n{protobuf_modules}"); + + for (initial, expected) in [ + ( + None, + format!("{header}pub mod alpha_ttrpc;\npub mod zeta_ttrpc;\n"), + ), + ( + Some(existing_index.as_str()), + format!("{header}{existing}pub mod zeta_ttrpc;\n"), + ), + ( + Some(protobuf_index.as_str()), + format!("{header}{protobuf_modules}pub mod alpha_ttrpc;\npub mod zeta_ttrpc;\n"), + ), + ] { let dir = tempfile::tempdir().unwrap(); let path = dir.path().join("mod.rs"); if let Some(contents) = initial { @@ -872,10 +895,6 @@ mod tests { "alpha.proto".to_owned(), ]; gen_and_write(&descriptors, &inputs, dir.path(), &customize).unwrap(); - let expected = match initial { - Some(contents) => format!("{contents}pub mod zeta_ttrpc;\n"), - None => "pub mod alpha_ttrpc;\npub mod zeta_ttrpc;\n".to_owned(), - }; assert_eq!(fs::read_to_string(&path).unwrap(), expected); inputs.reverse(); diff --git a/example/protocols/asynchronous/mod.rs b/example/protocols/asynchronous/mod.rs deleted file mode 100644 index c142ea64..00000000 --- a/example/protocols/asynchronous/mod.rs +++ /dev/null @@ -1,16 +0,0 @@ -// @generated - -pub mod agent; -pub mod empty; -pub mod gogo; -pub mod health; -pub mod oci; -pub mod streaming; -pub mod types; -pub mod well_known; -pub mod well_known_streaming; -pub mod agent_ttrpc; -pub mod health_ttrpc; -pub mod streaming_ttrpc; -pub mod well_known_streaming_ttrpc; -pub mod well_known_ttrpc; diff --git a/example/protocols/sync/mod.rs b/example/protocols/sync/mod.rs deleted file mode 100644 index 2b34e668..00000000 --- a/example/protocols/sync/mod.rs +++ /dev/null @@ -1,12 +0,0 @@ -// @generated - -pub mod agent; -pub mod empty; -pub mod gogo; -pub mod health; -pub mod oci; -pub mod types; -pub mod well_known; -pub mod agent_ttrpc; -pub mod health_ttrpc; -pub mod well_known_ttrpc; diff --git a/ttrpc-codegen/tests/well_known.rs b/ttrpc-codegen/tests/well_known.rs index 8b37eeec..142d3fda 100644 --- a/ttrpc-codegen/tests/well_known.rs +++ b/ttrpc-codegen/tests/well_known.rs @@ -3,7 +3,7 @@ use std::fs; use std::path::{Path, PathBuf}; use tempfile::TempDir; -use ttrpc_codegen::{parse_and_typecheck, Codegen, Customize}; +use ttrpc_codegen::{parse_and_typecheck, Codegen, Customize, ProtobufCustomize}; fn proto_dir() -> PathBuf { Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/protos") @@ -46,11 +46,15 @@ fn generates_runtime_paths_for_embedded_types() { .rust_protobuf() .customize(Customize { async_all, + gen_mod: true, ..Default::default() }) + .rust_protobuf_customize(ProtobufCustomize::default().gen_mod_rs(true)) .run() .unwrap(); + let modules = fs::read_to_string(output.path().join("mod.rs")).unwrap(); + assert!(modules.starts_with("// @generated\n#![cfg_attr(rustfmt, rustfmt::skip)]\n")); let messages = fs::read_to_string(output.path().join("well_known.rs")).unwrap(); let services = fs::read_to_string(output.path().join("well_known_ttrpc.rs")).unwrap(); let timestamp = "::protobuf::well_known_types::timestamp::Timestamp";