use super::*;
#[path = "core.rs"]
mod core;
pub(crate) use core::generate_runner_body;
pub(crate) struct RecursionContext {
depth: usize,
labels: Vec<String>,
}
impl RecursionContext {
pub(crate) fn new() -> Self {
Self {
depth: 0,
labels: Vec::new(),
}
}
fn enter_rec(&mut self, label: &str) {
self.depth += 1;
self.labels.push(label.to_string());
}
fn exit_rec(&mut self) {
self.depth -= 1;
self.labels.pop();
}
fn is_in_rec(&self, label: &str) -> bool {
self.labels.contains(&label.to_string())
}
}
#[derive(Default)]
pub(crate) struct HintCounters {
send_count: usize,
recv_count: usize,
}
pub(crate) fn generate_runner_body_with_hints(
local_type: &LocalType,
ctx: &mut RecursionContext,
hints: Option<&ExecutionHints>,
path: &OperationPath,
counters: &mut HintCounters,
) -> TokenStream {
match local_type {
LocalType::Send {
to,
message,
continuation,
} => {
let msg_type = &message.name;
let current_path = path.push(OperationStep::Send(counters.send_count));
counters.send_count += 1;
let is_parallel = hints.map(|h| h.is_parallel(¤t_path)).unwrap_or(false);
let cont =
generate_runner_body_with_hints(continuation, ctx, hints, ¤t_path, counters);
if let Some(index) = to.index() {
match index {
crate::ast::role::RoleIndex::Wildcard => {
let family_name = to.name().to_string();
return if is_parallel {
quote! {
let roles = adapter.resolve_family(#family_name)?;
if roles.is_empty() {
return Err(::telltale_choreography::ChoreographyError::EmptyRoleFamily(
#family_name.to_string()
).into());
}
let msg: #msg_type = adapter.provide_message(roles[0]).await?;
{
use ::futures::future::join_all;
let futures: Vec<_> = roles.iter()
.map(|r| adapter.send(r.clone(), msg.clone()))
.collect();
let results = join_all(futures).await;
for result in results {
result?;
}
}
#cont
}
} else {
quote! {
let roles = adapter.resolve_family(#family_name)?;
if roles.is_empty() {
return Err(::telltale_choreography::ChoreographyError::EmptyRoleFamily(
#family_name.to_string()
).into());
}
let msg: #msg_type = adapter.provide_message(roles[0]).await?;
adapter.broadcast(&roles, msg).await?;
#cont
}
};
}
crate::ast::role::RoleIndex::Range(range) => {
let family_name = to.name().to_string();
let (start_expr, end_expr) = generate_range_exprs(range);
return if is_parallel {
quote! {
let roles = adapter.resolve_range(#family_name, #start_expr, #end_expr)?;
if roles.is_empty() {
return Err(::telltale_choreography::ChoreographyError::EmptyRoleFamily(
#family_name.to_string()
).into());
}
let msg: #msg_type = adapter.provide_message(roles[0]).await?;
{
use ::futures::future::join_all;
let futures: Vec<_> = roles.iter()
.map(|r| adapter.send(r.clone(), msg.clone()))
.collect();
let results = join_all(futures).await;
for result in results {
result?;
}
}
#cont
}
} else {
quote! {
let roles = adapter.resolve_range(#family_name, #start_expr, #end_expr)?;
if roles.is_empty() {
return Err(::telltale_choreography::ChoreographyError::EmptyRoleFamily(
#family_name.to_string()
).into());
}
let msg: #msg_type = adapter.provide_message(roles[0]).await?;
adapter.broadcast(&roles, msg).await?;
#cont
}
};
}
_ => {} }
}
let to_role = generate_role_id(to);
quote! {
let msg: #msg_type = adapter.provide_message(#to_role).await?;
output.metadata.messages_sent += 1;
adapter.send(#to_role, msg).await?;
#cont
}
}
LocalType::Receive {
from,
message,
continuation,
} => {
let msg_type = &message.name;
let current_path = path.push(OperationStep::Recv(counters.recv_count));
counters.recv_count += 1;
let is_parallel = hints.map(|h| h.is_parallel(¤t_path)).unwrap_or(false);
let min_responses = hints.and_then(|h| h.min_responses(¤t_path));
let cont =
generate_runner_body_with_hints(continuation, ctx, hints, ¤t_path, counters);
if let Some(index) = from.index() {
match index {
crate::ast::role::RoleIndex::Wildcard => {
let family_name = from.name().to_string();
return if is_parallel {
if let Some(min) = min_responses {
quote! {
let roles = adapter.resolve_family(#family_name)?;
if roles.is_empty() {
return Err(::telltale_choreography::ChoreographyError::EmptyRoleFamily(
#family_name.to_string()
).into());
}
let _msgs: Vec<#msg_type> = {
use ::futures::future::join_all;
let futures: Vec<_> = roles.iter()
.map(|r| adapter.recv::<#msg_type>(r.clone()))
.collect();
let results = join_all(futures).await;
let mut collected = Vec::new();
for result in results {
if let Ok(msg) = result {
collected.push(msg);
}
}
if collected.len() < #min as usize {
return Err(::telltale_choreography::ChoreographyError::InsufficientResponses {
expected: #min as usize,
received: collected.len(),
}.into());
}
collected
};
#cont
}
} else {
quote! {
let roles = adapter.resolve_family(#family_name)?;
if roles.is_empty() {
return Err(::telltale_choreography::ChoreographyError::EmptyRoleFamily(
#family_name.to_string()
).into());
}
let _msgs: Vec<#msg_type> = {
use ::futures::future::join_all;
let futures: Vec<_> = roles.iter()
.map(|r| adapter.recv::<#msg_type>(r.clone()))
.collect();
let results = join_all(futures).await;
results.into_iter().collect::<Result<Vec<_>, _>>()?
};
#cont
}
}
} else {
quote! {
let roles = adapter.resolve_family(#family_name)?;
if roles.is_empty() {
return Err(::telltale_choreography::ChoreographyError::EmptyRoleFamily(
#family_name.to_string()
).into());
}
let _msgs: Vec<#msg_type> = adapter.collect(&roles).await?;
#cont
}
};
}
crate::ast::role::RoleIndex::Range(range) => {
let family_name = from.name().to_string();
let (start_expr, end_expr) = generate_range_exprs(range);
return if is_parallel {
quote! {
let roles = adapter.resolve_range(#family_name, #start_expr, #end_expr)?;
if roles.is_empty() {
return Err(::telltale_choreography::ChoreographyError::EmptyRoleFamily(
#family_name.to_string()
).into());
}
let _msgs: Vec<#msg_type> = {
use ::futures::future::join_all;
let futures: Vec<_> = roles.iter()
.map(|r| adapter.recv::<#msg_type>(r.clone()))
.collect();
let results = join_all(futures).await;
results.into_iter().collect::<Result<Vec<_>, _>>()?
};
#cont
}
} else {
quote! {
let roles = adapter.resolve_range(#family_name, #start_expr, #end_expr)?;
if roles.is_empty() {
return Err(::telltale_choreography::ChoreographyError::EmptyRoleFamily(
#family_name.to_string()
).into());
}
let _msgs: Vec<#msg_type> = adapter.collect(&roles).await?;
#cont
}
};
}
_ => {} }
}
let from_role = generate_role_id(from);
quote! {
let _msg: #msg_type = adapter.recv(#from_role).await?;
#cont
}
}
_ => generate_runner_body(local_type, ctx),
}
}
pub(crate) fn generate_range_exprs(
range: &crate::ast::role::RoleRange,
) -> (TokenStream, TokenStream) {
use crate::ast::role::RangeExpr;
let start_expr = match &range.start {
RangeExpr::Concrete(n) => quote! { #n },
RangeExpr::Symbolic(var) => {
let var_ident = format_ident!("{}", var);
quote! { #var_ident }
}
};
let end_expr = match &range.end {
RangeExpr::Concrete(n) => quote! { #n },
RangeExpr::Symbolic(var) => {
let var_ident = format_ident!("{}", var);
quote! { #var_ident }
}
};
(start_expr, end_expr)
}
pub(crate) fn generate_role_id(role: &Role) -> TokenStream {
use crate::ast::role::RoleIndex;
let name = role.name();
if let Some(index) = role.index() {
match index {
RoleIndex::Concrete(n) => {
quote! {
Role::#name(#n)
}
}
RoleIndex::Symbolic(var) => {
let var_ident = format_ident!("{}", var);
quote! {
Role::#name(#var_ident)
}
}
RoleIndex::Wildcard => {
quote! {{
return Err(::telltale_choreography::ChoreographyError::ExecutionError(
"wildcard roles in this context should use resolve_family() instead".to_string()
).into());
}}
}
RoleIndex::Range(_) => {
quote! {{
return Err(::telltale_choreography::ChoreographyError::ExecutionError(
"range roles in this context should use resolve_range() instead".to_string()
).into());
}}
}
}
} else if role.param().is_some() {
quote! {
Role::#name(index)
}
} else {
quote! {
Role::#name
}
}
}