pub(crate) mod fields;
pub(crate) mod lock;
pub(crate) mod message;
mod patch_field;
pub(crate) mod scalar;
pub(crate) mod update_message;
use std::collections::{BTreeMap, BTreeSet};
use cratestack_core::Schema;
use crate::shared::{is_primary_key, is_readonly_field, is_server_only_field, is_version_field};
use fields::{model_allows_create, scalar_model_fields, visible_model_fields, visible_type_fields};
pub(crate) fn build_domain_pb_items(
schema: &Schema,
pb_lock: &cratestack_proto::PbLock,
enum_names: &BTreeSet<&str>,
) -> Result<Vec<proc_macro2::TokenStream>, String> {
let model_names: BTreeSet<&str> = schema.models.iter().map(|m| m.name.as_str()).collect();
let mut pb_items = Vec::new();
for model in &schema.models {
let numbers = numbers_for(pb_lock, &model.name)?;
let fields = visible_model_fields(model);
let domain_path = {
let ident = crate::shared::ident(&model.name);
quote::quote! { super::super::#ident }
};
let rendered =
message::render_message(&model.name, domain_path, &fields, numbers, enum_names)?;
pb_items.push(rendered.tokens);
if model_allows_create(model) {
let create_name = format!("Create{}Input", model.name);
let create_numbers = numbers_for(pb_lock, &create_name)?;
let create_fields = scalar_model_fields(model, &model_names)
.into_iter()
.filter(|field| !crate::shared::is_generated_on_create(field))
.collect::<Vec<_>>();
let create_ident = crate::shared::ident(&create_name);
let rendered = message::render_message(
&create_name,
quote::quote! { super::super::#create_ident },
&create_fields,
create_numbers,
enum_names,
)?;
pb_items.push(rendered.tokens);
}
let update_name = format!("Update{}Input", model.name);
let update_numbers = numbers_for(pb_lock, &update_name)?;
let update_fields = update_input_fields(model, &model_names);
let update_ident = crate::shared::ident(&update_name);
let rendered = update_message::render_update_message(
&update_name,
quote::quote! { super::super::#update_ident },
&update_fields,
update_numbers,
enum_names,
)?;
pb_items.push(rendered.tokens);
}
for ty in &schema.types {
let numbers = numbers_for(pb_lock, &ty.name)?;
let fields = visible_type_fields(ty);
let ty_ident = crate::shared::ident(&ty.name);
let rendered = message::render_message(
&ty.name,
quote::quote! { super::super::#ty_ident },
&fields,
numbers,
enum_names,
)?;
pb_items.push(rendered.tokens);
}
Ok(pb_items)
}
pub(crate) fn models_with_pk(
schema: &Schema,
) -> Vec<(&cratestack_core::Model, &cratestack_core::Field)> {
schema
.models
.iter()
.filter_map(|model| {
model
.fields
.iter()
.find(|field| is_primary_key(field))
.map(|pk| (model, pk))
})
.collect()
}
pub(crate) fn numbers_for<'a>(
lock: &'a cratestack_proto::PbLock,
message: &str,
) -> Result<&'a BTreeMap<String, i32>, String> {
lock.messages
.get(message)
.map(|entry| &entry.fields)
.ok_or_else(|| format!("no `.pb.lock` entry for message `{message}`"))
}
pub(crate) fn update_input_fields<'a>(
model: &'a cratestack_core::Model,
model_names: &BTreeSet<&str>,
) -> Vec<&'a cratestack_core::Field> {
scalar_model_fields(model, model_names)
.into_iter()
.filter(|field| !is_primary_key(field))
.filter(|field| !is_readonly_field(field))
.filter(|field| !is_server_only_field(field))
.filter(|field| !is_version_field(field))
.collect()
}