Skip to main content

minimal_object_notation/
lib.rs

1
2pub struct MiniON {
3    pub name: String,
4    pub length: usize,
5    pub content: Option<String>,
6}
7
8impl MiniON {
9    /// Construct a new `MiniON`.
10    pub fn new(name: String) -> MiniON {
11        MiniON {
12            name,
13            length: 0,
14            content: None,
15        }
16    }
17
18    /// Set the content of a `MiniON`.
19    pub fn set_content(&mut self, content: String) {
20        self.length = content.len();
21        self.content = Some(content);
22    }
23
24    /// Return the `MiniON` as a `String`.
25    /// ## Example
26    /// ```
27    ///     use minimal_object_notation::*;
28    /// 
29    ///     let mut minion = MiniON::new("greeting".to_string());
30    /// 
31    ///     minion.set_content("Hello, world!".to_string());
32    /// 
33    ///     let minion = minion.to_string();
34    /// ```
35    /// Will give you a `String` containing `"greeting|13~Hello, world!"`.
36    pub fn to_string(&self) -> String {
37        let mut output = String::from(&self.name);
38        output.push('|');
39        output.push_str(&format!("{}",self.length));
40        output.push('~');
41        
42        match &self.content {
43            Some(content) => {
44                output.push_str(&content);
45
46                return output;
47            },
48            None => {
49                return output;
50            }
51        } 
52    }
53
54    /// Parse data into a `MiniON` object.
55    /// ## Example
56    /// ```
57    ///     use minimal_object_notation::*;
58    /// 
59    ///     let data = b"greeting|13~Hello, world!";
60    /// 
61    ///     let mut incr: usize = 0;
62    /// 
63    ///     match MiniON::parse_one(data, &mut incr) {
64    ///         Ok(minion) => {
65    ///             assert_eq!("greeting",minion.name);
66    ///             
67    ///             match minion.content {
68    ///                 Some(content) => {
69    ///                     assert_eq!("Hello, world!",content);
70    ///                 },
71    ///                 None => {
72    ///                     panic!("Expected content!");
73    ///                 }
74    ///             }
75    ///         },
76    ///         Err(e) => {
77    ///             panic!("{}",e.to_string());
78    ///         }
79    ///     }
80    /// 
81    /// ```
82    pub fn parse_one(bytes: &[u8], incr: &mut usize) -> Result<MiniON,Error> {
83        #[allow(unused_assignments)] // Don't know why it warns me.
84        let mut name = String::new();
85        #[allow(unused_assignments)] // Idem.
86        let mut length: usize = 0;
87        let mut content = String::new();
88
89        match MiniON::parse_name(bytes, incr) {
90            Ok(n) => {
91                name = n;
92            },
93            Err(e) => {
94                return Err(e);
95            }
96        }
97
98        match MiniON::parse_length(bytes, incr, &name) {
99            Ok(n) => {
100                length = n;
101            },
102            Err(e) => {
103                return Err(e);
104            }
105        }
106
107        if length != 0 {
108            match MiniON::parse_content(bytes, incr, &name, length) {
109                Ok(n) => {
110                    content = n;
111                },
112                Err(e) => {
113                    return Err(e);
114                }
115            }
116        }
117
118        let mut minion = MiniON::new(name);
119        if length != 0 {
120            minion.set_content(content);
121        }
122
123        return Ok(minion);
124    }
125
126    /// Parse data that contains multiple miniON objects ONE AFTER THE OTHER. Will not parse nested miniON objects.
127    /// ## Example
128    /// ```
129    ///     use minimal_object_notation::*;
130    /// 
131    ///     let data = b"first|4~ONE,second|4~TWO,third|6~THREE,container|29~name|5~NAME,content|7~CONTENT";
132    /// 
133    ///     match MiniON::parse_all(data) {
134    ///         Ok(minions) => {
135    ///             assert_eq!(4,minions.len());
136    /// 
137    ///             assert_eq!("first",minions[0].name);
138    /// 
139    ///             assert_eq!("second",minions[1].name);
140    /// 
141    ///             assert_eq!("third",minions[2].name);
142    /// 
143    ///             assert_eq!("container",minions[3].name);
144    /// 
145    ///             match &minions[0].content {
146    ///                 Some(content) => {
147    ///                     assert_eq!("ONE,",content);
148    ///                 },
149    ///                 None => {
150    ///                     panic!("Expected content!");
151    ///                 }
152    ///             }
153    /// 
154    ///             match &minions[3].content {
155    ///                 Some(content) => {
156    ///                     assert_eq!("name|5~NAME,content|7~CONTENT",content);
157    ///                 },
158    ///                 None => {
159    ///                     panic!("Expected content!");
160    ///                 }
161    ///             }
162    ///         },
163    ///         Err(e) => {
164    ///             panic!("{}",e.to_string());
165    ///         }
166    ///     }
167    /// ```
168    pub fn parse_all(bytes: &[u8]) -> Result<Vec<MiniON>,Error> {
169
170        let mut minions: Vec<MiniON> = Vec::new();
171
172        let mut incr: usize = 0;
173
174        loop {
175            match MiniON::parse_one(bytes, &mut incr) {
176                Ok(minion) => {
177                    minions.push(minion);
178
179                    if incr == bytes.len() {
180                        return Ok(minions);
181                    }
182                },
183                Err(e) => {
184                    return Err(e);
185                }
186            }
187        }
188    }
189
190    /// Find a specific miniON object by its name.
191    /// ## Example
192    /// ```
193    ///     use minimal_object_notation::*;
194    /// 
195    ///     let data = b"one|3~ONEtwo|3~TWOthree|5~THREE";
196    /// 
197    ///     match MiniON::find(data,"two") {
198    ///         Ok(minion) => {
199    ///             assert_eq!("two",minion.name);
200    ///             assert_eq!(Some("TWO".to_string()),minion.content);
201    ///         },
202    ///         Err(e) => {
203    ///             panic!("{}",e.to_string());
204    ///         }
205    ///     }
206    /// ```
207    pub fn find(bytes: &[u8], name: &str) -> Result<MiniON,Error> {
208
209        let mut incr: usize = 0;
210
211        loop {
212
213            match MiniON::parse_name(bytes, &mut incr) {
214                Ok(nm) => {
215                    match name == nm {
216                        true => {
217                            let mut minion = MiniON::new(name.to_string());
218
219                            match MiniON::parse_length(bytes, &mut incr, name) {
220                                Ok(len) => {
221
222                                    match MiniON::parse_content(bytes, &mut incr, name, len) {
223                                        Ok(content) => {
224                                            minion.set_content(content);
225                                        },
226                                        Err(e) => {
227                                            return Err(e);
228                                        }
229                                    }
230
231                                },
232                                Err(e) => {
233                                    return Err(e);
234                                }
235                            }
236
237                            return Ok(minion);
238                        },
239                        false => {}
240                    }
241                },
242                Err(e) => {
243                    return Err(e);
244                }
245            }
246
247            match MiniON::parse_length(bytes, &mut incr, name) {
248                Ok(len) => {
249                    incr += len;
250                },
251                Err(e) => {
252                    return Err(e);
253                }
254            }
255
256        }
257    }
258
259    /// Parse the name of a miniON object. (Start at the correct position.)
260    /// ## Example
261    /// ```
262    ///     use minimal_object_notation::*;
263    /// 
264    ///     let data = b"greeting|13~Hello, world!";
265    /// 
266    ///     let mut incr: usize = 0;
267    /// 
268    ///     match MiniON::parse_name(data,&mut incr) {
269    ///         Ok(name) => {
270    ///             assert_eq!("greeting",name);
271    ///             assert_eq!(9,incr);
272    ///         },
273    ///         Err(e) => {
274    ///             panic!("{}",e.to_string());
275    ///         }
276    ///     }
277    /// ```
278    pub fn parse_name(bytes: &[u8], incr: &mut usize) -> Result<String,Error> {
279        let mut output = String::new();
280    
281        loop {
282    
283            match bytes[*incr] as char {
284                '|' => {
285                    match *incr + 1 < bytes.len() {
286                        true => {
287                            *incr += 1;
288                        },
289                        false => {
290                            return Err(Error::Incomplete(format!("No more data after name ({}) field at position {}.",output,*incr)));
291                        }
292                    }
293    
294                    return Ok(output);
295                },
296                c => {
297                    output.push(c);
298                }
299            }
300    
301            match *incr + 1 < bytes.len() {
302                true => {
303                    *incr += 1;
304                },
305                false => {
306                    return Err(Error::NoStructure);
307                }
308            }
309    
310        }
311    }
312
313    /// Parse the length of a miniON object (after having parsed the name tag).
314    /// ## Example
315    /// ```
316    ///     use minimal_object_notation::*;
317    /// 
318    ///     let data = b"greeting|13~Hello, world!";
319    /// 
320    ///     let mut incr: usize = 9;
321    /// 
322    ///     match MiniON::parse_length(data,&mut incr,"greeting") {
323    ///         Ok(length) => {
324    ///             assert_eq!(13,length);
325    ///             assert_eq!(12,incr);
326    ///         },
327    ///         Err(e) => {
328    ///             panic!("{}",e.to_string());
329    ///         }
330    ///     }
331    /// ```
332    pub fn parse_length(bytes: &[u8], incr: &mut usize, name: &str) -> Result<usize,Error> {
333        let mut output = String::new();
334    
335        loop {
336    
337            match bytes[*incr] as char {
338                '~' => {
339                    match *incr + 1 < bytes.len() {
340                        true => {
341                            *incr += 1;
342                        },
343                        false => {
344                            match output.parse::<usize>() {
345                                Ok(length) => {
346                                    match length == 0 {
347                                        true => {
348                                            return Ok(length);
349                                        },
350                                        false => {
351                                            return Err(Error::Incomplete(format!("No more data after length (name: {}) field at position {}.",name,*incr)));
352                                        }
353                                    }
354                                },
355                                Err(_) => {
356                                    return Err(Error::BadStructure(format!("Could not parse the length field. Contains: {}",output)));
357                                }
358                            }
359                        }
360                    }
361    
362                    match output.parse::<usize>() {
363                        Ok(length) => {
364                            return Ok(length);
365                        },
366                        Err(_) => {
367                            return Err(Error::BadStructure(format!("Could not parse the length field. Contains: {}",output)));
368                        }
369                    }
370    
371                },
372                c => {
373                    output.push(c);
374                }
375            }
376    
377            match *incr + 1 < bytes.len() {
378                true => {
379                    *incr += 1;
380                },
381                false => {
382                    return Err(Error::NoStructure);
383                }
384            }
385    
386        }
387    }
388
389    /// Parse the contents of a miniON object (after having parsed the name and length tags).
390    /// ## Example
391    /// ```
392    ///     use minimal_object_notation::*;
393    /// 
394    ///     let data = b"greeting|13~Hello, world!";
395    /// 
396    ///     let mut incr: usize = 12;
397    /// 
398    ///     match MiniON::parse_content(data, &mut incr, "greeting", 13) {
399    ///         Ok(content) => {
400    ///             assert_eq!("Hello, world!",content);
401    ///             assert_eq!(incr,data.len());
402    ///         },
403    ///         Err(e) => {
404    ///             panic!("{}",e.to_string());
405    ///         }
406    ///     }
407    /// ```
408    /// ## Warning!
409    /// Should not be called when the object has a length of 0! This will result in errors!
410    pub fn parse_content(bytes: &[u8], incr: &mut usize, name: &str, length: usize) -> Result<String,Error> {
411        let mut output = String::new();
412    
413        let mut pos_count: usize = 0;
414    
415        loop {
416    
417            pos_count += 1;
418    
419            output.push(bytes[*incr] as char);
420    
421            match *incr + 1 < bytes.len() {
422                true => {
423                    match pos_count < length {
424                        true => {
425                            *incr += 1;
426                        },
427                        false => {
428                            *incr += 1;
429    
430                            return Ok(output);
431                        }
432                    }
433                },
434                false => {
435    
436                    match pos_count == length {
437                        true => {
438                            *incr += 1;
439
440                            return Ok(output);
441                        },
442                        false => {
443                            return Err(Error::Incomplete(format!("The object (name: {}) is incomplete. Bytes missing = {} .",name, length - pos_count )));
444                        }
445                    }
446                    
447                }
448            }
449    
450        }
451
452    }
453    
454}
455
456pub enum Error {
457    Incomplete(String),
458    NoStructure,
459    BadStructure(String),
460    NotFound(String),
461}
462
463impl Error {
464    /// Will `println!` the error with an explanation for you. 
465    pub fn print(&self) {
466        match self {
467            Error::Incomplete(info) => {
468                println!("Error: Incomplete data: {}",info);
469            },
470            Error::NoStructure => {
471                println!("Error: No structure: The data does not follow the mON structure.");
472            },
473            Error::BadStructure(info) => {
474                println!("Error: Bad data: {}",info);
475            },
476            Error::NotFound(name) => {
477                println!("Error: The `miniON` object (name: {}) was not found.",name);
478            }
479        }
480    }
481
482    /// Will give you a `String` with the relevant info.
483    pub fn to_string(&self) -> String {
484        match self {
485            Error::Incomplete(info) => {
486                return format!("Error: Incomplete data: {}",info);
487            },
488            Error::NoStructure => {
489                return format!("Error: No structure: The data does not follow the mON structure.")
490            },
491            Error::BadStructure(info) => {
492                return format!("Error: Bad data: {}",info);
493            },
494            Error::NotFound(name) => {
495                return format!("Error: The `miniON` object (name: {}) was not found.",name);
496            }
497        }
498    }
499}
500
501#[cfg(test)]
502mod tests {
503    use super::*;
504
505    #[test]
506    fn test_create_minion() {
507        let mut minion = MiniON::new("greeting".to_string());
508
509        minion.set_content("Hello, world!".to_string());
510
511        assert_eq!("greeting|13~Hello, world!",minion.to_string());
512
513    }
514
515    #[test]
516    fn test_multi_minion() {
517        
518        let mut minion_container = MiniON::new("container".to_string());
519
520        let mut days_of_the_week = MiniON::new("object".to_string());
521
522        days_of_the_week.set_content("____________________".to_string());
523
524        let mut pairs_of_socks = MiniON::new("object".to_string());
525
526        pairs_of_socks.set_content("____________________".to_string());
527
528        let mut content = days_of_the_week.to_string();
529        content.push_str(&pairs_of_socks.to_string());
530
531        minion_container.set_content(content);
532
533        assert_eq!("container|60~object|20~____________________object|20~____________________",minion_container.to_string());
534    }
535
536    #[test]
537    fn test_parse_manually() {
538        let data = b"greeting|13~Hello, world!name|6~miniON";
539
540        let mut incr: usize = 0;
541
542        match MiniON::parse_name(data, &mut incr) {
543            Ok(name) => {
544                assert_eq!("greeting",name);
545            },
546            Err(e) => {
547                panic!("{}",e.to_string());
548            }
549        }
550
551        match MiniON::parse_length(data, &mut incr, "greeting") {
552            Ok(length) => {
553                assert_eq!(13,length);
554            },
555            Err(e) => {
556                panic!("{}",e.to_string());
557            }
558        }
559
560        match MiniON::parse_content(data, &mut incr, "greeting", 13) {
561            Ok(content) => {
562                assert_eq!("Hello, world!",content);
563            },
564            Err(e) => {
565                panic!("{}",e.to_string());
566            }
567        }
568
569        match MiniON::parse_name(data, &mut incr) {
570            Ok(name) => {
571                assert_eq!("name",name);
572            },
573            Err(e) => {
574                panic!("{}",e.to_string());
575            }
576        }
577
578        match MiniON::parse_length(data, &mut incr, "name") {
579            Ok(length) => {
580                assert_eq!(6,length);
581            },
582            Err(e) => {
583                panic!("{}",e.to_string());
584            }
585        }
586
587        match MiniON::parse_content(data, &mut incr, "name", 6) {
588            Ok(content) => {
589                assert_eq!("miniON",content);
590            },
591            Err(e) => {
592                panic!("{}",e.to_string());
593            }
594        }
595    }
596
597    #[test]
598    fn test_parse_one() {
599        let data = b"greeting|13~Hello, world!";
600
601        let mut incr: usize = 0;
602
603        match MiniON::parse_one(data, &mut incr) {
604            Ok(minion) => {
605                assert_eq!("greeting",minion.name);
606                
607                match minion.content {
608                    Some(content) => {
609                        assert_eq!("Hello, world!",content);
610                    },
611                    None => {
612                        panic!("No content!");
613                    }
614                }
615            },
616            Err(e) => {
617                panic!("{}",e.to_string());
618            }
619        }
620    }
621
622    #[test]
623    fn test_parse_all() {
624        let data = b"title|12~grocery listdate|10~04/08/2020grocery list|21~1.|6~cheese2.|5~bread";
625
626        match MiniON::parse_all(data) {
627            Ok(minions) => {
628                assert_eq!(3,minions.len());
629
630                assert_eq!("title",minions[0].name);
631                
632                match &minions[0].content {
633                    Some(content) => {
634                        assert_eq!("grocery list",content);
635                    },
636                    None => {
637                        panic!("Expected content!");
638                    }
639                }
640
641                assert_eq!("date",minions[1].name);
642
643                match &minions[1].content {
644                    Some(content) => {
645                        assert_eq!("04/08/2020",content);
646                    },
647                    None => {
648                        panic!("Expected content!");
649                    }
650                }
651
652                assert_eq!("grocery list",minions[2].name);
653
654                match &minions[2].content {
655                    Some(content) => {
656
657                        assert_eq!("1.|6~cheese2.|5~bread",content);
658                        
659                        match MiniON::parse_all(content.as_bytes()) {
660                            Ok(minions) => {
661                                assert_eq!(2,minions.len());
662
663                                assert_eq!("1.",minions[0].name);
664
665                                match &minions[0].content {
666                                    Some(content) => {
667                                        assert_eq!("cheese",content);
668                                    },
669                                    None => {
670                                        panic!("Expected content!");
671                                    }
672                                }
673
674                                assert_eq!("2.",minions[1].name);
675
676                                match &minions[1].content {
677                                    Some(content) => {
678                                        assert_eq!("bread",content);
679                                    },
680                                    None => {
681                                        panic!("Expected content!");
682                                    }
683                                }
684
685                            },
686                            Err(e) => {
687                                panic!("{}",e.to_string());
688                            }
689                        }
690
691                    },
692                    None => {
693                        panic!("Expected content!");
694                    }
695                }
696            },
697            Err(e) => {
698                panic!("{}",e.to_string());
699            }
700        }
701    }
702
703
704    #[test]
705    fn test_find() {
706        let data = b"one|3~ONEtwo|3~TWOthree|5~THREE";
707
708        match MiniON::find(data,"two") {
709            Ok(_) => {
710
711            },
712            Err(e) => {
713                panic!("{}",e.to_string());
714            }
715        }
716    }
717}