1use std::{fmt::{self, Display, Write}, str::{FromStr, SplitWhitespace}};
2
3use anyhow::{anyhow, Context, Error, Result};
4
5use nom::Err;
6use tokio::sync::oneshot;
7
8use haystack_types::{self as hs_types, HCast, Float};
9
10#[derive(Debug)]
11pub enum FStr<'a> {
12 Str(&'a str),
13 String(String)
14}
15
16impl FStr<'_> {
17 pub fn as_str(&self) -> &str {
18 match self {
19 FStr::Str(s) => s,
20 FStr::String(s) => s.as_str(),
21 }
22 }
23
24 pub fn split(&self) -> SplitWhitespace<'_> {
25 match self {
26 FStr::Str(s) => s.split_whitespace(),
27 FStr::String(s) => s.split_whitespace(),
28 }
29 }
30}
31
32impl <'a>std::clone::Clone for FStr<'a> {
33 fn clone(&self) -> Self {
34 match self {
35 Self::Str(arg0) => Self::Str(arg0.clone()),
36 Self::String(arg0) => Self::String(arg0.clone()),
37 }
38 }
39}
40
41impl <'a>From<String> for FStr<'a> {
42 fn from(value: String) -> Self {
43 FStr::String(value)
44 }
45}
46
47impl <'a>From<&'a str> for FStr<'a> {
48 fn from(value: &'a str) -> Self {
49 FStr::Str(value)
50 }
51}
52
53impl <'a>fmt::Display for FStr<'a> {
54 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
55 match self {
56 FStr::Str(a) => write!(f, "{}", a),
57 FStr::String(a) => write!(f, "{}", a)
58 }
59 }
60}
61
62pub enum Haystack {
63 About,
64 Close,
65 Defs,
66 Filetypes,
67 HisRead,
68 HisWrite,
69 InvokeAction,
70 Libs,
71 Nav,
72 Ops,
73 PointWrite,
74 Read,
75 WatchPoll,
76 WatchSub,
77 WatchUnsub
78}
79
80const RAW_EMPTY_GRID: FStr = FStr::Str("ver:\"3.0\"\nempty");
81
82impl Haystack {
83 pub fn about() -> RestOp {
93 RestOp { op:"about".into(), method:"GET".into(), body:None }
94 }
95
96 pub fn ops() -> RestOp {
97 RestOp { op:"ops".into(), method:"GET".into(), body:None }
98 }
99
100 pub fn close() -> RestOp {
101 RestOp { op:"close".into(), method:"POST".into(), body:Some(RAW_EMPTY_GRID) }
102 }
103
104 pub fn formats() -> RestOp {
105 RestOp { op:"formats".into(), method:"GET".into(), body:None }
106 }
107
108 pub fn read(filter: FStr, limit: Option<usize>) -> Result<RestOp,Error> {
109 let mut grid = String::new();
110 write!(grid,"ver:\"3.0\"\nfilter,limit\n")?;
111 match limit {
112 Some(lim) => write!(grid,"\"{}\",{}\n",filter,lim),
113 None => write!(grid,"\"{}\",\n",filter)
114 }?;
115
116 Ok(RestOp { op:"read".into(), method:"POST".into(), body:Some(grid.into()) })
117 }
118}
119
120pub struct RestOp {
121 op: FStr<'static>,
122 method: FStr<'static>,
123 body: Option<FStr<'static>>,
124}
125
126#[derive(Debug)]
127pub struct HaystackOpTxRx {
128 op: FStr<'static>,
129 method: FStr<'static>,
130 body: Option<FStr<'static>>,
131 pub resp_tx: oneshot::Sender<HaystackResponse>
132}
133
134impl <'a>HaystackOpTxRx {
141 pub fn new(op:FStr<'static>, method:FStr<'static>, body:Option<FStr<'static>>) -> (Self,oneshot::Receiver<HaystackResponse>) {
142 let (resp_tx, resp_rx) = oneshot::channel();
143 (Self { op, method, body, resp_tx }, resp_rx)
144 }
145
146 pub fn priv_op(&'a self) -> FStr {
147 self.op.clone()
148 }
149
150 pub fn priv_method(&'a self) -> FStr {
151 self.method.clone()
152 }
153
154 pub fn priv_body(&'a self) -> Option<FStr> {
155 match &self.body {
156 Some(x) => Some(x.to_owned()),
157 None => return None,
158 }
159 }
160
161 pub fn about() -> (Self,oneshot::Receiver<HaystackResponse>) {
162 let (resp_tx, resp_rx) = oneshot::channel();
163 let op = Self {
164 op: FStr::Str("about"),
165 method: FStr::Str("GET"),
166 body: None,
167 resp_tx
168 };
169
170 (op, resp_rx)
171 }
172
173 pub fn ops() -> (Self,oneshot::Receiver<HaystackResponse>) {
174 let (resp_tx, resp_rx) = oneshot::channel();
175 let op = Self {
176 op: FStr::Str("ops"),
177 method: FStr::Str("GET"),
178 body: None,
179 resp_tx
180 };
181
182 (op, resp_rx)
183 }
184
185 pub fn close() -> (Self,oneshot::Receiver<HaystackResponse>) {
186 let (resp_tx, resp_rx) = oneshot::channel();
187 let op = Self {
188 op: FStr::Str("close"),
189 method: FStr::Str("POST"),
190 body: Some(FStr::Str("ver:\"3.0\"\nempty\n")),
191 resp_tx
192 };
193
194 (op, resp_rx)
195 }
196
197 pub fn filetypes() -> (Self,oneshot::Receiver<HaystackResponse>) {
198 let (resp_tx, resp_rx) = oneshot::channel();
199 let op = Self {
200 op: FStr::Str("formats"),
201 method: FStr::Str("GET"),
202 body: None,
203 resp_tx
204 };
205
206 (op, resp_rx)
207 }
208
209 pub fn read(filter: FStr, limit: Option<usize>) -> Result<(Self,oneshot::Receiver<HaystackResponse>),Error> {
210 let (resp_tx, resp_rx) = oneshot::channel();
211 let RestOp { op, method, body} = Haystack::read(filter, limit)?;
212
213 let op = Self {
214 op, method, body, resp_tx
215 };
216
217 Ok((op, resp_rx))
218 }
219
220 pub fn read_by_ids<'b, I>(ids: I) -> Result<(Self,oneshot::Receiver<HaystackResponse>)>
222 where
223 I: IntoIterator<Item = &'b str>,
224 {
225 let (resp_tx, resp_rx) = oneshot::channel();
226
227 let mut grid: String = String::new();
228 write!(grid,"ver:\"3.0\"\nid\n")
229 .or(Err(anyhow!("Failed to write OP body")))?;
230
231 for id in ids {
232 write!(grid,"{}\n",id)
233 .or(Err(anyhow!("Failed to write OP body")))?;
234 }
235
236 let op = Self {
237 op: FStr::Str("read"),
238 method: FStr::Str("POST"),
239 body: Some(FStr::String(grid)),
240 resp_tx
241 };
242
243 Ok((op, resp_rx))
244 }
245
246 pub fn nav(nav: Option<&str>) -> Result<(Self,oneshot::Receiver<HaystackResponse>),&'a str> {
247 let (resp_tx, resp_rx) = oneshot::channel();
248
249 let mut grid = String::new();
250 write!(grid,"ver:\"3.0\"\nnavId\n")
251 .or(Err("Failed to write OP body"))?;
252
253 match nav {
254 Some(n) => write!(grid,"{}\n",n),
255 None => Ok(())
256 }.or(Err("Failed to write OP body"))?;
257
258 let op = Self {
259 op: FStr::Str("nav"),
260 method: FStr::Str("POST"),
261 body: Some(FStr::String(grid)),
262 resp_tx
263 };
264
265 Ok((op, resp_rx))
266 }
267
268 pub fn watch_sub<'b, I>(dis: Option<&str>, id: Option<&str>, lease: Option<&str>, ids: Option<I>) -> Result<(Self,oneshot::Receiver<HaystackResponse>),&'a str>
269 where
270 I: IntoIterator<Item = &'b str>,
271 {
272 let (resp_tx, resp_rx) = oneshot::channel();
273
274 if dis.is_none() && id.is_none() {
275 Err("If ID is omitted, a display name must be provided")?;
276 }
277
278 let mut grid = String::new();
279
280 write!(grid,"ver:\"3.0\"").or(Err("Failed to write OP body"))?;
281
282 if let Some(s) = id {
283 write!(grid," watchId:\"{}\"",s).or(Err("Failed to write watchId"))?;
284 }
285
286 if let Some(s) = dis {
287 write!(grid," watchDis:\"{}\"",s).or(Err("Failed to write watchDis"))?;
288 }
289
290 if let Some(s) = lease {
291 write!(grid," lease:{}",s).or(Err("Failed to write watch lease"))?;
292 }
293
294 write!(grid,"\nid\n").or(Err("Failed to write OP body"))?;
295
296 if let Some(s) = ids {
297 for id in s {
298 write!(grid,"{}\n",id)
299 .or(Err("Failed to write OP body"))?;
300 }
301 }
302
303 let op = Self {
304 op: FStr::Str("watchSub"),
305 method: FStr::Str("POST"),
306 body: Some(FStr::String(grid)),
307 resp_tx
308 };
309
310 Ok((op, resp_rx))
311 }
312
313 pub fn watch_unsub<'b, I>(watch_id: &str, ids: Option<I>, close: bool) -> Result<(Self,oneshot::Receiver<HaystackResponse>),&'a str>
314 where
315 I: IntoIterator<Item = &'b str>,
316 {
317 let (resp_tx, resp_rx) = oneshot::channel();
318
319 let mut grid = String::new();
320 write!(grid,"ver:\"3.0\" watchId:\"{}\"",watch_id)
321 .or(Err("Failed to write OP body"))?;
322
323 if close {
324 write!(grid," close").or(Err("Failed to write watch close"))?;
325 }
326
327 write!(grid,"\nid\n").or(Err("Failed to write OP body"))?;
328
329 if let Some(s) = ids {
330 for id in s {
331 write!(grid,"{}\n",id)
332 .or(Err("Failed to write OP body"))?;
333 }
334 }
335
336 let op = Self {
337 op: FStr::Str("watchUnsub"),
338 method: FStr::Str("POST"),
339 body: Some(FStr::String(grid)),
340 resp_tx
341 };
342
343 Ok((op, resp_rx))
344 }
345
346 pub fn watch_poll<'b>(watch_id: &str, refresh: bool) -> Result<(Self,oneshot::Receiver<HaystackResponse>),&'a str> {
347 let (resp_tx, resp_rx) = oneshot::channel();
348 let mut grid = String::new();
349
350 write!(grid,"ver:\"3.0\" watchId:\"{}\"",watch_id).or(Err("Failed to write OP body"))?;
351
352 if refresh {
353 write!(grid," refresh").or(Err("Failed to write watch refresh"))?;
354 }
355
356 write!(grid,"\nempty\n").or(Err("Failed to write OP body"))?;
357
358 let op = Self {
359 op: FStr::Str("watchPoll"),
360 method: FStr::Str("POST"),
361 body: Some(FStr::String(grid)),
362 resp_tx
363 };
364
365 Ok((op, resp_rx))
366 }
367
368 pub fn his_read(id: &str, date_range: &str) -> Result<(Self,oneshot::Receiver<HaystackResponse>),&'a str> {
369 let (resp_tx, resp_rx) = oneshot::channel();
370
371 let mut grid = String::new();
372 write!(grid,"ver:\"3.0\"\nid,range\n{},\"{}\"",id,date_range)
373 .or(Err("Failed to write OP body"))?;
374
375 let op = Self {
376 op: FStr::Str("hisRead"),
377 method: FStr::Str("POST"),
378 body: Some(FStr::String(grid)),
379 resp_tx
380 };
381
382 Ok((op, resp_rx))
383 }
384
385 pub fn his_read_multi<'b, I>(ids: I, date_range: &str, timezone: Option<&str>) -> Result<(Self,oneshot::Receiver<HaystackResponse>),&'a str>
386 where
387 I: IntoIterator<Item = &'b str>,
388 {
389 let (resp_tx, resp_rx) = oneshot::channel();
390
391 let mut grid = String::new();
392 write!(grid,"ver:\"3.0\" range:\"{}\"",date_range)
393 .or(Err("Failed to write OP meta"))?;
394
395 if let Some(tz) = timezone {
396 write!(grid," tz:\"{}\"",tz)
397 .or(Err("Failed to write OP meta"))?;
398 }
399
400 write!(grid,"\nid\n")
401 .or(Err("Failed to write OP col names"))?;
402
403 for id in ids {
404 write!(grid,"{}\n",id)
405 .or(Err("Failed to write OP body"))?;
406 }
407
408 let op = Self {
409 op: FStr::Str("hisRead"),
410 method: FStr::Str("POST"),
411 body: Some(FStr::String(grid)),
412 resp_tx
413 };
414
415 Ok((op, resp_rx))
416 }
417
418 pub fn his_write(his: &str) -> Result<(Self,oneshot::Receiver<HaystackResponse>),&'a str> {
419 let (resp_tx, resp_rx) = oneshot::channel();
421
422 let mut grid = String::new();
423 write!(grid,"{}",his)
424 .or(Err("Failed to write OP body"))?;
425
426 let op = Self {
427 op: FStr::Str("hisWrite"),
428 method: FStr::Str("POST"),
429 body: Some(FStr::String(grid)),
430 resp_tx
431 };
432
433 Ok((op, resp_rx))
434 }
435}
436
437#[derive(Debug)]
438pub enum HaystackResponse {
439 Raw(String),
440}
441
442impl <'a>HaystackResponse {
443 pub fn get_raw(self) -> FStr<'a> {
444 let HaystackResponse::Raw(body) = self;
445 FStr::String(body)
446 }
447 pub fn as_result<T: Float + Display + FromStr>(self) -> Result<HaystackResponse> {
448 match self {
449 HaystackResponse::Raw(ref body) => {
450 match hs_types::io::parse::zinc::grid_err::<T>(body.as_str()) {
451 Ok((input, grid_err)) => {
452 Err(anyhow!("{}", body))
453 },
454 Err(e) => {
455 Ok(HaystackResponse::Raw(body.to_owned()))
456 }
457 }
458 }
459 }
460 }
461}
462
463impl <'a>fmt::Display for HaystackResponse {
464 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
465 let HaystackResponse::Raw(body) = self;
466 write!(f, "<HaystackResponse\n{}\n>",body)
467 }
468}