147 lines
5.1 KiB
Rust
147 lines
5.1 KiB
Rust
use super::*;
|
|
use crate::analysis_numeric::{NumericType, ResolvedKind, Unit};
|
|
use crate::{Id, Project, SharedBody};
|
|
use std::collections::BTreeMap;
|
|
|
|
/// Bounded local nominal origin; no rooted graph is cloned during arithmetic.
|
|
/// Full ResolvedKind equality additionally includes dimension/algebra/difference.
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
|
pub struct KindHandle {
|
|
model: Id,
|
|
definition: Id,
|
|
}
|
|
impl KindHandle {
|
|
pub fn definition(self) -> Id {
|
|
self.definition
|
|
}
|
|
pub(crate) fn local(model: Id, definition: Id) -> Self {
|
|
Self { model, definition }
|
|
}
|
|
}
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
pub enum ResolvedScalar {
|
|
Boolean,
|
|
Numeric(NumericType<KindHandle>),
|
|
}
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
pub enum ResolvedType {
|
|
Scalar(ResolvedScalar),
|
|
List(ResolvedScalar),
|
|
}
|
|
impl ResolvedType {
|
|
pub(crate) fn boolean() -> Self {
|
|
Self::Scalar(ResolvedScalar::Boolean)
|
|
}
|
|
pub(crate) fn numeric(value: NumericType<KindHandle>) -> Self {
|
|
Self::Scalar(ResolvedScalar::Numeric(value.normalized()))
|
|
}
|
|
}
|
|
#[derive(Debug, Clone, Copy)]
|
|
pub struct EffectiveBinding<'a> {
|
|
pub binding: &'a Binding,
|
|
pub origin: BindingOrigin,
|
|
}
|
|
|
|
/// Constructed only by admission; exact source/capture provenance stays with its owner.
|
|
/// Values from different accepted environments must not be mixed by an evaluator.
|
|
pub struct ResolvedAnalysis<'p> {
|
|
pub(super) project: &'p Project,
|
|
pub(super) kinds: BTreeMap<Id, ResolvedKind<KindHandle>>,
|
|
pub(super) units: BTreeMap<Id, Unit<KindHandle>>,
|
|
pub(super) limits: AdmissionLimits,
|
|
pub(super) usage: AdmissionUsage,
|
|
}
|
|
impl<'p> ResolvedAnalysis<'p> {
|
|
pub fn project(&self) -> &'p Project {
|
|
self.project
|
|
}
|
|
pub fn admission_usage(&self) -> AdmissionUsage {
|
|
self.usage
|
|
}
|
|
pub fn admission_limits(&self) -> AdmissionLimits {
|
|
self.limits
|
|
}
|
|
pub fn definition(&self, reference: DefinitionRef) -> Option<&'p Definition> {
|
|
self.project.definitions.get(&reference.local())
|
|
}
|
|
pub fn kind(&self, reference: DefinitionRef) -> Option<&ResolvedKind<KindHandle>> {
|
|
self.kinds.get(&reference.local())
|
|
}
|
|
pub fn unit(&self, reference: DefinitionRef) -> Option<&Unit<KindHandle>> {
|
|
self.units.get(&reference.local())
|
|
}
|
|
pub fn calculation(&self, reference: DefinitionRef) -> Option<&'p CalculationDefinition> {
|
|
match &self.definition(reference)?.body {
|
|
DefinitionBody::Calculation(value) => Some(value),
|
|
_ => None,
|
|
}
|
|
}
|
|
pub fn declared_type(&self, value: &AnalysisType) -> Option<ResolvedType> {
|
|
let (AnalysisType::Scalar(scalar) | AnalysisType::List(scalar)) = value;
|
|
let scalar = match scalar {
|
|
ScalarType::Boolean => ResolvedScalar::Boolean,
|
|
ScalarType::Number => ResolvedScalar::Numeric(NumericType::Number),
|
|
ScalarType::Quantity(reference) => {
|
|
ResolvedScalar::Numeric(NumericType::Named(self.kind(*reference)?.clone()))
|
|
}
|
|
};
|
|
Some(match value {
|
|
AnalysisType::Scalar(_) => ResolvedType::Scalar(scalar),
|
|
AnalysisType::List(_) => ResolvedType::List(scalar),
|
|
})
|
|
}
|
|
pub fn effective_binding(
|
|
&self,
|
|
property: Id,
|
|
context: EvaluationContext,
|
|
) -> Result<EffectiveBinding<'p>, AnalysisError> {
|
|
let element = self.project.elements.get(&property).ok_or_else(|| {
|
|
AnalysisError::new(
|
|
property,
|
|
"context",
|
|
"analysis_property",
|
|
"Property does not exist.",
|
|
)
|
|
})?;
|
|
let Some(SharedBody::EngineeringProperty { base, .. }) = element.shared() else {
|
|
return Err(AnalysisError::new(
|
|
property,
|
|
"context",
|
|
"analysis_property",
|
|
"Target is not an engineering property.",
|
|
));
|
|
};
|
|
if let EvaluationContext::Case(id) = context {
|
|
let case = self.project.elements.get(&id).ok_or_else(|| {
|
|
AnalysisError::new(id, "context", "analysis_case", "Case does not exist.")
|
|
})?;
|
|
let Some(SharedBody::AnalysisCase { overrides, .. }) = case.shared() else {
|
|
return Err(AnalysisError::new(
|
|
id,
|
|
"context",
|
|
"analysis_case",
|
|
"Context is not an analysis case.",
|
|
));
|
|
};
|
|
if case.layer() != element.layer() {
|
|
return Err(AnalysisError::new(
|
|
id,
|
|
"context",
|
|
"analysis_layer",
|
|
"Case and property must share a layer.",
|
|
));
|
|
}
|
|
if let Some(binding) = overrides.get(&property) {
|
|
return Ok(EffectiveBinding {
|
|
binding,
|
|
origin: BindingOrigin::CaseOverride(id),
|
|
});
|
|
}
|
|
}
|
|
Ok(EffectiveBinding {
|
|
binding: base,
|
|
origin: BindingOrigin::ModelBaseline,
|
|
})
|
|
}
|
|
}
|