use golem_wasm_ast::analysis::AnalysedType;
use rib::{FunctionName, WorkerFunctionType, WorkerFunctionsInRib};
use std::collections::HashMap;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct FunctionConstraints {
pub constraints: Vec<FunctionUsageConstraint>,
}
impl From<FunctionConstraints> for WorkerFunctionsInRib {
fn from(value: FunctionConstraints) -> Self {
WorkerFunctionsInRib {
function_calls: value
.constraints
.iter()
.map(|function_constraint| {
rib::WorkerFunctionType::from(function_constraint.clone())
})
.collect(),
}
}
}
impl FunctionConstraints {
pub fn from_worker_functions_in_rib(
worker_functions_in_rib: &WorkerFunctionsInRib,
) -> FunctionConstraints {
let functions = worker_functions_in_rib
.function_calls
.iter()
.map(FunctionUsageConstraint::from_worker_function_type)
.collect::<Vec<_>>();
FunctionConstraints {
constraints: functions,
}
}
pub fn remove_constraints(&self, constraints_to_remove: &[FunctionSignature]) -> Option<Self> {
let mut constraints = vec![];
for constraint in &self.constraints {
if constraints_to_remove.contains(&constraint.function_signature) {
let mut constraint = constraint.clone();
constraint.decrement_usage_count();
if constraint.usage_count > 0 {
constraints.push(constraint);
}
}
}
if self.constraints.is_empty() {
None
} else {
Some(FunctionConstraints { constraints })
}
}
pub fn try_merge(
worker_functions: Vec<FunctionConstraints>,
) -> Result<FunctionConstraints, String> {
let mut merged_function_calls: HashMap<FunctionName, FunctionUsageConstraint> =
HashMap::new();
for wf in worker_functions {
for constraint_usage in wf.constraints {
match merged_function_calls.get_mut(constraint_usage.function_key()) {
Some(existing_constraint) => {
if existing_constraint.parameter_types()
!= constraint_usage.parameter_types()
{
return Err(format!(
"Parameter type conflict for function key {:?}: {:?} vs {:?}",
constraint_usage.function_key(),
existing_constraint.parameter_types(),
constraint_usage.parameter_types()
));
}
if existing_constraint.return_type() != constraint_usage.return_type() {
return Err(format!(
"Return type conflict for function key {:?}: {:?} vs {:?}",
constraint_usage.function_key(),
existing_constraint.return_type(),
constraint_usage.return_type()
));
}
existing_constraint.usage_count = existing_constraint
.usage_count
.saturating_add(constraint_usage.usage_count);
}
None => {
merged_function_calls
.insert(constraint_usage.function_key().clone(), constraint_usage);
}
}
}
}
let mut merged_function_calls_vec: Vec<FunctionUsageConstraint> =
merged_function_calls.into_values().collect();
merged_function_calls_vec.sort_by(|a, b| a.function_key().cmp(b.function_key()));
Ok(FunctionConstraints {
constraints: merged_function_calls_vec,
})
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct FunctionSignature {
function_name: FunctionName,
parameter_types: Vec<AnalysedType>,
return_type: Option<AnalysedType>,
}
impl FunctionSignature {
pub fn new(
function_key: FunctionName,
parameter_types: Vec<AnalysedType>,
return_type: Option<AnalysedType>,
) -> Self {
FunctionSignature {
function_name: function_key,
parameter_types,
return_type,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct FunctionUsageConstraint {
pub function_signature: FunctionSignature,
pub usage_count: u32,
}
impl From<FunctionUsageConstraint> for WorkerFunctionType {
fn from(value: FunctionUsageConstraint) -> Self {
WorkerFunctionType {
function_name: value.function_signature.function_name.clone(),
parameter_types: value.function_signature.parameter_types.clone(),
return_type: value.function_signature.return_type.clone(),
}
}
}
impl FunctionUsageConstraint {
pub fn function_key(&self) -> &FunctionName {
&self.function_signature.function_name
}
pub fn parameter_types(&self) -> &Vec<AnalysedType> {
&self.function_signature.parameter_types
}
pub fn return_type(&self) -> &Option<AnalysedType> {
&self.function_signature.return_type
}
pub fn from_worker_function_type(
worker_function_type: &WorkerFunctionType,
) -> FunctionUsageConstraint {
FunctionUsageConstraint {
function_signature: FunctionSignature {
function_name: worker_function_type.function_name.clone(),
parameter_types: worker_function_type.parameter_types.clone(),
return_type: worker_function_type.return_type.clone(),
},
usage_count: 1,
}
}
fn decrement_usage_count(&mut self) {
self.usage_count -= 1;
}
}
#[cfg(feature = "protobuf")]
mod protobuf {
use crate::model::component_constraint::{
FunctionConstraints, FunctionSignature, FunctionUsageConstraint,
};
use golem_api_grpc::proto::golem::component::FunctionConstraint as FunctionConstraintProto;
use golem_api_grpc::proto::golem::component::FunctionConstraintCollection as FunctionConstraintCollectionProto;
use golem_wasm_ast::analysis::AnalysedType;
use rib::FunctionName;
impl TryFrom<golem_api_grpc::proto::golem::component::FunctionConstraintCollection>
for FunctionConstraints
{
type Error = String;
fn try_from(
value: golem_api_grpc::proto::golem::component::FunctionConstraintCollection,
) -> Result<Self, Self::Error> {
let collection = FunctionConstraints {
constraints: value
.constraints
.iter()
.map(|constraint_proto| {
FunctionUsageConstraint::try_from(constraint_proto.clone())
})
.collect::<Result<_, _>>()?,
};
Ok(collection)
}
}
impl From<FunctionConstraints> for FunctionConstraintCollectionProto {
fn from(value: FunctionConstraints) -> Self {
FunctionConstraintCollectionProto {
constraints: value
.constraints
.iter()
.map(|function_constraint| {
FunctionConstraintProto::from(function_constraint.clone())
})
.collect(),
}
}
}
impl TryFrom<FunctionConstraintProto> for FunctionUsageConstraint {
type Error = String;
fn try_from(value: FunctionConstraintProto) -> Result<Self, Self::Error> {
let return_type = value
.return_type
.as_ref()
.map(AnalysedType::try_from)
.transpose()?;
let parameter_types = value
.parameter_types
.iter()
.map(AnalysedType::try_from)
.collect::<Result<_, _>>()?;
let function_name_proto = value
.function_key
.and_then(|x| x.function_name)
.ok_or("Function key missing")?;
let function_key = FunctionName::try_from(function_name_proto)?;
let usage_count = value.usage_count;
Ok(Self {
function_signature: FunctionSignature {
function_name: function_key,
parameter_types,
return_type,
},
usage_count,
})
}
}
impl From<FunctionUsageConstraint> for FunctionConstraintProto {
fn from(value: FunctionUsageConstraint) -> Self {
let function_name =
golem_api_grpc::proto::golem::rib::function_name_type::FunctionName::from(
value.function_signature.clone().function_name,
);
let function_name_type = golem_api_grpc::proto::golem::rib::FunctionNameType {
function_name: Some(function_name),
};
FunctionConstraintProto {
function_key: Some(function_name_type),
parameter_types: value
.parameter_types()
.iter()
.map(|analysed_type| analysed_type.into())
.collect(),
return_type: value
.return_type()
.as_ref()
.map(|analysed_type| analysed_type.into()),
usage_count: value.usage_count,
}
}
}
}