haystack_server/ops/
defs.rs1use axum::extract::State;
4use axum::http::HeaderMap;
5use axum::response::{IntoResponse, Response};
6
7use haystack_core::data::{HCol, HDict, HGrid};
8use haystack_core::kinds::Kind;
9
10use crate::content;
11use crate::error::HaystackError;
12use crate::state::SharedState;
13
14pub async fn handle(
16 State(state): State<SharedState>,
17 headers: HeaderMap,
18 body: String,
19) -> Result<Response, HaystackError> {
20 let content_type = headers
21 .get("Content-Type")
22 .and_then(|v| v.to_str().ok())
23 .unwrap_or("");
24 let accept = headers
25 .get("Accept")
26 .and_then(|v| v.to_str().ok())
27 .unwrap_or("");
28
29 let ns = state.namespace.read();
30
31 let filter: Option<String> = if body.trim().is_empty() {
33 None
34 } else {
35 let request_grid = content::decode_request_grid(&body, content_type)
36 .map_err(|e| HaystackError::bad_request(format!("failed to decode request: {e}")))?;
37
38 request_grid.row(0).and_then(|row| match row.get("filter") {
39 Some(Kind::Str(s)) if !s.is_empty() => Some(s.clone()),
40 _ => None,
41 })
42 };
43
44 let cols = vec![HCol::new("def"), HCol::new("lib"), HCol::new("doc")];
46
47 let defs = ns.defs();
48 let mut rows: Vec<HDict> = Vec::new();
49
50 for (symbol, def) in defs {
51 if let Some(ref f) = filter
53 && !symbol.contains(f.as_str())
54 {
55 continue;
56 }
57
58 let mut row = HDict::new();
59 row.set(
60 "def",
61 Kind::Symbol(haystack_core::kinds::Symbol::new(symbol)),
62 );
63 row.set(
64 "lib",
65 Kind::Symbol(haystack_core::kinds::Symbol::new(&def.lib)),
66 );
67 row.set("doc", Kind::Str(def.doc.clone()));
68 rows.push(row);
69 }
70
71 rows.sort_by(|a, b| {
73 let a_name = match a.get("def") {
74 Some(Kind::Symbol(s)) => s.val(),
75 _ => "",
76 };
77 let b_name = match b.get("def") {
78 Some(Kind::Symbol(s)) => s.val(),
79 _ => "",
80 };
81 a_name.cmp(b_name)
82 });
83
84 let grid = HGrid::from_parts(HDict::new(), cols, rows);
85 let (encoded, ct) = content::encode_response_grid(&grid, accept)
86 .map_err(|e| HaystackError::internal(format!("encoding error: {e}")))?;
87
88 Ok(([(axum::http::header::CONTENT_TYPE, ct)], encoded).into_response())
89}
90
91pub async fn handle_libs(
93 State(state): State<SharedState>,
94 headers: HeaderMap,
95) -> Result<Response, HaystackError> {
96 let accept = headers
97 .get("Accept")
98 .and_then(|v| v.to_str().ok())
99 .unwrap_or("");
100
101 let ns = state.namespace.read();
102
103 let cols = vec![HCol::new("name"), HCol::new("version")];
104 let libs = ns.libs();
105 let mut rows: Vec<HDict> = libs
106 .values()
107 .map(|lib| {
108 let mut row = HDict::new();
109 row.set("name", Kind::Str(lib.name.clone()));
110 row.set("version", Kind::Str(lib.version.clone()));
111 row
112 })
113 .collect();
114
115 rows.sort_by(|a, b| {
117 let a_name = match a.get("name") {
118 Some(Kind::Str(s)) => s.as_str(),
119 _ => "",
120 };
121 let b_name = match b.get("name") {
122 Some(Kind::Str(s)) => s.as_str(),
123 _ => "",
124 };
125 a_name.cmp(b_name)
126 });
127
128 let grid = HGrid::from_parts(HDict::new(), cols, rows);
129 let (encoded, ct) = content::encode_response_grid(&grid, accept)
130 .map_err(|e| HaystackError::internal(format!("encoding error: {e}")))?;
131
132 Ok(([(axum::http::header::CONTENT_TYPE, ct)], encoded).into_response())
133}