use proc_macro2::{Ident, TokenStream};
use quote::{format_ident, quote};
use super::{BindingBackend, ShapeParts};
use crate::analysis::{EmitShape, RequestParam};
use crate::codegen::{BindingMode, MethodHandler, ServiceHandler};
fn shape_parts<'a>(
backend: &dyn BindingBackend,
method: &'a MethodHandler<'a>,
mode: BindingMode,
) -> ShapeParts<'a> {
ShapeParts {
param_defs: backend.param_defs(method, mode),
client_call: backend.client_call(method, mode),
method,
mode,
}
}
fn emit_for_shape(
backend: &dyn BindingBackend,
method: &MethodHandler<'_>,
mode: BindingMode,
) -> TokenStream {
let parts = shape_parts(backend, method, mode);
match method.emit_shape() {
EmitShape::List => backend.emit_list(&parts),
EmitShape::Create => backend.emit_create(&parts),
EmitShape::GetUpdate => backend.emit_get_update(&parts),
EmitShape::Delete => backend.emit_delete(&parts),
}
}
fn scoped_instance_method(
backend: &dyn BindingBackend,
method: MethodHandler<'_>,
) -> Option<TokenStream> {
method
.is_scoped_instance_method()
.then(|| emit_for_shape(backend, &method, BindingMode::Scoped))
}
fn root_method(
backend: &dyn BindingBackend,
method: MethodHandler<'_>,
mode: BindingMode,
) -> Option<TokenStream> {
match mode {
BindingMode::Scoped => {
let collection_shaped =
matches!(method.emit_shape(), EmitShape::List | EmitShape::Create);
(method.is_collection_method() && collection_shaped)
.then(|| emit_for_shape(backend, &method, mode))
}
BindingMode::Flat => Some(emit_for_shape(backend, &method, mode)),
}
}
pub(crate) fn scoped_client_methods(
backend: &dyn BindingBackend,
service: &ServiceHandler<'_>,
) -> Vec<TokenStream> {
service
.methods()
.filter_map(|m| scoped_instance_method(backend, m))
.collect()
}
pub(crate) fn root_client_methods(
backend: &dyn BindingBackend,
services: &[&ServiceHandler<'_>],
) -> Vec<TokenStream> {
services
.iter()
.flat_map(|s| {
let mode = s.binding_mode();
s.methods()
.filter_map(move |m| root_method(backend, m, mode))
.collect::<Vec<_>>()
})
.collect()
}
pub(crate) fn client_method(method: &MethodHandler<'_>, mode: BindingMode) -> (Ident, bool) {
match mode {
BindingMode::Scoped => (method.plan.resource_client_method(), true),
BindingMode::Flat => (method.plan.base_method_ident(), false),
}
}
pub(crate) fn is_seeded_page_token(method: &MethodHandler<'_>, param: &RequestParam) -> bool {
method.emit_shape() == EmitShape::List && param.name() == "page_token"
}
pub(crate) fn sorted_services<'s, 'a>(
services: &'s [ServiceHandler<'a>],
) -> Vec<&'s ServiceHandler<'a>> {
let mut sorted: Vec<_> = services.iter().collect();
sorted.sort_by(|a, b| a.plan.service_name.cmp(&b.plan.service_name));
sorted
}
pub(crate) fn scoped_client_imports<'s>(
services: &'s [&ServiceHandler<'_>],
prefix: &'s str,
) -> impl Iterator<Item = TokenStream> + 's {
services
.iter()
.filter(|s| s.emits_scoped_binding())
.map(move |s| {
let mod_name = format_ident!("{}", s.plan.base_path);
let client_name = format_ident!("{prefix}{}", s.client_type());
quote! { use crate::codegen::#mod_name::#client_name; }
})
}
pub(crate) fn resource_accessor(
service: &ServiceHandler<'_>,
attr: TokenStream,
client_prefix: &str,
) -> Option<TokenStream> {
let spec = service.accessor_spec()?;
let method_name = format_ident!("{}", spec.singular);
let client_name = format_ident!("{client_prefix}{}", service.client_type());
let param_idents: Vec<_> = spec
.params
.iter()
.map(|p| crate::utils::field_ident(p))
.collect();
let param_list = param_idents.iter().map(|id| quote! { #id: String });
let client = if spec.nested {
let from_full_name_method = format_ident!("{}_from_full_name", spec.singular);
let format_str = spec.join_format();
quote! {
let full_name = format!(#format_str, #(#param_idents),*);
#client_name {
client: self.client.#from_full_name_method(full_name),
}
}
} else {
quote! {
#client_name {
client: self.client.#method_name(#(#param_idents),*),
}
}
};
Some(quote! {
#attr
pub fn #method_name(&self, #(#param_list),*) -> #client_name {
#client
}
})
}
pub(crate) fn resource_accessor_methods(
backend: &dyn BindingBackend,
services: &[&ServiceHandler<'_>],
) -> Vec<TokenStream> {
services
.iter()
.filter(|s| s.supports_scoped_client())
.filter_map(|s| backend.emit_resource_accessor(s))
.collect()
}