Skip to main content

shape_runtime/context/
config.rs

1//! Configuration methods for ExecutionContext
2//!
3//! Handles ID, timeframe, date range, and execution mode settings.
4
5use super::super::lookahead_guard::{DataAccessMode, LookAheadGuard};
6use crate::data::Timeframe;
7use chrono::{DateTime, Utc};
8use shape_ast::error::{Result, ShapeError};
9
10impl super::ExecutionContext {
11    /// Set the current ID
12    pub fn set_id(&mut self, id: &str) {
13        self.current_id = Some(id.to_string());
14    }
15
16    /// Get the ID being analyzed
17    pub fn id(&self) -> Result<&str> {
18        self.current_id
19            .as_deref()
20            .ok_or_else(|| ShapeError::RuntimeError {
21                message: "No ID set".to_string(),
22                location: None,
23            })
24    }
25
26    /// Get the current ID (owned string)
27    pub fn get_current_id(&self) -> Result<String> {
28        self.current_id
29            .clone()
30            .ok_or_else(|| ShapeError::RuntimeError {
31                message: "No data loaded".to_string(),
32                location: None,
33            })
34    }
35
36    /// Get the timeframe
37    pub fn timeframe(&self) -> Result<&Timeframe> {
38        self.current_timeframe
39            .as_ref()
40            .ok_or_else(|| ShapeError::RuntimeError {
41                message: "No timeframe set".to_string(),
42                location: None,
43            })
44    }
45
46    /// Get the current timeframe
47    pub fn get_current_timeframe(&self) -> Result<Timeframe> {
48        self.current_timeframe
49            .ok_or_else(|| ShapeError::RuntimeError {
50                message: "No timeframe set in context".to_string(),
51                location: None,
52            })
53    }
54
55    /// Set the current timeframe
56    pub fn set_current_timeframe(&mut self, timeframe: Timeframe) -> Result<()> {
57        self.current_timeframe = Some(timeframe);
58        Ok(())
59    }
60
61    /// Update ID and timeframe from a DataFrame
62    pub fn update_data(&mut self, data: &super::super::data::DataFrame) {
63        self.current_id = Some(data.id.clone());
64        self.current_timeframe = Some(data.timeframe);
65        // Set current row to last row so [-1] gives most recent value
66        // This is correct for non-simulation contexts where we want full history access
67        self.current_row_index = if data.row_count() == 0 {
68            0
69        } else {
70            data.row_count() - 1
71        };
72    }
73
74    /// Get the loaded date range (if any) as native DateTime values
75    pub fn get_date_range(&self) -> Option<(DateTime<Utc>, DateTime<Utc>)> {
76        self.date_range
77    }
78
79    /// Set the date range for data loading with native DateTime values
80    pub fn set_date_range(&mut self, start: DateTime<Utc>, end: DateTime<Utc>) {
81        self.date_range = Some((start, end));
82    }
83
84    /// Set the date range by parsing ISO8601 strings
85    ///
86    /// Accepts common date formats:
87    /// - ISO8601: "2024-01-01T00:00:00Z"
88    /// - Date only: "2024-01-01" (assumes start of day UTC)
89    pub fn set_date_range_parsed(&mut self, start: &str, end: &str) -> Result<()> {
90        let parse_date = |s: &str| -> Result<DateTime<Utc>> {
91            // Try full ISO8601 first
92            if let Ok(dt) = DateTime::parse_from_rfc3339(s) {
93                return Ok(dt.with_timezone(&Utc));
94            }
95            // Try date-only format
96            if let Ok(nd) = chrono::NaiveDate::parse_from_str(s, "%Y-%m-%d") {
97                return Ok(nd
98                    .and_hms_opt(0, 0, 0)
99                    .ok_or_else(|| ShapeError::RuntimeError {
100                        message: format!("Invalid time for date: {}", s),
101                        location: None,
102                    })?
103                    .and_utc());
104            }
105            Err(ShapeError::RuntimeError {
106                message: format!("Cannot parse date '{}': expected ISO8601 or YYYY-MM-DD", s),
107                location: None,
108            })
109        };
110
111        let start_dt = parse_date(start)?;
112        let end_dt = parse_date(end)?;
113        self.date_range = Some((start_dt, end_dt));
114        Ok(())
115    }
116
117    /// Set the reference datetime for row indexing
118    pub fn set_reference_datetime(&mut self, datetime: DateTime<Utc>) {
119        self.reference_datetime = Some(datetime);
120    }
121
122    /// Get the reference datetime
123    pub fn get_reference_datetime(&self) -> Option<DateTime<Utc>> {
124        self.reference_datetime
125    }
126
127    /// Update current row to match reference datetime
128    pub fn sync_to_reference_datetime(&mut self) -> Result<()> {
129        if self.reference_datetime.is_some() {
130            self.current_row_index = 0; // Reset to beginning
131        }
132        Ok(())
133    }
134
135    /// Set the data access mode for future data validation
136    pub fn set_data_access_mode(&mut self, mode: DataAccessMode, strict: bool) {
137        self.lookahead_guard = Some(LookAheadGuard::new(mode, strict));
138    }
139
140    /// Get the look-ahead guard
141    pub fn lookahead_guard(&self) -> Option<&LookAheadGuard> {
142        self.lookahead_guard.as_ref()
143    }
144}