mod procedure_arms;
mod procedures;
mod rpc_inputs;
mod rpc_list;
mod service;
use std::collections::BTreeSet;
use std::path::Path;
use cratestack_core::{Schema, TransportStyle};
use proc_macro::TokenStream;
use quote::quote;
use syn::LitStr;
use crate::include::grpc_pb::{build_domain_pb_items, models_with_pk, numbers_for};
pub(super) fn build_grpc_module(
schema: &Schema,
schema_resolved: &Path,
schema_path: &LitStr,
) -> Result<proc_macro2::TokenStream, TokenStream> {
if schema.transport != TransportStyle::Grpc {
return Ok(quote! {});
}
let extra_messages = cratestack_proto::synthesize_messages(schema)
.map_err(|error| super::collect::compile_error(schema_path, error.to_string()))?;
let pb_lock =
crate::include::grpc_pb::lock::load_pb_lock(schema, schema_resolved, &extra_messages)
.map_err(|error| super::collect::compile_error(schema_path, error))?;
let enum_names: BTreeSet<&str> = schema.enums.iter().map(|e| e.name.as_str()).collect();
let mut pb_items = build_domain_pb_items(schema, &pb_lock, &enum_names)
.map_err(|error| super::collect::compile_error(schema_path, error))?;
let models_with_pk = models_with_pk(schema);
if !models_with_pk.is_empty() {
let string_list_numbers = numbers_for(&pb_lock, "StringList")
.map_err(|error| super::collect::compile_error(schema_path, error))?;
pb_items.push(
rpc_inputs::render_string_list(string_list_numbers)
.map_err(|error| super::collect::compile_error(schema_path, error))?,
);
let predicate_numbers = numbers_for(&pb_lock, "RpcListPredicate")
.map_err(|error| super::collect::compile_error(schema_path, error))?;
pb_items.push(
rpc_inputs::render_rpc_list_predicate(predicate_numbers)
.map_err(|error| super::collect::compile_error(schema_path, error))?,
);
let page_info_numbers = numbers_for(&pb_lock, "PageInfo")
.map_err(|error| super::collect::compile_error(schema_path, error))?;
pb_items.push(
rpc_inputs::render_page_info(page_info_numbers)
.map_err(|error| super::collect::compile_error(schema_path, error))?,
);
for (model, pk) in &models_with_pk {
let pk_numbers = numbers_for(&pb_lock, &format!("{}RpcPkInput", model.name))
.map_err(|error| super::collect::compile_error(schema_path, error))?;
pb_items.push(
rpc_inputs::render_rpc_pk_input(&model.name, pk, pk_numbers)
.map_err(|error| super::collect::compile_error(schema_path, error))?,
);
let update_numbers = numbers_for(&pb_lock, &format!("{}RpcUpdateInput", model.name))
.map_err(|error| super::collect::compile_error(schema_path, error))?;
pb_items.push(
rpc_inputs::render_rpc_update_input(&model.name, pk, update_numbers)
.map_err(|error| super::collect::compile_error(schema_path, error))?,
);
let list_numbers = numbers_for(&pb_lock, &format!("{}RpcListInput", model.name))
.map_err(|error| super::collect::compile_error(schema_path, error))?;
pb_items.push(
rpc_list::render_rpc_list_input(&model.name, list_numbers)
.map_err(|error| super::collect::compile_error(schema_path, error))?,
);
let page_numbers = numbers_for(&pb_lock, &format!("PageOf{}", model.name))
.map_err(|error| super::collect::compile_error(schema_path, error))?;
pb_items.push(
rpc_list::render_page_of(&model.name, page_numbers)
.map_err(|error| super::collect::compile_error(schema_path, error))?,
);
}
}
let covered_pages: BTreeSet<String> = models_with_pk
.iter()
.map(|(model, _)| format!("PageOf{}", model.name))
.collect();
let mut extra_pages_emitted: BTreeSet<String> = BTreeSet::new();
for procedure in &schema.procedures {
let base = cratestack_proto::to_pascal_case(&procedure.name);
let input_numbers = numbers_for(&pb_lock, &format!("{base}Input"))
.map_err(|error| super::collect::compile_error(schema_path, error))?;
pb_items.push(
procedures::render_procedure_input(procedure, input_numbers, &enum_names)
.map_err(|error| super::collect::compile_error(schema_path, error))?,
);
let output_numbers = numbers_for(&pb_lock, &format!("{base}Output"))
.map_err(|error| super::collect::compile_error(schema_path, error))?;
pb_items.push(
procedures::render_procedure_output(procedure, output_numbers, &enum_names)
.map_err(|error| super::collect::compile_error(schema_path, error))?,
);
let Some(item) = procedure.return_type.page_item() else {
continue;
};
let message_name = format!("PageOf{}", item.name);
if covered_pages.contains(&message_name)
|| !extra_pages_emitted.insert(message_name.clone())
{
continue;
}
let page_numbers = numbers_for(&pb_lock, &message_name)
.map_err(|error| super::collect::compile_error(schema_path, error))?;
pb_items.push(
rpc_list::render_page_of(&item.name, page_numbers)
.map_err(|error| super::collect::compile_error(schema_path, error))?,
);
}
let package = pb_lock.package.clone().unwrap_or_default();
let service_tokens = service::build_service(schema, &package, &models_with_pk);
Ok(quote! {
pub mod grpc {
pub mod pb {
#(#pb_items)*
}
#service_tokens
}
})
}