Skip to main content

shape_runtime/data/
load_query.rs

1//! Generic load query for industry-agnostic data loading.
2//!
3//! Phase 1.B (ADR-006 §2.7.4 audit-accuracy ruling): the
4//! `to_data_query` body decoded `&ValueWord` parameters via tag-bit
5//! dispatch (`as_str()`, `as_time()`, `as_timeframe()`, `as_duration()`,
6//! `as_f64()`) that no longer exists. The kind-threaded rebuild
7//! lands in Phase 2c when the per-position `NativeKind` is threaded
8//! from the schema; until then the body returns a deferred error and
9//! the type signature is preserved at the [`KindedSlot`] shape per
10//! ADR-006 §2.7.5.
11
12use super::DataQuery;
13use shape_ast::error::{Result, ShapeError};
14use shape_value::KindedSlot;
15use std::collections::HashMap;
16
17/// Generic data load request (industry-agnostic).
18#[derive(Debug, Clone, Default)]
19pub struct LoadQuery {
20    /// Provider name (e.g., "data", "api", "warehouse"). If `None`,
21    /// uses the default provider.
22    pub provider: Option<String>,
23
24    /// Generic parameters (arbitrary key-value). `KindedSlot`'s explicit
25    /// `Drop`/`Clone` impls dispatch on `NativeKind` so each parameter's
26    /// heap refcount is released on teardown.
27    pub params: HashMap<String, KindedSlot>,
28
29    /// Target type name for validation (e.g., "Candle", "TickData").
30    pub target_type: Option<String>,
31
32    /// Optional column mapping override.
33    pub column_mapping: Option<HashMap<String, String>>,
34}
35
36impl LoadQuery {
37    /// Create a new empty load query.
38    pub fn new() -> Self {
39        Self::default()
40    }
41
42    /// Set the provider name.
43    pub fn with_provider(mut self, name: &str) -> Self {
44        self.provider = Some(name.to_string());
45        self
46    }
47
48    /// Add a parameter.
49    pub fn with_param(mut self, key: &str, value: KindedSlot) -> Self {
50        self.params.insert(key.to_string(), value);
51        self
52    }
53
54    /// Set the target type for validation.
55    pub fn with_type(mut self, type_name: &str) -> Self {
56        self.target_type = Some(type_name.to_string());
57        self
58    }
59
60    /// Set the column mapping.
61    pub fn with_column_mapping(mut self, mapping: HashMap<String, String>) -> Self {
62        self.column_mapping = Some(mapping);
63        self
64    }
65
66    /// Convert to a provider-specific [`DataQuery`].
67    ///
68    /// Phase 1.B: the param-decode helpers (`value_to_timestamp`,
69    /// `as_timeframe`/`as_duration` decoders) are deferred to Phase 2c.
70    /// Until then, this method returns a deferred error rather than
71    /// silently produce a malformed `DataQuery`.
72    pub fn to_data_query(&self) -> Result<DataQuery> {
73        Err(ShapeError::RuntimeError {
74            message: "LoadQuery::to_data_query: pending Phase 2c kind-threaded param decode — see ADR-006 §2.7.4".to_string(),
75            location: None,
76        })
77    }
78}
79
80#[cfg(test)]
81mod tests {
82    // Pre-bulldozer behavioural tests covered the `to_data_query`
83    // happy-path / error-path / range / limit shapes. They return
84    // alongside the kind-threaded rebuild in Phase 2c.
85
86    use super::*;
87
88    #[test]
89    fn test_basic_query_metadata() {
90        // Metadata-only test that doesn't invoke the deferred body.
91        let query = LoadQuery::new()
92            .with_provider("data")
93            .with_type("Candle");
94
95        assert_eq!(query.provider, Some("data".to_string()));
96        assert_eq!(query.target_type, Some("Candle".to_string()));
97    }
98}