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