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(filter: Option<FStr>, limit: Option<usize>) -> Result<(Self,oneshot::Receiver<HaystackResponse>),Error> {
174 let (resp_tx, resp_rx) = oneshot::channel();
175
176 let mut payload = "ver:\"3.0\"\n".to_string();
177
178 match filter {
179 Some(f) => {
180 write!(payload,"filter")?;
181 if limit.is_some() {
182 write!(payload,",limit")?;
183 }
184 write!(payload,"\n\"{}\"",f)?;
185 if let Some(l) = limit {
186 write!(payload,",{}",l)?;
187 }
188 },
189 None => {
190 if limit.is_some() {
191 write!(payload,"limit")?;
192 } else {
193 write!(payload,"empty")?;
194 }
195 if let Some(l) = limit {
196 write!(payload,"\n{}",l)?;
197 }
198 }
199 };
200
201 write!(payload,"\n")?;
202
203 let op = Self {
204 op: FStr::Str("ops"),
205 method: FStr::Str("POST"),
206 body: Some(FStr::String(payload)),
207 resp_tx
208 };
209
210 Ok((op, resp_rx))
211 }
212
213 pub fn close() -> (Self,oneshot::Receiver<HaystackResponse>) {
214 let (resp_tx, resp_rx) = oneshot::channel();
215 let op = Self {
216 op: FStr::Str("close"),
217 method: FStr::Str("POST"),
218 body: Some(FStr::Str("ver:\"3.0\"\nempty\n")),
219 resp_tx
220 };
221
222 (op, resp_rx)
223 }
224
225 pub fn defs(filter: Option<FStr>, limit: Option<usize>) -> Result<(Self,oneshot::Receiver<HaystackResponse>),Error> {
226 let (resp_tx, resp_rx) = oneshot::channel();
227
228 let mut payload = "ver:\"3.0\"\n".to_string();
229
230 match filter {
231 Some(f) => {
232 write!(payload,"filter")?;
233 if limit.is_some() {
234 write!(payload,",limit")?;
235 }
236 write!(payload,"\n\"{}\"",f)?;
237 if let Some(l) = limit {
238 write!(payload,",{}",l)?;
239 }
240 },
241 None => {
242 if limit.is_some() {
243 write!(payload,"limit")?;
244 } else {
245 write!(payload,"empty")?;
246 }
247 if let Some(l) = limit {
248 write!(payload,"\n{}",l)?;
249 }
250 }
251 };
252
253 write!(payload,"\n")?;
254
255 let op = Self {
256 op: FStr::Str("defs"),
257 method: FStr::Str("POST"),
258 body: Some(FStr::String(payload)),
259 resp_tx
260 };
261
262 Ok((op, resp_rx))
263 }
264
265 pub fn libs(filter: Option<FStr>, limit: Option<usize>) -> Result<(Self,oneshot::Receiver<HaystackResponse>),Error> {
266 let (resp_tx, resp_rx) = oneshot::channel();
267
268 let mut payload = "ver:\"3.0\"\n".to_string();
269
270 match filter {
271 Some(f) => {
272 write!(payload,"filter")?;
273 if limit.is_some() {
274 write!(payload,",limit")?;
275 }
276 write!(payload,"\n\"{}\"",f)?;
277 if let Some(l) = limit {
278 write!(payload,",{}",l)?;
279 }
280 },
281 None => {
282 if limit.is_some() {
283 write!(payload,"limit")?;
284 } else {
285 write!(payload,"empty")?;
286 }
287 if let Some(l) = limit {
288 write!(payload,"\n{}",l)?;
289 }
290 }
291 };
292
293 write!(payload,"\n")?;
294
295 let op = Self {
296 op: FStr::Str("libs"),
297 method: FStr::Str("POST"),
298 body: Some(FStr::String(payload)),
299 resp_tx
300 };
301
302 Ok((op, resp_rx))
303 }
304
305 pub fn filetypes(filter: Option<FStr>, limit: Option<usize>) -> Result<(Self,oneshot::Receiver<HaystackResponse>),Error> {
306 let (resp_tx, resp_rx) = oneshot::channel();
307
308 let mut payload = "ver:\"3.0\"\n".to_string();
309
310 match filter {
311 Some(f) => {
312 write!(payload,"filter")?;
313 if limit.is_some() {
314 write!(payload,",limit")?;
315 }
316 write!(payload,"\n\"{}\"",f)?;
317 if let Some(l) = limit {
318 write!(payload,",{}",l)?;
319 }
320 },
321 None => {
322 if limit.is_some() {
323 write!(payload,"limit")?;
324 } else {
325 write!(payload,"empty")?;
326 }
327 if let Some(l) = limit {
328 write!(payload,"\n{}",l)?;
329 }
330 }
331 };
332
333 write!(payload,"\n")?;
334
335 let op = Self {
336 op: FStr::Str("filetypes"),
337 method: FStr::Str("POST"),
338 body: Some(FStr::String(payload)),
339 resp_tx
340 };
341
342 Ok((op, resp_rx))
343 }
344
345 pub fn read(filter: FStr, limit: Option<usize>) -> Result<(Self,oneshot::Receiver<HaystackResponse>),Error> {
346 let (resp_tx, resp_rx) = oneshot::channel();
347 let RestOp { op, method, body} = Haystack::read(filter, limit)?;
348
349 let op = Self {
350 op, method, body, resp_tx
351 };
352
353 Ok((op, resp_rx))
354 }
355
356 pub fn read_by_ids<'b, I>(ids: I) -> Result<(Self,oneshot::Receiver<HaystackResponse>)>
358 where
359 I: IntoIterator<Item = &'b str>,
360 {
361 let (resp_tx, resp_rx) = oneshot::channel();
362
363 let mut grid: String = String::new();
364 write!(grid,"ver:\"3.0\"\nid\n")
365 .or(Err(anyhow!("Failed to write OP body")))?;
366
367 for id in ids {
368 write!(grid,"{}\n",id)
369 .or(Err(anyhow!("Failed to write OP body")))?;
370 }
371
372 let op = Self {
373 op: FStr::Str("read"),
374 method: FStr::Str("POST"),
375 body: Some(FStr::String(grid)),
376 resp_tx
377 };
378
379 Ok((op, resp_rx))
380 }
381
382 pub fn nav(nav: Option<&str>) -> Result<(Self,oneshot::Receiver<HaystackResponse>),&'a str> {
383 let (resp_tx, resp_rx) = oneshot::channel();
384
385 let mut grid = String::new();
386 write!(grid,"ver:\"3.0\"\nnavId\n")
387 .or(Err("Failed to write OP body"))?;
388
389 match nav {
390 Some(n) => write!(grid,"{}\n",n),
391 None => Ok(())
392 }.or(Err("Failed to write OP body"))?;
393
394 let op = Self {
395 op: FStr::Str("nav"),
396 method: FStr::Str("POST"),
397 body: Some(FStr::String(grid)),
398 resp_tx
399 };
400
401 Ok((op, resp_rx))
402 }
403
404 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>
405 where
406 I: IntoIterator<Item = &'b str>,
407 {
408 let (resp_tx, resp_rx) = oneshot::channel();
409
410 if dis.is_none() && id.is_none() {
411 Err("If ID is omitted, a display name must be provided")?;
412 }
413
414 let mut grid = String::new();
415
416 write!(grid,"ver:\"3.0\"").or(Err("Failed to write OP body"))?;
417
418 if let Some(s) = id {
419 write!(grid," watchId:\"{}\"",s).or(Err("Failed to write watchId"))?;
420 }
421
422 if let Some(s) = dis {
423 write!(grid," watchDis:\"{}\"",s).or(Err("Failed to write watchDis"))?;
424 }
425
426 if let Some(s) = lease {
427 write!(grid," lease:{}",s).or(Err("Failed to write watch lease"))?;
428 }
429
430 write!(grid,"\nid\n").or(Err("Failed to write OP body"))?;
431
432 if let Some(s) = ids {
433 for id in s {
434 write!(grid,"{}\n",id)
435 .or(Err("Failed to write OP body"))?;
436 }
437 }
438
439 let op = Self {
440 op: FStr::Str("watchSub"),
441 method: FStr::Str("POST"),
442 body: Some(FStr::String(grid)),
443 resp_tx
444 };
445
446 Ok((op, resp_rx))
447 }
448
449 pub fn watch_unsub<'b, I>(watch_id: &str, ids: Option<I>, close: bool) -> Result<(Self,oneshot::Receiver<HaystackResponse>),&'a str>
450 where
451 I: IntoIterator<Item = &'b str>,
452 {
453 let (resp_tx, resp_rx) = oneshot::channel();
454
455 let mut grid = String::new();
456 write!(grid,"ver:\"3.0\" watchId:\"{}\"",watch_id)
457 .or(Err("Failed to write OP body"))?;
458
459 if close {
460 write!(grid," close").or(Err("Failed to write watch close"))?;
461 }
462
463 write!(grid,"\nid\n").or(Err("Failed to write OP body"))?;
464
465 if let Some(s) = ids {
466 for id in s {
467 write!(grid,"{}\n",id)
468 .or(Err("Failed to write OP body"))?;
469 }
470 }
471
472 let op = Self {
473 op: FStr::Str("watchUnsub"),
474 method: FStr::Str("POST"),
475 body: Some(FStr::String(grid)),
476 resp_tx
477 };
478
479 Ok((op, resp_rx))
480 }
481
482 pub fn watch_poll<'b>(watch_id: &str, refresh: bool) -> Result<(Self,oneshot::Receiver<HaystackResponse>),&'a str> {
483 let (resp_tx, resp_rx) = oneshot::channel();
484 let mut grid = String::new();
485
486 write!(grid,"ver:\"3.0\" watchId:\"{}\"",watch_id).or(Err("Failed to write OP body"))?;
487
488 if refresh {
489 write!(grid," refresh").or(Err("Failed to write watch refresh"))?;
490 }
491
492 write!(grid,"\nempty\n").or(Err("Failed to write OP body"))?;
493
494 let op = Self {
495 op: FStr::Str("watchPoll"),
496 method: FStr::Str("POST"),
497 body: Some(FStr::String(grid)),
498 resp_tx
499 };
500
501 Ok((op, resp_rx))
502 }
503
504 pub fn his_read(id: &str, date_range: &str) -> Result<(Self,oneshot::Receiver<HaystackResponse>),&'a str> {
505 let (resp_tx, resp_rx) = oneshot::channel();
506
507 let mut grid = String::new();
508 write!(grid,"ver:\"3.0\"\nid,range\n{},\"{}\"",id,date_range)
509 .or(Err("Failed to write OP body"))?;
510
511 let op = Self {
512 op: FStr::Str("hisRead"),
513 method: FStr::Str("POST"),
514 body: Some(FStr::String(grid)),
515 resp_tx
516 };
517
518 Ok((op, resp_rx))
519 }
520
521 pub fn his_read_multi<'b, I>(ids: I, date_range: &str, timezone: Option<&str>) -> Result<(Self,oneshot::Receiver<HaystackResponse>),&'a str>
522 where
523 I: IntoIterator<Item = &'b str>,
524 {
525 let (resp_tx, resp_rx) = oneshot::channel();
526
527 let mut grid = String::new();
528 write!(grid,"ver:\"3.0\" range:\"{}\"",date_range)
529 .or(Err("Failed to write OP meta"))?;
530
531 if let Some(tz) = timezone {
532 write!(grid," tz:\"{}\"",tz)
533 .or(Err("Failed to write OP meta"))?;
534 }
535
536 write!(grid,"\nid\n")
537 .or(Err("Failed to write OP col names"))?;
538
539 for id in ids {
540 write!(grid,"{}\n",id)
541 .or(Err("Failed to write OP body"))?;
542 }
543
544 let op = Self {
545 op: FStr::Str("hisRead"),
546 method: FStr::Str("POST"),
547 body: Some(FStr::String(grid)),
548 resp_tx
549 };
550
551 Ok((op, resp_rx))
552 }
553
554 pub fn his_write(his: &str) -> Result<(Self,oneshot::Receiver<HaystackResponse>),&'a str> {
555 let (resp_tx, resp_rx) = oneshot::channel();
557
558 let mut grid = String::new();
559 write!(grid,"{}",his)
560 .or(Err("Failed to write OP body"))?;
561
562 let op = Self {
563 op: FStr::Str("hisWrite"),
564 method: FStr::Str("POST"),
565 body: Some(FStr::String(grid)),
566 resp_tx
567 };
568
569 Ok((op, resp_rx))
570 }
571}
572
573#[derive(Debug)]
574pub enum HaystackResponse {
575 Raw(String),
576}
577
578impl <'a>HaystackResponse {
579 pub fn get_raw(self) -> FStr<'a> {
580 let HaystackResponse::Raw(body) = self;
581 FStr::String(body)
582 }
583 pub fn as_result<T: Float + Display + FromStr>(self) -> Result<HaystackResponse> {
584 match self {
585 HaystackResponse::Raw(ref body) => {
586 match hs_types::io::parse::zinc::grid_err::<T>(body.as_str()) {
587 Ok((input, grid_err)) => {
588 Err(anyhow!("{}", body))
589 },
590 Err(e) => {
591 Ok(HaystackResponse::Raw(body.to_owned()))
592 }
593 }
594 }
595 }
596 }
597}
598
599impl <'a>fmt::Display for HaystackResponse {
600 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
601 let HaystackResponse::Raw(body) = self;
602 write!(f, "<HaystackResponse\n{}\n>",body)
603 }
604}