use core::fmt::{Debug, Display};
use std::string::ToString;
use std::sync::Arc;
use melodium_common::descriptor::{DataType, Flow, Identifier, Status};
use crate::{building::CheckStep, design::Value, designer::Reference};
#[derive(Debug, Clone)]
pub enum LogicErrorKind {
CollectionUndefined,
UncommitedDescriptor { identifier: Identifier },
NoDesigner { identifier: Identifier },
ErroneousDesign { identifier: Identifier },
ErroneousChecks,
UnavailableDesign { identifier: Identifier },
UnexistingVariable {
identifier: Identifier,
parameter: String,
variable: String,
},
UnexistingContextVariable {
identifier: Identifier,
parameter: String,
context: Identifier,
variable: String,
},
UnexistingParameter {
scope: Identifier,
called: Identifier,
parameter: String,
},
UnmatchingDataType {
scope: Identifier,
called: Identifier,
parameter: String,
value: Value,
expected: DataType,
given: DataType,
},
UnsetParameter {
scope: Identifier,
called: Identifier,
parameter: String,
},
MultipleParameterAssignation {
scope: Identifier,
called: Identifier,
parameter: String,
},
NoValue {
scope: Identifier,
called: Identifier,
parameter: String,
},
NoContext {
scope: Identifier,
model: Identifier,
name: String,
parameter: String,
},
UnavailableContext {
scope: Identifier,
context: Identifier,
},
ConnectionInputNotFound {
scope: Identifier,
to: Identifier,
input: String,
},
ConnectionSelfInputNotFound { scope: Identifier, input: String },
ConnectionOutputNotFound {
scope: Identifier,
from: Identifier,
output: String,
},
ConnectionSelfOutputNotFound { scope: Identifier, output: String },
UnexistingTreatment {
scope: Identifier,
claimed: Identifier,
},
UnexistingModel {
scope: Identifier,
claimed: Identifier,
},
UnexistingContext {
scope: Identifier,
claimed: Identifier,
},
UnexistingFunction {
scope: Identifier,
claimed: Identifier,
},
UndeclaredModel { scope: Identifier, model: String },
UndeclaredTreatment {
scope: Identifier,
treatment: String,
},
UnexistingConnectionType {
scope: Identifier,
from: String,
output: String,
to: String,
input: String,
output_flow: Flow,
output_type: DataType,
input_flow: Flow,
input_type: DataType,
},
UnsatisfiedOutput { scope: Identifier, output: String },
OverloadedOutput { scope: Identifier, output: String },
UnmatchingModelType {
scope: Identifier,
called: Identifier,
name: String,
expected: Identifier,
given_name: String,
given: Identifier,
},
UnexistingParametricModel {
scope: Identifier,
called: Identifier,
parametric_model: String,
},
UnsetModel {
scope: Identifier,
called: Identifier,
parametric_model: String,
},
AlreadyIncludedBuildStep {
treatment: Identifier,
cause_step: CheckStep,
check_steps: Vec<CheckStep>,
},
UnsatisfiedInput {
scope: Option<Identifier>,
treatment: String,
input: String,
},
ConstRequiredVarProvided {
scope: Identifier,
called: Identifier,
parameter: String,
variable: String,
},
ConstRequiredContextProvided {
scope: Identifier,
called: Identifier,
parameter: String,
context: Identifier,
entry: String,
},
ModelInstanciationConstOnly {
scope: Identifier,
called: Identifier,
name: String,
parameter: String,
},
ConstRequiredFunctionReturnsVar {
scope: Identifier,
called: Identifier,
parameter: String,
function: Identifier,
},
UnmatchingNumberOfParameters {
scope: Identifier,
function: Identifier,
},
}
impl Display for LogicErrorKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
LogicErrorKind::CollectionUndefined => write!(f, "No collection defined"),
LogicErrorKind::UncommitedDescriptor { identifier } => write!(f, "Uncommited descriptor, no designer available for '{identifier}'"),
LogicErrorKind::NoDesigner {identifier } => write!(f, "Nothing to commit, as no designer available for '{identifier}'"),
LogicErrorKind::ErroneousDesign {identifier} => write!(f, "Design for '{identifier}' contains errors and cannot be commited"),
LogicErrorKind::ErroneousChecks => write!(f, "Building coherency checks found errors, build cannot be made"),
LogicErrorKind::UnavailableDesign { identifier } => write!(f, "Unavailable design for '{identifier}'"),
LogicErrorKind::UnexistingVariable {identifier,
parameter,
variable,} => write!(f, "Referenced '{variable}' variable for '{parameter}' parameter doesn't exist in '{identifier}'"),
LogicErrorKind::UnexistingContextVariable{identifier,
parameter,
context,
variable,} => write!(f, "Referenced '{variable}' context value for '{parameter}' does not exist within '{context}' context in '{identifier}'"),
LogicErrorKind::UnexistingParameter {
scope,
called,
parameter,
} => write!(f, "Parameter '{parameter}' does not exist for '{called}' in '{scope}'"),
LogicErrorKind::UnmatchingDataType {
scope, called, parameter, value, expected, given
} => write!(f, "Datatype does not match for '{parameter}' of '{called}' in '{scope}', {expected} expected but '{value}' is {given}"),
LogicErrorKind::UnsetParameter { scope, called, parameter } => write!(f, "Parameter '{parameter}' of '{called}' is not set in '{scope}'"),
LogicErrorKind::MultipleParameterAssignation { scope, called, parameter } => write!(f, "Parameter '{parameter}' of '{called}' assigned multiple times in '{scope}'"),
LogicErrorKind::NoValue {scope, called, parameter} => write!(f, "No value assigned to '{parameter}' of '{called}' in '{scope}'"),
LogicErrorKind::NoContext{scope, model, name, parameter } => write!(f, "Context used for parameter '{parameter}' of model '{name}' type '{model}' in '{scope}', but context values cannot be used for models"),
LogicErrorKind::UnavailableContext{scope, context} => write!(f, "Context '{context}' not available in '{scope}'"),
LogicErrorKind::ConnectionInputNotFound{scope: _, to, input} => write!(f, "Input '{input}' is not provided by '{to}'"),
LogicErrorKind::ConnectionSelfInputNotFound { scope, input } => write!(f, "Input '{input}' does not exist for '{scope}'"),
LogicErrorKind::ConnectionOutputNotFound { scope: _, from, output } => write!(f, "Output '{output}' is not provided by '{from}'"),
LogicErrorKind::ConnectionSelfOutputNotFound{scope, output} => write!(f, "Output '{output}' does not exist for '{scope}'"),
LogicErrorKind::UnexistingTreatment{scope: _, claimed} => write!(f, "Treatment '{claimed}' does not exist"),
LogicErrorKind::UnexistingModel { scope: _, claimed } => write!(f, "Model '{claimed}' does not exist"),
LogicErrorKind::UnexistingContext { scope: _, claimed } => write!(f, "Context '{claimed}' does not exist"),
LogicErrorKind::UnexistingFunction{ scope: _, claimed } => write!(f, "Function '{claimed}' does not exist"),
LogicErrorKind::UndeclaredModel { scope, model } => write!(f, "Model '{model}' is not declared in '{scope}'"),
LogicErrorKind::UndeclaredTreatment { scope, treatment } => write!(f, "Treatment '{treatment}' is not declared in '{scope}'"),
LogicErrorKind::UnexistingConnectionType { scope, from, output, to, input, output_type, input_type, output_flow, input_flow } => write!(f, "Connection from '{from}' to '{to}' in '{scope}' is not possible, '{output}' is {output_flow}<{output_type}> but '{input}' is {input_flow}<{input_type}>"),
LogicErrorKind::UnsatisfiedOutput { scope, output } => write!(f, "Output '{output}' is not satisfied in '{scope}'"),
LogicErrorKind::OverloadedOutput { scope, output } => write!(f, "Output '{output}' is overloaded in '{scope}', only one connection is possible to 'Self' outputs"),
LogicErrorKind::UnmatchingModelType { scope: _, called, name, expected, given_name, given } => write!(f, "Model '{name}' for '{called}' is expected to be '{expected}', but given '{given_name}' is '{given}' and not based on it"),
LogicErrorKind::UnexistingParametricModel { scope, called, parametric_model } => write!(f, "Parametric model '{parametric_model}' does not exist for '{called}' in '{scope}'"),
LogicErrorKind::UnsetModel { scope, called, parametric_model } => write!(f, "No model assigned to '{parametric_model}' of '{called}' in '{scope}'"),
LogicErrorKind::AlreadyIncludedBuildStep { treatment , cause_step,check_steps} => write!(f, "Treatment '{treatment}' is referring to same instanciation of itself, causing infinite connection loop ({cause_step} already present in {})", check_steps.iter().map(|cs| cs.to_string()).collect::<Vec<_>>().join(", ")),
LogicErrorKind::UnsatisfiedInput { scope, treatment, input } => if let Some(id) = scope {write!(f, "Input '{input}' of '{treatment}' is not satisfied in '{id}'")} else {write!(f, "Entrypoint have input '{input}' that must be satisfied")},
LogicErrorKind::ConstRequiredVarProvided { scope, called, parameter, variable } => write!(f, "Parameter '{parameter}' of '{called}' is constant but provided '{variable}' is variable in '{scope}'"),
LogicErrorKind::ConstRequiredContextProvided { scope, called, parameter, context, entry: _ } => write!(f, "Parameter '{parameter}' of '{called}' is constant but context '{context}' is provided in '{scope}', contexts are implicitly variable"),
LogicErrorKind::ModelInstanciationConstOnly { scope, called, name, parameter } => write!(f, "Variable provided for parameter '{parameter}' of model '{name}' from type '{called}' in '{scope}', model instanciations can only get constants"),
LogicErrorKind::ConstRequiredFunctionReturnsVar { scope, called, parameter, function } => write!(f, "Parameter '{parameter}' of '{called}' is constant but provided '{function}' have variable return value in '{scope}'"),
LogicErrorKind::UnmatchingNumberOfParameters { scope, function } => write!(f, "Number of parameters given do not match for function '{function}' in '{scope}'"),
}
}
}
#[derive(Debug, Clone)]
pub struct LogicError {
pub id: u32,
pub kind: LogicErrorKind,
pub design_reference: Option<Arc<dyn Reference>>,
}
impl LogicError {
pub fn collection_undefined(id: u32, design_reference: Option<Arc<dyn Reference>>) -> Self {
Self {
id,
design_reference,
kind: LogicErrorKind::CollectionUndefined,
}
}
pub fn uncommited_descriptor(
id: u32,
identifier: Identifier,
design_reference: Option<Arc<dyn Reference>>,
) -> Self {
Self {
id,
design_reference,
kind: LogicErrorKind::UncommitedDescriptor { identifier },
}
}
pub fn no_designer(
id: u32,
identifier: Identifier,
design_reference: Option<Arc<dyn Reference>>,
) -> Self {
Self {
id,
design_reference,
kind: LogicErrorKind::NoDesigner { identifier },
}
}
pub fn erroneous_design(
id: u32,
identifier: Identifier,
design_reference: Option<Arc<dyn Reference>>,
) -> Self {
Self {
id,
design_reference,
kind: LogicErrorKind::ErroneousDesign { identifier },
}
}
pub fn erroneous_checks(id: u32, design_reference: Option<Arc<dyn Reference>>) -> Self {
Self {
id,
design_reference,
kind: LogicErrorKind::ErroneousChecks,
}
}
pub fn unavailable_design(
id: u32,
identifier: Identifier,
design_reference: Option<Arc<dyn Reference>>,
) -> Self {
Self {
id,
design_reference,
kind: LogicErrorKind::UnavailableDesign { identifier },
}
}
pub fn unexisting_variable(
id: u32,
identifier: Identifier,
parameter: String,
variable: String,
design_reference: Option<Arc<dyn Reference>>,
) -> Self {
Self {
id,
design_reference,
kind: LogicErrorKind::UnexistingVariable {
identifier,
parameter,
variable,
},
}
}
pub fn unexisting_context_variable(
id: u32,
identifier: Identifier,
parameter: String,
context: Identifier,
variable: String,
design_reference: Option<Arc<dyn Reference>>,
) -> Self {
Self {
id,
design_reference,
kind: LogicErrorKind::UnexistingContextVariable {
identifier,
parameter,
context,
variable,
},
}
}
pub fn unexisting_parameter(
id: u32,
scope: Identifier,
called: Identifier,
parameter: String,
design_reference: Option<Arc<dyn Reference>>,
) -> Self {
Self {
id,
design_reference,
kind: LogicErrorKind::UnexistingParameter {
scope,
called,
parameter,
},
}
}
pub fn unmatching_datatype(
id: u32,
scope: Identifier,
called: Identifier,
parameter: String,
value: Value,
expected: DataType,
given: DataType,
design_reference: Option<Arc<dyn Reference>>,
) -> Self {
Self {
id,
design_reference,
kind: LogicErrorKind::UnmatchingDataType {
scope,
called,
parameter,
value,
expected,
given,
},
}
}
pub fn unset_parameter(
id: u32,
scope: Identifier,
called: Identifier,
parameter: String,
design_reference: Option<Arc<dyn Reference>>,
) -> Self {
Self {
id,
design_reference,
kind: LogicErrorKind::UnsetParameter {
scope,
called,
parameter,
},
}
}
pub fn multiple_parameter_assignation(
id: u32,
scope: Identifier,
called: Identifier,
parameter: String,
design_reference: Option<Arc<dyn Reference>>,
) -> Self {
Self {
id,
design_reference,
kind: LogicErrorKind::MultipleParameterAssignation {
scope,
called,
parameter,
},
}
}
pub fn no_value(
id: u32,
scope: Identifier,
called: Identifier,
parameter: String,
design_reference: Option<Arc<dyn Reference>>,
) -> Self {
Self {
id,
design_reference,
kind: LogicErrorKind::NoValue {
scope,
called,
parameter,
},
}
}
pub fn no_context(
id: u32,
scope: Identifier,
model: Identifier,
name: String,
parameter: String,
design_reference: Option<Arc<dyn Reference>>,
) -> Self {
Self {
id,
design_reference,
kind: LogicErrorKind::NoContext {
scope,
model,
name,
parameter,
},
}
}
pub fn unavailable_context(
id: u32,
scope: Identifier,
context: Identifier,
design_reference: Option<Arc<dyn Reference>>,
) -> Self {
Self {
id,
design_reference,
kind: LogicErrorKind::UnavailableContext { scope, context },
}
}
pub fn connection_input_not_found(
id: u32,
scope: Identifier,
to: Identifier,
input: String,
design_reference: Option<Arc<dyn Reference>>,
) -> Self {
Self {
id,
design_reference,
kind: LogicErrorKind::ConnectionInputNotFound { scope, to, input },
}
}
pub fn connection_self_input_not_found(
id: u32,
scope: Identifier,
input: String,
design_reference: Option<Arc<dyn Reference>>,
) -> Self {
Self {
id,
design_reference,
kind: LogicErrorKind::ConnectionSelfInputNotFound { scope, input },
}
}
pub fn connection_output_not_found(
id: u32,
scope: Identifier,
from: Identifier,
output: String,
design_reference: Option<Arc<dyn Reference>>,
) -> Self {
Self {
id,
design_reference,
kind: LogicErrorKind::ConnectionOutputNotFound {
scope,
from,
output,
},
}
}
pub fn connection_self_output_not_found(
id: u32,
scope: Identifier,
output: String,
design_reference: Option<Arc<dyn Reference>>,
) -> Self {
Self {
id,
design_reference,
kind: LogicErrorKind::ConnectionSelfOutputNotFound { scope, output },
}
}
pub fn unexisting_treatment(
id: u32,
scope: Identifier,
claimed: Identifier,
design_reference: Option<Arc<dyn Reference>>,
) -> Self {
Self {
id,
design_reference,
kind: LogicErrorKind::UnexistingTreatment { scope, claimed },
}
}
pub fn unexisting_model(
id: u32,
scope: Identifier,
claimed: Identifier,
design_reference: Option<Arc<dyn Reference>>,
) -> Self {
Self {
id,
design_reference,
kind: LogicErrorKind::UnexistingModel { scope, claimed },
}
}
pub fn unexisting_context(
id: u32,
scope: Identifier,
claimed: Identifier,
design_reference: Option<Arc<dyn Reference>>,
) -> Self {
Self {
id,
design_reference,
kind: LogicErrorKind::UnexistingContext { scope, claimed },
}
}
pub fn unexisting_function(
id: u32,
scope: Identifier,
claimed: Identifier,
design_reference: Option<Arc<dyn Reference>>,
) -> Self {
Self {
id,
design_reference,
kind: LogicErrorKind::UnexistingFunction { scope, claimed },
}
}
pub fn undeclared_model(
id: u32,
scope: Identifier,
model: String,
design_reference: Option<Arc<dyn Reference>>,
) -> Self {
Self {
id,
design_reference,
kind: LogicErrorKind::UndeclaredModel { scope, model },
}
}
pub fn undeclared_treatment(
id: u32,
scope: Identifier,
treatment: String,
design_reference: Option<Arc<dyn Reference>>,
) -> Self {
Self {
id,
design_reference,
kind: LogicErrorKind::UndeclaredTreatment { scope, treatment },
}
}
pub fn unexisting_connexion_type(
id: u32,
scope: Identifier,
from: String,
output: String,
to: String,
input: String,
output_flow: Flow,
output_type: DataType,
input_flow: Flow,
input_type: DataType,
design_reference: Option<Arc<dyn Reference>>,
) -> Self {
Self {
id,
design_reference,
kind: LogicErrorKind::UnexistingConnectionType {
scope,
from,
output,
to,
input,
output_flow,
output_type,
input_flow,
input_type,
},
}
}
pub fn unsatisfied_output(
id: u32,
scope: Identifier,
output: String,
design_reference: Option<Arc<dyn Reference>>,
) -> Self {
Self {
id,
design_reference,
kind: LogicErrorKind::UnsatisfiedOutput { scope, output },
}
}
pub fn overloaded_output(
id: u32,
scope: Identifier,
output: String,
design_reference: Option<Arc<dyn Reference>>,
) -> Self {
Self {
id,
design_reference,
kind: LogicErrorKind::OverloadedOutput { scope, output },
}
}
pub fn unmatching_model_type(
id: u32,
scope: Identifier,
called: Identifier,
name: String,
expected: Identifier,
given_name: String,
given: Identifier,
design_reference: Option<Arc<dyn Reference>>,
) -> Self {
Self {
id,
design_reference,
kind: LogicErrorKind::UnmatchingModelType {
scope,
called,
name,
expected,
given_name,
given,
},
}
}
pub fn unexisting_parametric_model(
id: u32,
scope: Identifier,
called: Identifier,
parametric_model: String,
design_reference: Option<Arc<dyn Reference>>,
) -> Self {
Self {
id,
design_reference,
kind: LogicErrorKind::UnexistingParametricModel {
scope,
called,
parametric_model,
},
}
}
pub fn unset_model(
id: u32,
scope: Identifier,
called: Identifier,
parametric_model: String,
design_reference: Option<Arc<dyn Reference>>,
) -> Self {
Self {
id,
design_reference,
kind: LogicErrorKind::UnsetModel {
scope,
called,
parametric_model,
},
}
}
pub fn already_included_build_step(
id: u32,
treatment: Identifier,
cause_step: CheckStep,
check_steps: Vec<CheckStep>,
design_reference: Option<Arc<dyn Reference>>,
) -> Self {
Self {
id,
design_reference,
kind: LogicErrorKind::AlreadyIncludedBuildStep {
treatment,
cause_step,
check_steps,
},
}
}
pub fn unsatisfied_input(
id: u32,
scope: Option<Identifier>,
treatment: String,
input: String,
design_reference: Option<Arc<dyn Reference>>,
) -> Self {
Self {
id,
design_reference,
kind: LogicErrorKind::UnsatisfiedInput {
scope,
treatment,
input,
},
}
}
pub fn const_required_var_provided(
id: u32,
scope: Identifier,
called: Identifier,
parameter: String,
variable: String,
design_reference: Option<Arc<dyn Reference>>,
) -> Self {
Self {
id,
design_reference,
kind: LogicErrorKind::ConstRequiredVarProvided {
scope,
called,
parameter,
variable,
},
}
}
pub fn const_required_context_provided(
id: u32,
scope: Identifier,
called: Identifier,
parameter: String,
context: Identifier,
entry: String,
design_reference: Option<Arc<dyn Reference>>,
) -> Self {
Self {
id,
design_reference,
kind: LogicErrorKind::ConstRequiredContextProvided {
scope,
called,
parameter,
context,
entry,
},
}
}
pub fn model_instanciation_const_only(
id: u32,
scope: Identifier,
called: Identifier,
name: String,
parameter: String,
design_reference: Option<Arc<dyn Reference>>,
) -> Self {
Self {
id,
design_reference,
kind: LogicErrorKind::ModelInstanciationConstOnly {
scope,
called,
name,
parameter,
},
}
}
pub fn const_required_function_returns_var(
id: u32,
scope: Identifier,
called: Identifier,
parameter: String,
function: Identifier,
design_reference: Option<Arc<dyn Reference>>,
) -> Self {
Self {
id,
design_reference,
kind: LogicErrorKind::ConstRequiredFunctionReturnsVar {
scope,
called,
parameter,
function,
},
}
}
pub fn unmatching_number_of_parameters(
id: u32,
scope: Identifier,
function: Identifier,
design_reference: Option<Arc<dyn Reference>>,
) -> Self {
Self {
id,
design_reference,
kind: LogicErrorKind::UnmatchingNumberOfParameters { scope, function },
}
}
}
impl Display for LogicError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "D{:04}: {}", self.id, self.kind)
}
}
pub type LogicErrors = Vec<LogicError>;
pub type LogicResult<T> = Status<T, LogicError, LogicError>;
impl From<LogicError> for LogicErrors {
fn from(value: LogicError) -> Self {
vec![value]
}
}