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() -> (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    // TODO: Implment with real [HRefs]
221    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        // TODO: Implement test
420        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}