Skip to main content

linkrs_query/executor/base/
execution_result.rs

1//! Type of execution result
2//!
3//! Defines the data structure of the actuator's execution result, supporting multiple result types.
4
5use linkrs_core::error::DBError;
6use linkrs_core::types::{SpaceStatus, SpaceSummary};
7use linkrs_core::{DataSet, Value};
8
9/// Type of execution result
10///
11/// Uniformly represents the execution results of all actuators and supports multiple data formats.
12#[derive(Debug, Clone)]
13pub enum ExecutionResult {
14    /// Successful execution returns structured dataset (primary result type)
15    DataSet { data: DataSet },
16    /// Successful execution, no data returned
17    Empty,
18    /// Successful execution, no data returned (alias)
19    Success,
20    /// Space switched successfully, contains the new space info
21    SpaceSwitched(SpaceSummary),
22    /// implementation error
23    Error(String),
24    /// Configuration update intent produced by `UPDATE CONFIGS`.
25    ///
26    /// The query engine parses the statement and evaluates the assigned
27    /// value but never touches global configuration storage: the host
28    /// (network service or embedding) applies the intent with its own
29    /// validation, persistence, and restart semantics.
30    ConfigUpdate {
31        module: Option<String>,
32        name: String,
33        value: Value,
34    },
35    /// Configuration listing intent produced by `SHOW CONFIGS`.
36    ///
37    /// The engine carries no configuration store, so the host resolves the
38    /// intent against the live configuration and returns the rows.
39    ShowConfigs { module: Option<String> },
40}
41
42impl ExecutionResult {
43    /// Get the count of elements in the result
44    pub fn count(&self) -> usize {
45        match self {
46            ExecutionResult::DataSet { data, .. } => data.row_count(),
47            ExecutionResult::Success => 0,
48            ExecutionResult::Empty => 0,
49            ExecutionResult::SpaceSwitched(_) => 0,
50            ExecutionResult::Error(_) => 0,
51            ExecutionResult::ConfigUpdate { .. } => 0,
52            ExecutionResult::ShowConfigs { .. } => 0,
53        }
54    }
55
56    /// Creating an ExecutionResult from a DataSet
57    pub fn from_data_set(data: DataSet) -> Self {
58        ExecutionResult::DataSet { data }
59    }
60
61    /// Convert to DataSet
62    pub fn to_data_set(&self) -> Option<&DataSet> {
63        match self {
64            ExecutionResult::DataSet { data, .. } => Some(data),
65            _ => None,
66        }
67    }
68
69    /// Check if this is a space switched result
70    pub fn is_space_switched(&self) -> bool {
71        matches!(self, ExecutionResult::SpaceSwitched(_))
72    }
73
74    /// Get the space summary if this is a SpaceSwitched result
75    pub fn space_summary(&self) -> Option<&SpaceSummary> {
76        match self {
77            ExecutionResult::SpaceSwitched(summary) => Some(summary),
78            _ => None,
79        }
80    }
81}
82
83/// Result type alias
84pub type DBResult<T> = Result<T, DBError>;
85
86/// Support for traits that are converted to execution results
87pub trait IntoExecutionResult {
88    fn into_execution_result(self) -> ExecutionResult;
89}
90
91impl IntoExecutionResult for DataSet {
92    fn into_execution_result(self) -> ExecutionResult {
93        ExecutionResult::DataSet { data: self }
94    }
95}
96
97impl IntoExecutionResult for () {
98    fn into_execution_result(self) -> ExecutionResult {
99        ExecutionResult::Success
100    }
101}
102
103/// Check if a space is writable for write operations
104pub fn check_space_writable(space: &SpaceSummary) -> Result<(), ExecutionResult> {
105    match space.status {
106        SpaceStatus::Online => Ok(()),
107        SpaceStatus::Maintenance => Err(ExecutionResult::Error(format!(
108            "Space '{}' is in maintenance mode. Write operations are not allowed.",
109            space.name
110        ))),
111        SpaceStatus::ReadOnly => Err(ExecutionResult::Error(format!(
112            "Space '{}' is in read-only mode. Write operations are not allowed.",
113            space.name
114        ))),
115        SpaceStatus::Offline => Err(ExecutionResult::Error(format!(
116            "Space '{}' is offline. Operations are not allowed.",
117            space.name
118        ))),
119    }
120}
121
122/// Check if a space is accessible for read operations
123pub fn check_space_accessible(space: &SpaceSummary) -> Result<(), ExecutionResult> {
124    if space.is_accessible() {
125        Ok(())
126    } else {
127        Err(ExecutionResult::Error(format!(
128            "Space '{}' is offline. Operations are not allowed.",
129            space.name
130        )))
131    }
132}
133
134#[cfg(test)]
135mod tests {
136    use super::*;
137    use linkrs_core::types::DataType;
138
139    fn create_test_summary_with_status(status: SpaceStatus) -> SpaceSummary {
140        SpaceSummary {
141            id: 1,
142            name: "test_space".to_string(),
143            vid_type: DataType::String,
144            status,
145        }
146    }
147
148    #[test]
149    fn test_check_space_writable_online() {
150        let summary = create_test_summary_with_status(SpaceStatus::Online);
151        assert!(check_space_writable(&summary).is_ok());
152    }
153
154    #[test]
155    fn test_check_space_writable_maintenance() {
156        let summary = create_test_summary_with_status(SpaceStatus::Maintenance);
157        let result = check_space_writable(&summary);
158        assert!(result.is_err());
159        if let Err(ExecutionResult::Error(msg)) = result {
160            assert!(msg.contains("maintenance mode"));
161        }
162    }
163
164    #[test]
165    fn test_check_space_writable_readonly() {
166        let summary = create_test_summary_with_status(SpaceStatus::ReadOnly);
167        let result = check_space_writable(&summary);
168        assert!(result.is_err());
169        if let Err(ExecutionResult::Error(msg)) = result {
170            assert!(msg.contains("read-only mode"));
171        }
172    }
173
174    #[test]
175    fn test_check_space_writable_offline() {
176        let summary = create_test_summary_with_status(SpaceStatus::Offline);
177        let result = check_space_writable(&summary);
178        assert!(result.is_err());
179        if let Err(ExecutionResult::Error(msg)) = result {
180            assert!(msg.contains("offline"));
181        }
182    }
183
184    #[test]
185    fn test_check_space_accessible() {
186        let online = create_test_summary_with_status(SpaceStatus::Online);
187        let maintenance = create_test_summary_with_status(SpaceStatus::Maintenance);
188        let readonly = create_test_summary_with_status(SpaceStatus::ReadOnly);
189        let offline = create_test_summary_with_status(SpaceStatus::Offline);
190
191        assert!(check_space_accessible(&online).is_ok());
192        assert!(check_space_accessible(&maintenance).is_ok());
193        assert!(check_space_accessible(&readonly).is_ok());
194        assert!(check_space_accessible(&offline).is_err());
195    }
196}