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}