use crate::renderer::renderer_utils::quote_escaped_ident;
use crate::renderer::{normalize_generic_param, quote_ident};
use daml_lf::element::{DamlAbsoluteTyCon, DamlNonLocalTyCon, DamlTyCon, DamlTyConName, DamlType};
use heck::ToSnakeCase;
use proc_macro2::TokenStream;
use quote::quote;
use std::iter;
#[allow(clippy::match_same_arms)]
pub fn quote_type(daml_type: &DamlType<'_>) -> TokenStream {
match daml_type {
DamlType::List(args) | DamlType::TextMap(args) | DamlType::Optional(args) | DamlType::Numeric(args) =>
match args.as_slice() {
[arg] => {
let prim_name_tokens = quote_escaped_ident(daml_type.name());
let prim_param_tokens = quote_type(arg);
quote!(
#prim_name_tokens<#prim_param_tokens>
)
},
_ => panic!("expected exactly 1 type argument for {}, found {:?}", daml_type.name(), args),
},
DamlType::GenMap(args) => match args.as_slice() {
[k, v] => {
let prim_name_tokens = quote_escaped_ident(daml_type.name());
let prim_key_param_tokens = quote_type(k);
let prim_value_param_tokens = quote_type(v);
quote!(
#prim_name_tokens<#prim_key_param_tokens, #prim_value_param_tokens>
)
},
_ => panic!("expected exactly 2 type argument for {}, found {:?}", daml_type.name(), args),
},
DamlType::ContractId(_) => quote_escaped_ident(daml_type.name()),
DamlType::TyCon(tycon) => quote_tycon(tycon),
DamlType::BoxedTyCon(tycon) => {
let tycon = quote_tycon(tycon);
quote!(Box<#tycon>)
},
DamlType::Var(var) => {
let var_tokens = quote_ident(normalize_generic_param(var.var()).to_uppercase());
quote!(#var_tokens)
},
DamlType::Nat(n) => quote_ident(format!("{}{}", daml_type.name(), n)),
DamlType::Int64
| DamlType::Text
| DamlType::Timestamp
| DamlType::Party
| DamlType::Bool
| DamlType::Unit
| DamlType::Date => quote_escaped_ident(daml_type.name()),
DamlType::Update
| DamlType::Scenario
| DamlType::Arrow
| DamlType::Any
| DamlType::TypeRep
| DamlType::Bignumeric
| DamlType::RoundingMode
| DamlType::AnyException
| DamlType::Forall(_)
| DamlType::Struct(_)
| DamlType::Syn(_) => panic!("cannot render unsupported type: {}", daml_type.name()),
}
}
pub fn quote_tycon(tycon: &DamlTyCon<'_>) -> TokenStream {
let type_arguments_tokens = quote_generic_type_arguments(tycon.type_arguments());
match tycon.tycon() {
DamlTyConName::Local(local_tycon) => {
let target_type_tokens = quote_escaped_ident(local_tycon.data_name());
quote!(#target_type_tokens #type_arguments_tokens)
},
DamlTyConName::NonLocal(non_local_tycon) => {
let target_type_tokens = quote_escaped_ident(non_local_tycon.data_name());
let target_path_tokens = quote_non_local_path(non_local_tycon);
quote!(#target_path_tokens #target_type_tokens #type_arguments_tokens)
},
DamlTyConName::Absolute(abs_tycon) => {
let target_type_tokens = quote_escaped_ident(abs_tycon.data_name());
let target_path_tokens = quote_absolute_tycon(abs_tycon);
quote!(#target_path_tokens #target_type_tokens #type_arguments_tokens)
},
}
}
fn quote_generic_type_arguments(type_arguments: &[DamlType<'_>]) -> TokenStream {
if type_arguments.is_empty() {
quote!()
} else {
let all_type_arguments: Vec<_> = type_arguments.iter().map(quote_type).collect();
quote!( < #( #all_type_arguments ),* > )
}
}
fn quote_absolute_tycon(abs_tycon: &DamlAbsoluteTyCon<'_>) -> TokenStream {
let path: Vec<&str> = if abs_tycon.package_name().is_empty() {
abs_tycon.module_path().map(AsRef::as_ref).collect()
} else {
iter::once(abs_tycon.package_name()).chain(abs_tycon.module_path().map(AsRef::as_ref)).collect()
};
let target_path_tokens: Vec<_> =
path.into_iter().map(ToSnakeCase::to_snake_case).map(quote_escaped_ident).collect();
quote!(
crate :: #( #target_path_tokens :: )*
)
}
fn quote_non_local_path(tycon: &DamlNonLocalTyCon<'_>) -> TokenStream {
let current_full_path: Vec<_> = iter::once(tycon.source_package_name())
.chain(tycon.source_module_path().map(AsRef::as_ref))
.map(ToSnakeCase::to_snake_case)
.collect();
let target_full_path: Vec<_> = iter::once(tycon.target_package_name())
.chain(tycon.target_module_path().map(AsRef::as_ref))
.map(ToSnakeCase::to_snake_case)
.collect();
let common_prefix_length =
current_full_path.iter().zip(target_full_path.iter()).take_while(|(a, b)| a == b).count();
let super_prefix_tokens: Vec<_> =
iter::repeat(quote_ident("super")).take(current_full_path.len() - common_prefix_length).collect();
let relative_path_tokens: Vec<_> = target_full_path
.iter()
.skip(common_prefix_length)
.map(String::as_str)
.map(ToSnakeCase::to_snake_case)
.map(quote_escaped_ident)
.collect();
quote!(
#( #super_prefix_tokens :: )* #( #relative_path_tokens :: )*
)
}