Skip to main content

icydb_core/db/response/
rows.rs

1//! Module: response::rows
2//! Responsibility: engine-neutral projected-row response payloads.
3//! Does not own: query execution, SQL adaptation, or typed row decoding.
4//! Boundary: shared dynamic/SQL projection output over accepted public values.
5
6use crate::value::{OutputValue, render_output_value_text};
7use candid::CandidType;
8use serde::Deserialize;
9
10/// Row-oriented output from one accepted-schema-driven projection.
11#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq)]
12pub struct RowProjectionOutput {
13    /// Accepted entity name used for the read.
14    pub entity: String,
15    /// Selected output-column names in row order.
16    pub columns: Vec<String>,
17    /// Row-oriented output values.
18    pub rows: Vec<Vec<OutputValue>>,
19    /// Number of returned rows.
20    pub row_count: u32,
21}
22
23impl RowProjectionOutput {
24    /// Return the number of rows carried by this output.
25    #[must_use]
26    pub const fn len(&self) -> usize {
27        self.rows.len()
28    }
29
30    /// Return whether this output carries no rows.
31    #[must_use]
32    pub const fn is_empty(&self) -> bool {
33        self.rows.is_empty()
34    }
35
36    /// Render row values into stable display strings.
37    #[must_use]
38    pub fn rendered_rows(&self) -> Vec<Vec<String>> {
39        self.rows
40            .iter()
41            .map(|row| row.iter().map(render_output_value_text).collect())
42            .collect()
43    }
44}
45
46#[cfg(test)]
47mod tests {
48    use super::*;
49
50    #[derive(CandidType)]
51    struct FrozenRowProjectionOutputWire {
52        entity: String,
53        columns: Vec<String>,
54        rows: Vec<Vec<OutputValue>>,
55        row_count: u32,
56    }
57
58    #[test]
59    fn row_projection_output_preserves_the_frozen_candid_record_shape() {
60        let current = RowProjectionOutput {
61            entity: "example".to_string(),
62            columns: vec!["id".to_string()],
63            rows: Vec::new(),
64            row_count: 0,
65        };
66        let frozen = FrozenRowProjectionOutputWire {
67            entity: current.entity.clone(),
68            columns: current.columns.clone(),
69            rows: current.rows.clone(),
70            row_count: current.row_count,
71        };
72
73        assert_eq!(current.len(), 0);
74        assert!(current.is_empty());
75
76        assert_eq!(
77            candid::encode_one(&current).expect("current row projection should encode"),
78            candid::encode_one(&frozen).expect("frozen row projection should encode"),
79        );
80    }
81}