haystack_server/ops/
nav.rs1use actix_web::{HttpRequest, HttpResponse, web};
4
5use haystack_core::data::{HCol, HDict, HGrid};
6use haystack_core::kinds::Kind;
7
8use crate::content;
9use crate::error::HaystackError;
10use crate::state::AppState;
11
12pub async fn handle(
19 req: HttpRequest,
20 body: String,
21 state: web::Data<AppState>,
22) -> Result<HttpResponse, HaystackError> {
23 let content_type = req
24 .headers()
25 .get("Content-Type")
26 .and_then(|v| v.to_str().ok())
27 .unwrap_or("");
28 let accept = req
29 .headers()
30 .get("Accept")
31 .and_then(|v| v.to_str().ok())
32 .unwrap_or("");
33
34 let nav_id: Option<String> = if body.trim().is_empty() {
36 None
37 } else {
38 let request_grid = content::decode_request_grid(&body, content_type)
39 .map_err(|e| HaystackError::bad_request(format!("failed to decode request: {e}")))?;
40
41 request_grid.row(0).and_then(|row| match row.get("navId") {
42 Some(Kind::Str(s)) if !s.is_empty() => Some(s.clone()),
43 Some(Kind::Ref(r)) => Some(r.val.clone()),
44 _ => None,
45 })
46 };
47
48 let result_grid = match nav_id {
49 None => {
50 let sites = state
52 .graph
53 .read_all("site", 0)
54 .map_err(|e| HaystackError::internal(format!("graph error: {e}")))?;
55
56 build_nav_grid(sites)
57 }
58 Some(ref parent_id) => {
59 let parent = state.graph.get(parent_id);
61 if parent.is_none() {
62 return Err(HaystackError::not_found(format!(
63 "entity not found: {parent_id}"
64 )));
65 }
66
67 let child_refs = state.graph.refs_to(parent_id, None);
69 let mut children = Vec::new();
70 for ref_val in child_refs {
71 if let Some(entity) = state.graph.get(&ref_val) {
72 children.push(entity);
73 }
74 }
75 build_nav_grid(children)
76 }
77 };
78
79 let (encoded, ct) = content::encode_response_grid(&result_grid, accept)
80 .map_err(|e| HaystackError::internal(format!("encoding error: {e}")))?;
81
82 Ok(HttpResponse::Ok().content_type(ct).body(encoded))
83}
84
85fn build_nav_grid(entities: Vec<HDict>) -> HGrid {
87 if entities.is_empty() {
88 return HGrid::new();
89 }
90
91 let cols = vec![HCol::new("id"), HCol::new("dis"), HCol::new("navId")];
92 let rows: Vec<HDict> = entities
93 .into_iter()
94 .map(|entity| {
95 let mut row = HDict::new();
96 if let Some(id_ref) = entity.id() {
97 row.set("id", Kind::Ref(id_ref.clone()));
98 row.set("navId", Kind::Str(id_ref.val.clone()));
99 }
100 if let Some(dis) = entity.dis() {
101 row.set("dis", Kind::Str(dis.to_string()));
102 }
103 row
104 })
105 .collect();
106
107 HGrid::from_parts(HDict::new(), cols, rows)
108}