Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
147 changes: 135 additions & 12 deletions tools/routine-compiler/src/declaration_pipeline.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
//! Typed composition of declaration requirements, scalar binding, and syntax checks.
//! Typed composition of declaration requirements, scalar binding, source acquisition,
//! and syntax checks.
//!
//! Requirements and binding fan out from caller-supplied source claims. Source
//! bytes remain caller-owned, and successful output is the detached bound
//! projection rather than persisted declaration evidence.
//! Requirements and binding fan out from caller-supplied source claims. The
//! in-memory entry point borrows caller-owned bytes; the root entry point reads
//! them through caller-supplied directory capabilities. Successful output is
//! the detached bound projection rather than persisted declaration evidence.

use std::fmt;

use crate::bound_scalars::{BoundScalarError, BoundScalarProjection, bind_scalar_source_claims};
use crate::declaration_requirements::{
DeclarationRequirementError, project_declaration_requirements,
DeclarationRequirementError, DeclarationRequirementProjection, project_declaration_requirements,
};
use crate::declaration_source::{
DeclarationSourceError, DeclarationSourceLimits, DeclarationSourceRoots,
read_declaration_sources,
};
use crate::declaration_syntax::{
DeclarationSourceDocument, DeclarationSyntaxError, DeclarationSyntaxLimits,
Expand All @@ -17,6 +23,43 @@ use crate::declaration_syntax::{
use crate::scalar_names::NamedScalarProjection;
use crate::scalar_source_claims::ScalarSourceClaimProjection;

/// Inclusive bounds shared by root acquisition and declaration syntax checking.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct DeclarationRootPipelineLimits {
/// Maximum number of unique source documents.
pub max_documents: usize,
/// Maximum number of owner declaration requirements.
pub max_requirements: usize,
/// Maximum byte count for one source document.
pub max_source_bytes: usize,
/// Maximum byte count across all source documents.
pub max_total_source_bytes: usize,
/// Maximum number of direct members in one parsed owner class.
pub max_direct_members: usize,
}

impl From<DeclarationRootPipelineLimits> for DeclarationSourceLimits {
fn from(limits: DeclarationRootPipelineLimits) -> Self {
Self {
max_documents: limits.max_documents,
max_source_bytes: limits.max_source_bytes,
max_total_source_bytes: limits.max_total_source_bytes,
}
}
}

impl From<DeclarationRootPipelineLimits> for DeclarationSyntaxLimits {
fn from(limits: DeclarationRootPipelineLimits) -> Self {
Self {
max_documents: limits.max_documents,
max_requirements: limits.max_requirements,
max_source_bytes: limits.max_source_bytes,
max_total_source_bytes: limits.max_total_source_bytes,
max_direct_members: limits.max_direct_members,
}
}
}

/// Failure from the first declaration pipeline stage that rejects its input.
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum DeclarationPipelineError {
Expand Down Expand Up @@ -50,6 +93,64 @@ impl std::error::Error for DeclarationPipelineError {
}
}

/// Failure from source acquisition or the typed declaration pipeline around it.
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum DeclarationRootPipelineError {
/// Requirements, binding, or syntax checking failed.
Pipeline(DeclarationPipelineError),
/// Capability-relative source acquisition failed.
Source(DeclarationSourceError),
}

impl fmt::Display for DeclarationRootPipelineError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Pipeline(error) => write!(formatter, "declaration pipeline failed: {error}"),
Self::Source(error) => {
write!(formatter, "declaration source acquisition failed: {error}")
}
}
}
}

impl std::error::Error for DeclarationRootPipelineError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::Pipeline(error) => Some(error),
Self::Source(error) => Some(error),
}
}
}

struct PreparedDeclarationPipeline {
requirements: DeclarationRequirementProjection,
bound_projection: BoundScalarProjection,
}

fn prepare_declaration_pipeline(
named_projection: &NamedScalarProjection,
source_claim_projection: &ScalarSourceClaimProjection,
) -> Result<PreparedDeclarationPipeline, DeclarationPipelineError> {
let requirements = project_declaration_requirements(source_claim_projection)
.map_err(DeclarationPipelineError::Requirements)?;
let bound_projection = bind_scalar_source_claims(named_projection, source_claim_projection)
.map_err(DeclarationPipelineError::Binding)?;
Ok(PreparedDeclarationPipeline {
requirements,
bound_projection,
})
}

fn complete_declaration_pipeline(
prepared: PreparedDeclarationPipeline,
documents: &[DeclarationSourceDocument],
limits: DeclarationSyntaxLimits,
) -> Result<BoundScalarProjection, DeclarationPipelineError> {
check_owner_declaration_syntax(&prepared.requirements, documents, limits)
.map_err(DeclarationPipelineError::Syntax)?;
Ok(prepared.bound_projection)
}

/// Projects requirements, binds scalar claims, and checks supplied source bytes.
///
/// Requirements are derived directly from `source_claim_projection`; the bound
Expand All @@ -65,13 +166,35 @@ pub fn check_declaration_pipeline(
documents: &[DeclarationSourceDocument],
limits: DeclarationSyntaxLimits,
) -> Result<BoundScalarProjection, DeclarationPipelineError> {
let requirements = project_declaration_requirements(source_claim_projection)
.map_err(DeclarationPipelineError::Requirements)?;
let bound_projection = bind_scalar_source_claims(named_projection, source_claim_projection)
.map_err(DeclarationPipelineError::Binding)?;
check_owner_declaration_syntax(&requirements, documents, limits)
.map_err(DeclarationPipelineError::Syntax)?;
Ok(bound_projection)
let prepared = prepare_declaration_pipeline(named_projection, source_claim_projection)?;
complete_declaration_pipeline(prepared, documents, limits)
}

/// Projects requirements, binds scalar claims, acquires source bytes, and checks syntax.
///
/// Both directory capabilities remain borrowed. Requirements and binding finish
/// before either root is read; acquisition finishes before syntax checking. The
/// bound projection is returned only after every stage succeeds.
///
/// # Errors
///
/// Returns the first failed stage with its unchanged pipeline or source error.
pub fn check_declaration_pipeline_from_roots(
named_projection: &NamedScalarProjection,
source_claim_projection: &ScalarSourceClaimProjection,
roots: DeclarationSourceRoots<'_>,
limits: DeclarationRootPipelineLimits,
) -> Result<BoundScalarProjection, DeclarationRootPipelineError> {
let prepared = prepare_declaration_pipeline(named_projection, source_claim_projection)
.map_err(DeclarationRootPipelineError::Pipeline)?;
let documents = read_declaration_sources(
&prepared.requirements,
roots,
DeclarationSourceLimits::from(limits),
)
.map_err(DeclarationRootPipelineError::Source)?;
complete_declaration_pipeline(prepared, &documents, DeclarationSyntaxLimits::from(limits))
.map_err(DeclarationRootPipelineError::Pipeline)
}

#[cfg(test)]
Expand Down
Loading
Loading