Skip to main content

antecedent_core/query/
transport.rs

1//! Structural transportability queries.
2//!
3//! SPDX-License-Identifier: MIT OR Apache-2.0
4
5use std::sync::Arc;
6
7use crate::VariableId;
8
9use super::{QueryError, ResponseQuery};
10
11/// Transport a response from one explicitly named population to another.
12#[derive(Clone, Debug, PartialEq)]
13pub struct TransportQuery {
14    /// Response functional requested in the target population.
15    pub response: ResponseQuery,
16    /// Source population key.
17    pub source_population: Arc<str>,
18    /// Target population key.
19    pub target_population: Arc<str>,
20    /// Variables for which source experiments are available.
21    pub source_experiments: Arc<[VariableId]>,
22}
23
24impl TransportQuery {
25    /// Construct a single-source transport query.
26    #[must_use]
27    pub fn new(
28        response: ResponseQuery,
29        source_population: impl Into<Arc<str>>,
30        target_population: impl Into<Arc<str>>,
31        source_experiments: impl Into<Arc<[VariableId]>>,
32    ) -> Self {
33        Self {
34            response,
35            source_population: source_population.into(),
36            target_population: target_population.into(),
37            source_experiments: source_experiments.into(),
38        }
39    }
40
41    /// Validate population keys, response semantics, and experiment uniqueness.
42    ///
43    /// # Errors
44    ///
45    /// [`QueryError::InvalidTransport`] or the nested response validation error.
46    pub fn validate(&self) -> Result<(), QueryError> {
47        self.response.validate()?;
48        if self.source_population.trim().is_empty()
49            || self.target_population.trim().is_empty()
50            || self.source_population == self.target_population
51        {
52            return Err(QueryError::InvalidTransport(
53                "source and target population keys must be non-empty and distinct".into(),
54            ));
55        }
56        let mut experiments = self.source_experiments.to_vec();
57        experiments.sort_unstable_by_key(|id| id.raw());
58        if experiments.windows(2).any(|pair| pair[0] == pair[1]) {
59            return Err(QueryError::InvalidTransport(
60                "source experiment variables must be unique".into(),
61            ));
62        }
63        Ok(())
64    }
65}