Files
ARCADIA-Studio/crates/oa-core/src/analysis/resolve.rs
T

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,
})
}
}