haystackclientlib/
ops.rs

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    // fn rest(&self) -> RestOp {
84    //     match self {
85    //         Self::About => Self::about(),
86    //         Self::Ops => Self::ops(),
87    //         Self::Close => Self::close(),
88    //         _ => panic!("Operation not supported")
89    //     }
90    // }
91
92    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
134/* impl <'a>std::clone::Clone for HaystackOp<'a> {
135    fn clone(&self) -> Self {
136        Self { op: self.op.clone(), method: self.method.clone(), body: self.body.clone(), resp_tx: self.resp_tx }
137    }
138} */
139
140impl <'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    // TODO: Implment with real [HRefs]
357    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        // TODO: Implement test
556        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}