mycelium_command/
lib.rs

1#[macro_use]
2extern crate serde;
3extern crate serde_json;
4extern crate uuid;
5
6use serde::Serialize;
7
8use std::str::FromStr;
9
10pub mod node;
11pub mod prelude;
12
13pub type ResultList = Vec<Vec<u8>>;
14pub type ReturnPkg = (
15    Option<[u8; 16]>,
16    Option<String>,
17    Option<Vec<u8>>,
18    Option<ResultList>,
19    Option<Vec<String>>,
20);
21
22#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
23pub enum KeyWord {
24    Unknown,
25    Archive,
26    First,
27    From,
28    Insert,
29    Into,
30    Last,
31    Limit,
32    Select,
33    Top,
34    Update,
35    Where,
36}
37
38impl From<&str> for KeyWord {
39    fn from(s: &str) -> Self {
40        match s.to_lowercase().as_str() {
41            "archive" => KeyWord::Archive,
42            "first" => KeyWord::First,
43            "from" => KeyWord::From,
44            "insert" => KeyWord::Insert,
45            "into" => KeyWord::Into,
46            "last" => KeyWord::Last,
47            "limit" => KeyWord::Limit,
48            "select" => KeyWord::Select,
49            "top" => KeyWord::Top,
50            "update" => KeyWord::Update,
51            "where" => KeyWord::Where,
52            _ => KeyWord::Unknown,
53        }
54    }
55}
56
57impl Into<&str> for KeyWord {
58    fn into(self) -> &'static str {
59        match self {
60            KeyWord::Unknown => "unknown",
61            KeyWord::Archive => "archive",
62            KeyWord::First => "first",
63            KeyWord::From => "from",
64            KeyWord::Insert => "insert",
65            KeyWord::Into => "into",
66            KeyWord::Last => "last",
67            KeyWord::Limit => "limit",
68            KeyWord::Select => "select",
69            KeyWord::Top => "top",
70            KeyWord::Update => "update",
71            KeyWord::Where => "where",
72        }
73    }
74}
75
76///
77/// # Command struct can be used to build a command with the normal builder pattern.
78///
79///
80/// ```rust
81/// use crate::mycelium_command::prelude::*;
82/// let cmd = Command::new()
83///     .with_action(Action::Select)
84///     .with_what(What::Index(String::from("dog"))) //and
85///     .with_or_what(What::Index("idx3".to_string())) //or
86///     .with_limit(Limits::First(4)); // first usize results
87/// ```
88///
89///  - Action: Default (error), Archive, Insert, Select, Update. Action to perform.
90///  - Limits: None, Some(Default), Some(First(usize)), Some(Last(usize)), Some(Limit(usize))
91///  - LogicOps: List of logic ops and target
92///  - Tag: Tag to execute command against. Required.
93///  - What: What to execute command or operation against. Id of another node, Index value, or other tags.
94///
95#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
96pub struct Command {
97    pub action: Action,
98    pub limit: Option<Limits>,
99    pub ops: Vec<(LogicOp, What)>,
100    pub tag: String,
101    pub what: What,
102}
103
104///
105/// # Action enum values like Insert, Update, Archive, Select
106///
107///  - Default: No Action or Error result
108///  - Archive: Archive node of given Id will require with_tag
109///  - Insert: Insert new node with byte array content will require with_tag
110///  - Select: Select command will require with_tag and a with_what target
111///  - Update: Update command id of node and new byte content for node will require with_tag
112///
113#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
114pub enum Action {
115    Default,
116    Archive([u8; 16]),
117    Insert(Vec<u8>),
118    Select,
119    Update(([u8; 16], Vec<u8>)),
120}
121
122///
123/// # What to target: Id, Index
124///
125/// - Default: error
126/// - Id: Node Id to target
127/// - Index: Index term to target
128///
129#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
130pub enum What {
131    Default,
132    Id([u8; 16]),
133    Index(String),
134}
135
136///
137/// # Logic Operations like, and, or
138///
139#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
140pub enum LogicOp {
141    Default,
142    And,
143    Or,
144}
145
146///
147/// # Limits like First, Last, or Limit
148///
149#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
150pub enum Limits {
151    Default,
152    Limit(usize),
153    First(usize),
154    Last(usize),
155}
156
157#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
158pub enum Result {
159    Some(ReturnPkg),
160    None,
161}
162
163impl Default for Command {
164    fn default() -> Command {
165        Command {
166            action: Action::Default,
167            limit: None,
168            ops: vec![],
169            tag: "".to_string(),
170            what: What::Default,
171        }
172    }
173}
174
175impl Command {
176    pub fn new() -> Command {
177        Command::default()
178    }
179
180    ///
181    /// # Parse
182    ///
183    /// Parse a msql statement to a command.
184    ///
185    /// ```rust
186    /// use crate::mycelium_command::prelude::*;
187    /// let cmd = Command::parse("select from tag").unwrap();
188    /// ```
189    /// * Parameters
190    ///     - cmd: msql statement see [sql like](https://gitlab.com/matthew.bradford/myceliumdds/wikis/sql)
191    ///
192    pub fn parse(cmd: &str) -> std::result::Result<Command, Box<dyn std::error::Error>> {
193        let tmp = cmd.trim().to_string();
194        let split = tmp.split_at(7);
195
196        match split.0.trim().to_lowercase().as_str() {
197            "archive" => parse_archive(split.1),
198            "insert" => parse_insert(split.1),
199            "select" => parse_select(split.1),
200            "update" => parse_update(split.1),
201            _ => Err(From::from(format!(
202                "Invalid command: {} unrecognized.",
203                split.0
204            ))),
205        }
206    }
207
208    pub fn with_action(mut self, action: Action) -> Self {
209        self.action = action;
210        self
211    }
212
213    pub fn with_limit(mut self, limit: Limits) -> Self {
214        self.limit = Some(limit);
215        self
216    }
217
218    pub fn with_what(mut self, what: What) -> Self {
219        self._what(what);
220        self
221    }
222
223    fn _what(&mut self, what: What) {
224        self.what = what;
225    }
226
227    pub fn with_and_what(mut self, what: What) -> Self {
228        self._and_what(what);
229        self
230    }
231
232    fn _and_what(&mut self, what: What) {
233        self.ops.push((LogicOp::And, what));
234    }
235
236    pub fn with_or_what(mut self, what: What) -> Self {
237        self._or_what(what);
238        self
239    }
240
241    fn _or_what(&mut self, what: What) {
242        self.ops.push((LogicOp::Or, what));
243    }
244
245    pub fn with_tag(mut self, tag: &str) -> Self {
246        self._tag(tag);
247        self
248    }
249
250    fn _tag(&mut self, tag: &str) {
251        self.tag = tag.to_string();
252    }
253}
254
255// archive from <tag> where id is [;]
256fn parse_archive(_select: &str) -> std::result::Result<Command, Box<dyn std::error::Error>> {
257    unimplemented!()
258}
259
260fn parse_item_arr(stmt: &str) -> std::result::Result<Vec<u8>, Box<dyn std::error::Error>> {
261    match (stmt.find('['), stmt.find(']')) {
262        (Some(start), Some(end)) => {
263            let obj = stmt.get(start..=end).unwrap();
264            match serde_json::from_str(obj) {
265                Ok(item) => Ok(item),
266                _ => Err(From::from("Invalid command.")),
267            }
268        }
269        (_, _) => Err(From::from("Invalid statement termination.")),
270    }
271}
272
273// Insert [] into <tag>
274// Insert \"[65,32,100,111,103,32,105,115,32,97,32,100,111,103,32,105,115,32,97,32,102,105,115,104]\" into dog
275fn parse_insert(stmt: &str) -> std::result::Result<Command, Box<dyn std::error::Error>> {
276    let item_vec = parse_item_arr(stmt)?;
277    let mut item = Command::new().with_action(Action::Insert(item_vec));
278    let tag = parse_tag(stmt).expect("Failed to parse tag.");
279    item._tag(tag.as_str());
280    Ok(item)
281}
282
283// Select from <tag> where id is [u8; 16]
284// Select from <tag> where is dog and is fish or is like orange limit 1
285// Select from <tag> where is not fish
286// Select from <tag>
287fn parse_select(select: &str) -> std::result::Result<Command, Box<dyn std::error::Error>> {
288    let cmd = Command::new().with_action(Action::Select);
289
290    let pattern = (
291        select.find("from"),
292        select.find("where"),
293        select.find("limit"),
294    );
295    match pattern {
296        (Some(_), None, None) => {
297            let tag = parse_tag(select)?;
298            let mut cmd = cmd;
299            cmd._tag(&tag);
300            Ok(cmd)
301        }
302        (Some(_), Some(l_where), None) => {
303            let cmd_tag = parse_tag(select)?;
304            let mut cmd = cmd;
305            cmd._tag(&cmd_tag);
306            parse_where(select, l_where, select.len(), cmd)
307        }
308        (Some(_from), Some(_l_where), Some(_limit)) => {
309            //let cmd_from = parse_from(select, from, l_where, cmd);
310            //let cmd_what = parse_where(select, l_where, limit, cmd_from);
311            unimplemented!()
312        }
313        (Some(_from), None, Some(_limit)) => unimplemented!(),
314        (None, _, _) => Err(From::from("from part required")),
315    }
316}
317
318// mvp: is dog and is fish or like orange
319// eventual: is dog and is (select from <other tag> where is canine)
320// or: is dog and (select from <other tag> where is canine)
321fn parse_where(
322    stmt: &str,
323    l_where: usize,
324    stop: usize,
325    cmd: Command,
326) -> std::result::Result<Command, Box<dyn std::error::Error>> {
327    match stmt.find("id") {
328        Some(_) => {
329            // retrieve item by id
330            let id = parse_id(stmt);
331            if id.is_some() {
332                let id = *uuid::Uuid::from_str(&id.unwrap())?.as_bytes();
333                let mut cmd = cmd;
334                cmd._what(What::Id(id));
335                Ok(cmd)
336            } else {
337                Err(From::from("statement id is required"))
338            }
339        }
340        None => {
341            // no id's we are searching on indexes
342            let l_where = stmt.get((l_where + 5)..stop).unwrap();
343            parse_what(l_where, cmd)
344        }
345    }
346}
347
348// select from dog where id is [;]
349// update [] from dog where id is [;]
350fn parse_id(stmt: &str) -> Option<String> {
351    let pattern = (stmt.find("id"), stmt.find("is"));
352    match pattern {
353        (Some(_), Some(is_idx)) => {
354            let tmp = stmt.get(is_idx + 2..stmt.len()).unwrap();
355            let rmv = next_keyword_idx(tmp);
356            if rmv.is_some() {
357                let rmv = rmv.unwrap();
358                Some(stmt.get(0..rmv).unwrap().trim().to_string())
359            } else {
360                Some(tmp.trim().to_string())
361            }
362        }
363        (Some(id_idx), None) => {
364            let tmp = stmt.get(id_idx + 2..stmt.len()).unwrap();
365            let rmv = next_keyword_idx(tmp);
366            if rmv.is_some() {
367                let rmv = rmv.unwrap();
368                Some(stmt.get(0..rmv).unwrap().trim().to_string())
369            } else {
370                Some(tmp.trim().to_string())
371            }
372        }
373        _ => None,
374    }
375}
376
377fn parse_what(
378    stmt: &str,
379    cmd: Command,
380) -> std::result::Result<Command, Box<dyn std::error::Error>> {
381    let mut cmd = cmd;
382    match stmt.find("is") {
383        Some(_is) => {
384            let is: Vec<&str> = stmt.split("is").collect();
385            let mut next_modifier = None;
386            for condition in is {
387                let this_modifier = next_modifier.clone();
388                match (condition.trim().is_empty(), condition.len() > 4) {
389                    (true, _) => (), // Empty nothing to to do.
390                    (false, false) => {
391                        let what = condition.trim().to_string();
392                        match this_modifier {
393                            Some(LogicOp::And) => cmd._and_what(What::Index(what)),
394                            Some(LogicOp::Or) => cmd._or_what(What::Index(what)),
395                            Some(LogicOp::Default) | None => cmd._what(What::Index(what)),
396                        }
397                    }
398                    (false, true) => {
399                        let mut what = condition.trim().to_string();
400                        let next_op = what.get((what.len() - 3)..what.len()).unwrap();
401                        match next_op.trim() {
402                            "and" => next_modifier = Some(LogicOp::And),
403                            "or" => next_modifier = Some(LogicOp::Or),
404                            _ => next_modifier = None,
405                        }
406                        if next_modifier.is_some() {
407                            what = what.get(0..(what.len() - 3)).unwrap().trim().to_string();
408                        }
409
410                        match this_modifier {
411                            Some(LogicOp::And) => cmd._and_what(What::Index(what)),
412                            Some(LogicOp::Or) => cmd._or_what(What::Index(what)),
413                            Some(LogicOp::Default) | None => cmd._what(What::Index(what)),
414                        }
415                    }
416                }
417            }
418        }
419        None => {
420            let stmt = stmt.trim().to_string();
421            cmd._what(What::Index(stmt))
422        }
423    }
424    Ok(cmd)
425}
426
427fn parse_tag(stmt: &str) -> std::result::Result<String, Box<dyn std::error::Error>> {
428    let kw_from: &str = KeyWord::From.into();
429    let kw_into: &str = KeyWord::Into.into();
430    let pattern = (stmt.find(kw_from), stmt.find(kw_into));
431    match pattern {
432        (Some(idx), None) | (None, Some(idx)) => {
433            let stmt_minus_kw = stmt
434                .get((idx + 4)..stmt.len())
435                .expect("Split off from failed");
436            let next_key_word = next_keyword_idx(stmt_minus_kw);
437            if next_key_word.is_some() {
438                let next_key_word = next_key_word.unwrap();
439                Ok(stmt_minus_kw
440                    .get(0..next_key_word)
441                    .expect("Seperate tag from statement failed")
442                    .trim()
443                    .to_string())
444            } else {
445                Ok(stmt_minus_kw
446                    .get(0..stmt_minus_kw.len())
447                    .expect("Seperate tag from statement failed")
448                    .trim()
449                    .to_string())
450            }
451        }
452        _ => Err(From::from("Failed to parse tag out of statement")),
453    }
454}
455
456fn next_keyword_idx(stmt: &str) -> Option<usize> {
457    let mut iter = stmt
458        .split_whitespace()
459        .filter(|&s| KeyWord::from(s) != KeyWord::Unknown);
460    let first = iter.nth(0);
461
462    if first.is_some() {
463        let first = first.unwrap();
464        return Some(stmt.find(first).expect("Shouldn't error"));
465    }
466
467    None
468}
469
470/// Update [0,0,0,0,0] where id is [u8; 16]
471fn parse_update(stmt: &str) -> std::result::Result<Command, Box<dyn std::error::Error>> {
472    let item = parse_item_arr(stmt)?;
473    let id = parse_id(stmt);
474    let tag = parse_tag(stmt)?;
475    if id.is_some() {
476        let id = *uuid::Uuid::from_str(&id.unwrap())?.as_bytes();
477        let mut cmd = Command::new().with_action(Action::Update((id, item)));
478        cmd._tag(tag.as_str());
479        Ok(cmd)
480    } else {
481        Err(From::from("Statement id is required."))
482    }
483}
484
485#[cfg(test)]
486mod tests {
487    use super::*;
488    use std::str::FromStr;
489    use uuid::Uuid;
490
491    #[derive(Serialize, Deserialize, Clone, Debug)]
492    pub struct Test {
493        pub test: String,
494        pub test_int: i32,
495        pub test_lint: i64,
496        pub test_index: usize,
497        pub test_float: f32,
498        pub test_lfloat: f64,
499        pub test_bool: bool,
500        pub test_vec: Vec<u8>,
501        pub test_arr: [u8; 16],
502    }
503
504    impl Default for Test {
505        fn default() -> Self {
506            Test {
507                test: "Test".to_string(),
508                test_int: 0,
509                test_lint: 0,
510                test_index: 0,
511                test_float: 0.1,
512                test_lfloat: 0.22,
513                test_bool: false,
514                test_vec: vec![],
515                test_arr: [0; 16],
516            }
517        }
518    }
519
520    impl Test {
521        #[allow(clippy::neg_multiply, clippy::cast_lossless)]
522        pub fn new<S>(text: S, index: usize) -> Test
523        where
524            S: Into<String>,
525        {
526            let mut test = Test::default();
527            test.test = text.into();
528            test.test_int = (index as i32 * -1) as i32;
529            test.test_lint = (index as i32 * -1) as i64;
530            test.test_float = index as f32;
531            test.test_lfloat = index as f64;
532            test.test_index = index;
533
534            test
535        }
536
537        pub fn get_index(&self) -> usize {
538            self.test_index
539        }
540
541        pub fn serialize(&self) -> String {
542            serde_json::to_string(self).expect("Failed to serialize test data.")
543        }
544    }
545
546    use std::cmp::Ordering;
547    impl std::cmp::Ord for Test {
548        fn cmp(&self, other: &Self) -> Ordering {
549            self.get_index().cmp(&other.get_index())
550        }
551    }
552
553    impl Eq for Test {}
554
555    impl PartialOrd for Test {
556        fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
557            Some(self.get_index().cmp(&other.get_index()))
558        }
559    }
560
561    impl PartialEq for Test {
562        fn eq(&self, other: &Self) -> bool {
563            self.get_index() == other.get_index()
564        }
565    }
566
567    #[test]
568    fn parse_update() {
569        // update "\[10, 10, 10]\" where id [u8; 16]
570        let id = "936DA01F9ABD4d9d80C702AF85C822A8";
571        let test = Test::new("Some text", 1).serialize();
572        let test_vec = test.as_bytes().to_vec();
573        let sql_sorta = format!(
574            "update {} from dog where id {}",
575            serde_json::to_string(&test_vec).unwrap(),
576            id
577        );
578        let parse_cmd = Command::parse(sql_sorta.as_str()).expect("Error parsing update command.");
579        let uid = *Uuid::from_str(id).expect("Shouldn't fail").as_bytes();
580        let correct_cmd = Command::new()
581            .with_action(Action::Update((uid, test_vec)))
582            .with_tag("dog");
583        //correct_cmd._tag("dog");
584
585        assert_eq!(correct_cmd, parse_cmd);
586    }
587
588    #[test]
589    fn parse_insert() {
590        let mut cmp = Command::new().with_action(Action::Insert(
591            "A dog is a dog is a fish".as_bytes().to_vec(),
592        ));
593        cmp._tag("dog");
594        let tmp = "A dog is a dog is a fish".as_bytes().to_vec();
595        let cmd_string = format!("Insert {} into dog", serde_json::to_string(&tmp).unwrap());
596        match Command::parse(cmd_string.as_str()) {
597            Ok(cmd) => {
598                assert_eq!(cmp, cmd);
599            }
600            Err(_) => assert!(false),
601        }
602    }
603
604    #[test]
605    fn parse_select_and_or() {
606        let mut cmp = Command::new().with_action(Action::Select);
607        cmp._tag("frog");
608        cmp._what(What::Index("duck".into()));
609        cmp._and_what(What::Index("wet".into()));
610        cmp._or_what(What::Index("flower".into()));
611
612        match Command::parse("Select from frog where duck and is wet or is flower") {
613            Ok(cmd) => assert_eq!(cmp, cmd),
614            Err(_) => assert!(false),
615        }
616    }
617
618    #[test]
619    fn parse_select_id() {
620        // select from <tag>
621        let cmd = Command::parse(
622            format!("select from tag where id is 936DA01F9ABD4d9d80C702AF85C822A8").as_str(),
623        )
624        .expect("Valid shouldn't fail");
625
626        let mut cmd_cmp = Command::new().with_action(Action::Select);
627        cmd_cmp._tag("tag");
628        cmd_cmp._what(What::Id([
629            147, 109, 160, 31, 154, 189, 77, 157, 128, 199, 2, 175, 133, 200, 34, 168,
630        ]));
631
632        assert_eq!(cmd_cmp, cmd);
633    }
634
635    #[test]
636    fn parse_select() {
637        // select from <tag>
638        let cmd = Command::parse("select from tag").expect("Valid shouldn't fail");
639        let mut cmd_cmp = Command::new().with_action(Action::Select);
640        cmd_cmp._tag("tag");
641
642        assert_eq!(cmd_cmp, cmd);
643    }
644
645    #[test]
646    fn parse_select_where() {
647        let cmd = Command::parse("select from tag where duck").expect("Valid shouldn't fail.");
648        let mut cmd_cmp = Command::new().with_action(Action::Select);
649        cmd_cmp._tag("tag");
650        cmd_cmp._what(What::Index("duck".to_string()));
651
652        assert_eq!(cmd_cmp, cmd);
653    }
654}