vantage_cmd/table_source/
detail.rs1use ciborium::Value as CborValue;
4use indexmap::IndexMap;
5use serde_json::Value as JsonValue;
6use vantage_core::error;
7use vantage_dataset::traits::Result;
8use vantage_table::table::Table;
9use vantage_types::{Entity, Record};
10
11use super::list::rows_to_records;
12use crate::cmd::Cmd;
13use crate::rhai_engine::QueryContext;
14use crate::types::cbor_to_string;
15
16fn detail_row(
21 mut records: IndexMap<String, Record<CborValue>>,
22 id_field: Option<&str>,
23 id: &str,
24) -> Option<Record<CborValue>> {
25 if let Some(row) = records.shift_remove(id) {
26 return Some(row);
27 }
28 if records.len() != 1 {
29 return None;
30 }
31 let (_, mut row) = records.pop()?;
32 let carried = id_field
33 .and_then(|f| row.get(f))
34 .is_some_and(|v| !cbor_to_string(v).is_empty());
35 if carried {
36 return None;
37 }
38 if let Some(field) = id_field {
39 row.insert(field.to_string(), CborValue::Text(id.to_string()));
40 }
41 Some(row)
42}
43
44impl Cmd {
45 pub async fn get_table_value_with_row<E>(
51 &self,
52 table: &Table<Self, E>,
53 id: &str,
54 row: &Record<CborValue>,
55 ) -> Result<Option<Record<CborValue>>>
56 where
57 E: Entity<CborValue>,
58 Self: Sized,
59 {
60 let table_name = table.table_name().to_string();
61 if !self.has_detail_script(&table_name) {
62 let mut all = self.list_rows(table).await?;
63 return Ok(all.shift_remove(id));
64 }
65
66 let id_field = table.id_field().map(|c| c.name().to_string());
67 let columns: Vec<String> = table.columns().keys().cloned().collect();
68 let row_map = CborValue::Map(
69 row.iter()
70 .map(|(k, v)| (CborValue::Text(k.clone()), v.clone()))
71 .collect(),
72 );
73 let ctx = QueryContext {
74 conditions: Vec::new(),
75 columns,
76 limit: None,
77 offset: None,
78 id_column: id_field.clone(),
79 id: Some(id.to_string()),
80 row: row_map,
81 };
82 let cmd = self.clone();
83 let rows: Vec<JsonValue> = tokio::task::spawn_blocking(move || {
84 let compiled = cmd
85 .compiled_detail_script(&table_name)?
86 .ok_or_else(|| error!("detail script vanished"))?;
87 compiled.eval(ctx)
88 })
89 .await
90 .map_err(|e| error!("command task failed to join", detail = e.to_string()))??;
91
92 Ok(detail_row(
93 rows_to_records(rows, id_field.as_deref()),
94 id_field.as_deref(),
95 id,
96 ))
97 }
98}