1#![cfg_attr(not(feature = "std"), no_std)]
38#[cfg(not(feature = "std"))]
39extern crate alloc;
40
41#[cfg(not(feature = "std"))]
42use alloc::format;
43#[cfg(not(feature = "std"))]
44use alloc::string::String;
45#[cfg(not(feature = "std"))]
46use alloc::vec::Vec;
47
48#[cfg(not(feature = "std"))]
49use alloc::str::FromStr;
50#[cfg(feature = "std")]
51use std::str::FromStr;
52
53#[derive(Debug, PartialEq)]
55pub enum BLSValue {
56 Value(String),
58 ValueWithComment(String, String),
60}
61
62#[derive(Debug, PartialEq)]
64pub enum ValueSetPolicy {
65 ReplaceAll,
67 Append,
69 Prepend,
71 InsertAt(usize),
73}
74
75#[derive(Debug, PartialEq)]
77pub enum BLSKey {
78 Title,
80 Version,
82 MachineId,
84 SortKey,
86 Linux,
88 Efi,
90 Initrd,
92 Options,
94 Devicetree,
96 DevicetreeOverlay,
98 Architecture,
100 GrubHotkey,
102 GrubUsers,
104 GrubClass,
106 GrubArg,
108}
109
110impl FromStr for BLSKey {
111 type Err = String;
112
113 fn from_str(key: &str) -> Result<Self, Self::Err> {
114 match key {
115 "linux" => Ok(BLSKey::Linux),
116 "title" => Ok(BLSKey::Title),
117 "version" => Ok(BLSKey::Version),
118 "machine_id" | "machine-id" => Ok(BLSKey::MachineId),
119 "sort_key" | "sort-key" => Ok(BLSKey::SortKey),
120 "efi" => Ok(BLSKey::Efi),
121 "initrd" => Ok(BLSKey::Initrd),
122 "options" => Ok(BLSKey::Options),
123 "devicetree" => Ok(BLSKey::Devicetree),
124 "devicetree_overlay" | "devicetree-overlay" => Ok(BLSKey::DevicetreeOverlay),
125 "architecture" => Ok(BLSKey::Architecture),
126 "grub_hotkey" => Ok(BLSKey::GrubHotkey),
127 "grub_users" => Ok(BLSKey::GrubUsers),
128 "grub_class" => Ok(BLSKey::GrubClass),
129 "grub_arg" => Ok(BLSKey::GrubArg),
130 _ => Err(format!("Invalid key {}", key)),
131 }
132 }
133}
134
135#[derive(Debug)]
139pub struct BLSEntry {
140 pub title: Option<BLSValue>,
142 pub version: Option<BLSValue>,
144 pub machine_id: Option<BLSValue>,
146 pub sort_key: Option<BLSValue>,
148 pub linux: BLSValue,
150 pub efi: Option<BLSValue>,
152 pub initrd: Vec<BLSValue>,
154 pub options: Vec<BLSValue>,
156 pub devicetree: Option<BLSValue>,
158 pub devicetree_overlay: Option<BLSValue>,
160 pub architecture: Option<BLSValue>,
162 pub grub_hotkey: Option<BLSValue>,
164 pub grub_users: Option<BLSValue>,
166 pub grub_class: Vec<BLSValue>,
168 pub grub_arg: Option<BLSValue>,
170 pub comments: Vec<String>,
172}
173
174impl BLSEntry {
175 pub fn new() -> BLSEntry {
177 BLSEntry {
178 title: None,
179 version: None,
180 machine_id: None,
181 sort_key: None,
182 linux: BLSValue::Value(String::new()),
183 efi: None,
184 initrd: Vec::new(),
185 options: Vec::new(),
186 devicetree: None,
187 devicetree_overlay: None,
188 architecture: None,
189 grub_hotkey: None,
190 grub_users: None,
191 grub_class: Vec::new(),
192 grub_arg: None,
193 comments: Vec::new(),
194 }
195 }
196}
197
198impl Default for BLSEntry {
199 fn default() -> Self {
200 Self::new()
201 }
202}
203
204impl BLSEntry {
205 pub fn parse(buffer: &str) -> Result<BLSEntry, String> {
220 let mut entry = BLSEntry::new();
221 let mut has_linux = false;
222 let mut has_efi = false;
223
224 for line in buffer.lines() {
225 let mut comment = None;
226 let line = if line.contains("#") {
228 let split: Vec<_> = line.splitn(2, "#").collect();
229 comment = Some(String::from(split[1]));
230 split[0]
231 } else {
232 line
233 };
234
235 if line.trim().contains(" ") {
237 let key_value: Vec<&str> = line.trim().splitn(2, " ").collect();
238
239 let key = BLSKey::from_str(key_value[0])?;
240 if key == BLSKey::Linux {
241 has_linux = true;
242 } else if key == BLSKey::Efi {
243 has_efi = true;
244 }
245 entry.set(
246 key,
247 String::from(key_value[1]),
248 comment,
249 ValueSetPolicy::Append,
250 );
251 } else if let Some(comment) = comment {
252 entry.comments.push(comment);
253 }
254 }
255
256 if has_linux || has_efi {
257 Ok(entry)
258 } else {
259 Err(String::from("No 'linux' or 'efi' command found."))
260 }
261 }
262
263 pub fn render(&self) -> String {
278 let mut content = String::new();
279
280 fn render_value(content: &mut String, key: &str, value: &BLSValue) {
281 content.push_str(key);
282 content.push(' ');
283 match value {
284 BLSValue::Value(value) => content.push_str(value),
285 BLSValue::ValueWithComment(value, comment) => {
286 content.push_str(value);
287 content.push_str(" #");
288 content.push_str(comment);
289 }
290 }
291 content.push('\n');
292 }
293
294 fn render_single_value(content: &mut String, key: &str, value: &Option<BLSValue>) {
295 if let Some(value) = value {
296 render_value(content, key, value)
297 }
298 }
299
300 fn render_multiple_values(content: &mut String, key: &str, values: &Vec<BLSValue>) {
301 for val in values {
302 render_value(content, key, val)
303 }
304 }
305
306 for comment in &self.comments {
308 content.push('#');
309 content.push_str(comment);
310 content.push('\n');
311 }
312
313 render_value(&mut content, "linux", &self.linux);
315
316 render_single_value(&mut content, "title", &self.title);
318 render_single_value(&mut content, "version", &self.version);
319 render_single_value(&mut content, "machine-id", &self.machine_id);
320 render_single_value(&mut content, "sort-key", &self.sort_key);
321 render_single_value(&mut content, "efi", &self.efi);
322 render_single_value(&mut content, "devicetree", &self.devicetree);
323 render_single_value(&mut content, "devicetree-overlay", &self.devicetree_overlay);
324 render_single_value(&mut content, "architecture", &self.architecture);
325 render_single_value(&mut content, "grub_hotkey", &self.grub_hotkey);
326 render_single_value(&mut content, "grub_users", &self.grub_users);
327 render_single_value(&mut content, "grub_arg", &self.grub_arg);
328
329 render_multiple_values(&mut content, "initrd", &self.initrd);
331 render_multiple_values(&mut content, "options", &self.options);
332 render_multiple_values(&mut content, "grub_class", &self.grub_class);
333
334 content
335 }
336
337 pub fn set(
344 &mut self,
345 key: BLSKey,
346 value: String,
347 comment: Option<String>,
348 set_policy: ValueSetPolicy,
349 ) {
350 fn value_generator(value: String, comment: Option<String>) -> BLSValue {
351 match comment {
352 Some(comment) => BLSValue::ValueWithComment(value, comment),
353 None => BLSValue::Value(value),
354 }
355 }
356
357 fn push_value(values: &mut Vec<BLSValue>, val: BLSValue, policy: ValueSetPolicy) {
358 match policy {
359 ValueSetPolicy::Append => values.push(val),
360 ValueSetPolicy::InsertAt(i) => values.insert(i, val),
361 ValueSetPolicy::Prepend => values.insert(0, val),
362 ValueSetPolicy::ReplaceAll => {
363 values.clear();
364 values.push(val);
365 }
366 }
367 }
368
369 match key {
370 BLSKey::Title => self.title = Some(value_generator(value, comment)),
371 BLSKey::Version => self.version = Some(value_generator(value, comment)),
372 BLSKey::MachineId => self.machine_id = Some(value_generator(value, comment)),
373 BLSKey::SortKey => self.sort_key = Some(value_generator(value, comment)),
374 BLSKey::Linux => self.linux = value_generator(value, comment),
375 BLSKey::Efi => self.efi = Some(value_generator(value, comment)),
376 BLSKey::Devicetree => self.devicetree = Some(value_generator(value, comment)),
377 BLSKey::DevicetreeOverlay => {
378 self.devicetree_overlay = Some(value_generator(value, comment))
379 }
380 BLSKey::Architecture => self.architecture = Some(value_generator(value, comment)),
381 BLSKey::GrubHotkey => self.grub_hotkey = Some(value_generator(value, comment)),
382 BLSKey::GrubUsers => self.grub_users = Some(value_generator(value, comment)),
383 BLSKey::GrubArg => self.grub_arg = Some(value_generator(value, comment)),
384
385 BLSKey::Initrd => push_value(
386 &mut self.initrd,
387 value_generator(value, comment),
388 set_policy,
389 ),
390 BLSKey::Options => push_value(
391 &mut self.options,
392 value_generator(value, comment),
393 set_policy,
394 ),
395 BLSKey::GrubClass => push_value(
396 &mut self.grub_class,
397 value_generator(value, comment),
398 set_policy,
399 ),
400 }
401 }
402
403 pub fn clear(&mut self, key: BLSKey) {
405 match key {
406 BLSKey::Linux => self.linux = BLSValue::Value(String::from("")),
407 BLSKey::Title => self.title = None,
408 BLSKey::Version => self.version = None,
409 BLSKey::MachineId => self.machine_id = None,
410 BLSKey::SortKey => self.sort_key = None,
411 BLSKey::Efi => self.efi = None,
412 BLSKey::Devicetree => self.devicetree = None,
413 BLSKey::DevicetreeOverlay => self.devicetree_overlay = None,
414 BLSKey::Architecture => self.architecture = None,
415 BLSKey::GrubHotkey => self.grub_hotkey = None,
416 BLSKey::GrubUsers => self.grub_users = None,
417 BLSKey::GrubArg => self.grub_arg = None,
418
419 BLSKey::Initrd => self.initrd.clear(),
420 BLSKey::Options => self.options.clear(),
421 BLSKey::GrubClass => self.grub_class.clear(),
422 }
423 }
424}
425
426#[cfg(test)]
427mod bls_tests {
428 use core::str::FromStr;
429
430 #[cfg(not(feature = "std"))]
431 use alloc::string::String;
432 #[cfg(not(feature = "std"))]
433 use alloc::vec;
434
435 use super::BLSEntry;
436 use super::BLSKey;
437 use super::BLSValue;
438 use super::ValueSetPolicy;
439
440 #[test]
441 fn bls_key_from_str() {
442 assert!(BLSKey::from_str("linux").is_ok());
443 assert!(BLSKey::from_str("title").is_ok());
444 assert!(BLSKey::from_str("version").is_ok());
445 assert!(BLSKey::from_str("machine_id").is_ok());
446 assert!(BLSKey::from_str("machine-id").is_ok());
447 assert!(BLSKey::from_str("sort_key").is_ok());
448 assert!(BLSKey::from_str("sort-key").is_ok());
449 assert!(BLSKey::from_str("efi").is_ok());
450 assert!(BLSKey::from_str("initrd").is_ok());
451 assert!(BLSKey::from_str("options").is_ok());
452 assert!(BLSKey::from_str("devicetree").is_ok());
453 assert!(BLSKey::from_str("devicetree_overlay").is_ok());
454 assert!(BLSKey::from_str("devicetree-overlay").is_ok());
455 assert!(BLSKey::from_str("architecture").is_ok());
456 assert!(BLSKey::from_str("grub_hotkey").is_ok());
457 assert!(BLSKey::from_str("grub_users").is_ok());
458 assert!(BLSKey::from_str("grub_class").is_ok());
459 assert!(BLSKey::from_str("grub_arg").is_ok());
460 assert!(BLSKey::from_str("invalid_key").is_err());
461 }
462
463 #[test]
464 fn new_entry() {
465 let entry = BLSEntry::new();
466 match &entry.linux {
467 BLSValue::Value(linux) => assert_eq!(linux, ""),
468 _ => panic!("Invalid 'linux' value {:?}", entry.linux),
469 }
470 assert!(entry.title.is_none());
471 assert!(entry.version.is_none());
472 assert!(entry.machine_id.is_none());
473 assert!(entry.sort_key.is_none());
474 assert!(entry.efi.is_none());
475 assert_eq!(entry.initrd.len(), 0);
476 assert_eq!(entry.options.len(), 0);
477 assert!(entry.devicetree.is_none());
478 assert!(entry.devicetree_overlay.is_none());
479 assert!(entry.architecture.is_none());
480 assert!(entry.grub_hotkey.is_none());
481 assert!(entry.grub_users.is_none());
482 assert_eq!(entry.grub_class.len(), 0);
483 assert!(entry.grub_arg.is_none());
484 assert!(entry.comments.is_empty());
485 }
486
487 #[test]
488 fn parse_entry() {
489 let entry_txt = "#Comment\n\
490 linux foobar-2.4\n\
491 options foo=bar #Another Comment";
492 let entry = BLSEntry::parse(entry_txt);
493
494 assert!(entry.is_ok());
495 let entry = entry.unwrap();
496 assert_eq!(entry.comments.len(), 1);
497 assert_eq!(entry.comments[0], "Comment");
498
499 if let BLSValue::Value(linux) = entry.linux {
500 assert_eq!(linux, "foobar-2.4");
501 }
502
503 assert_eq!(entry.options.len(), 1);
504 match &entry.options[0] {
505 BLSValue::ValueWithComment(option, comment) => {
506 assert_eq!(option, "foo=bar");
507 assert_eq!(comment, "Another Comment");
508 }
509 _ => {
510 panic!("Invalid 'options' value {:?}", entry.options[0])
511 }
512 }
513 }
514
515 #[test]
516 fn parse_errors() {
517 let entry_txt = "options foo=bar";
519 let entry = BLSEntry::parse(entry_txt);
520 assert!(entry.is_err());
521
522 let entry_txt = "linux asdasdasdas\n\
524 invalid_command foo=bar";
525 let entry = BLSEntry::parse(entry_txt);
526 assert!(entry.is_err());
527 }
528
529 #[test]
530 fn parse_efi_only() {
531 let entry_txt = "title EFI App\nefi /EFI/app.efi";
532 let entry = BLSEntry::parse(entry_txt).expect("efi-only entry should parse");
533 assert!(entry.efi.is_some());
534 if let Some(BLSValue::Value(ref path)) = entry.efi {
535 assert_eq!(path, "/EFI/app.efi");
536 }
537 }
538
539 #[test]
540 fn parse_hyphenated_keys() {
541 let entry_txt = "title Fedora\nmachine-id 6a9857a393724b7a981ebb5b8495b9ea\nsort-key fedora\ndevicetree-overlay /overlay.dtbo\nlinux /vmlinuz";
542 let entry = BLSEntry::parse(entry_txt).expect("hyphenated keys should parse");
543 assert_eq!(
544 entry.title.as_ref().map(|v| match v {
545 BLSValue::Value(s) => s.as_str(),
546 _ => "",
547 }),
548 Some("Fedora")
549 );
550 assert!(entry.machine_id.is_some());
551 assert!(entry.sort_key.is_some());
552 assert!(entry.devicetree_overlay.is_some());
553 }
554
555 #[test]
556 fn parse_multiple_initrd_options() {
557 let entry_txt =
558 "linux /vmlinuz\ninitrd /initrd1\ninitrd /initrd2\noptions a=1\noptions b=2";
559 let entry = BLSEntry::parse(entry_txt).unwrap();
560 assert_eq!(entry.initrd.len(), 2);
561 assert_eq!(entry.options.len(), 2);
562 }
563
564 #[test]
565 fn parse_round_trip() {
566 let entry_txt = "title Fedora 19\nsort-key fedora\nmachine-id 6a9857a393724b7a981ebb5b8495b9ea\nversion 3.8.0-2.fc19.x86_64\noptions root=UUID=abc quiet\narchitecture x64\nlinux /6a9857a393724b7a981ebb5b8495b9ea/3.8.0-2.fc19.x86_64/linux\ninitrd /6a9857a393724b7a981ebb5b8495b9ea/3.8.0-2.fc19.x86_64/initrd";
567 let entry = BLSEntry::parse(entry_txt).unwrap();
568 let rendered = entry.render();
569 let entry2 = BLSEntry::parse(&rendered).unwrap();
570 assert_eq!(entry.title, entry2.title);
571 assert_eq!(entry.version, entry2.version);
572 assert_eq!(entry.machine_id, entry2.machine_id);
573 assert_eq!(entry.sort_key, entry2.sort_key);
574 assert_eq!(entry.linux, entry2.linux);
575 assert_eq!(entry.initrd, entry2.initrd);
576 assert_eq!(entry.options, entry2.options);
577 assert_eq!(entry.architecture, entry2.architecture);
578 }
579
580 #[test]
581 fn render_all_keys_including_grub() {
582 let mut entry = BLSEntry::new();
583 entry.set(
584 BLSKey::Linux,
585 String::from("/vmlinuz"),
586 None,
587 ValueSetPolicy::ReplaceAll,
588 );
589 entry.set(
590 BLSKey::Title,
591 String::from("Test"),
592 None,
593 ValueSetPolicy::ReplaceAll,
594 );
595 entry.set(
596 BLSKey::GrubHotkey,
597 String::from("t"),
598 None,
599 ValueSetPolicy::ReplaceAll,
600 );
601 entry.set(
602 BLSKey::GrubUsers,
603 String::from("root"),
604 None,
605 ValueSetPolicy::ReplaceAll,
606 );
607 entry.set(
608 BLSKey::GrubArg,
609 String::from("--debug"),
610 None,
611 ValueSetPolicy::ReplaceAll,
612 );
613 entry.set(
614 BLSKey::GrubClass,
615 String::from("recovery"),
616 None,
617 ValueSetPolicy::Append,
618 );
619 let out = entry.render();
620 assert!(out.contains("grub_hotkey t"));
621 assert!(out.contains("grub_users root"));
622 assert!(out.contains("grub_arg --debug"));
623 assert!(out.contains("grub_class recovery"));
624 }
625
626 #[test]
627 fn set_every_key() {
628 let mut entry = BLSEntry::new();
629 entry.set(
630 BLSKey::Linux,
631 String::from("/vmlinuz"),
632 None,
633 ValueSetPolicy::ReplaceAll,
634 );
635 entry.set(
636 BLSKey::Title,
637 String::from("T"),
638 None,
639 ValueSetPolicy::ReplaceAll,
640 );
641 entry.set(
642 BLSKey::Version,
643 String::from("1.0"),
644 None,
645 ValueSetPolicy::ReplaceAll,
646 );
647 entry.set(
648 BLSKey::MachineId,
649 String::from("abc"),
650 None,
651 ValueSetPolicy::ReplaceAll,
652 );
653 entry.set(
654 BLSKey::SortKey,
655 String::from("x"),
656 None,
657 ValueSetPolicy::ReplaceAll,
658 );
659 entry.set(
660 BLSKey::Efi,
661 String::from("/efi.efi"),
662 None,
663 ValueSetPolicy::ReplaceAll,
664 );
665 entry.set(
666 BLSKey::Initrd,
667 String::from("/i1"),
668 None,
669 ValueSetPolicy::Append,
670 );
671 entry.set(
672 BLSKey::Options,
673 String::from("opt"),
674 None,
675 ValueSetPolicy::Append,
676 );
677 entry.set(
678 BLSKey::Devicetree,
679 String::from("/dtb"),
680 None,
681 ValueSetPolicy::ReplaceAll,
682 );
683 entry.set(
684 BLSKey::DevicetreeOverlay,
685 String::from("/overlay"),
686 None,
687 ValueSetPolicy::ReplaceAll,
688 );
689 entry.set(
690 BLSKey::Architecture,
691 String::from("x64"),
692 None,
693 ValueSetPolicy::ReplaceAll,
694 );
695 entry.set(
696 BLSKey::GrubHotkey,
697 String::from("h"),
698 None,
699 ValueSetPolicy::ReplaceAll,
700 );
701 entry.set(
702 BLSKey::GrubUsers,
703 String::from("u"),
704 None,
705 ValueSetPolicy::ReplaceAll,
706 );
707 entry.set(
708 BLSKey::GrubClass,
709 String::from("c"),
710 None,
711 ValueSetPolicy::Append,
712 );
713 entry.set(
714 BLSKey::GrubArg,
715 String::from("a"),
716 None,
717 ValueSetPolicy::ReplaceAll,
718 );
719 assert!(entry.title.is_some());
720 assert!(entry.version.is_some());
721 assert!(entry.machine_id.is_some());
722 assert!(entry.sort_key.is_some());
723 assert!(entry.efi.is_some());
724 assert_eq!(entry.initrd.len(), 1);
725 assert_eq!(entry.options.len(), 1);
726 assert!(entry.devicetree.is_some());
727 assert!(entry.devicetree_overlay.is_some());
728 assert!(entry.architecture.is_some());
729 assert!(entry.grub_hotkey.is_some());
730 assert!(entry.grub_users.is_some());
731 assert_eq!(entry.grub_class.len(), 1);
732 assert!(entry.grub_arg.is_some());
733 }
734
735 #[test]
736 fn set_value_policies_initrd_grub_class() {
737 let mut entry = BLSEntry::new();
738 entry.set(
739 BLSKey::Linux,
740 String::from("/vmlinuz"),
741 None,
742 ValueSetPolicy::ReplaceAll,
743 );
744 entry.set(
745 BLSKey::Initrd,
746 String::from("a"),
747 None,
748 ValueSetPolicy::Append,
749 );
750 entry.set(
751 BLSKey::Initrd,
752 String::from("b"),
753 None,
754 ValueSetPolicy::Append,
755 );
756 entry.set(
757 BLSKey::Initrd,
758 String::from("mid"),
759 None,
760 ValueSetPolicy::InsertAt(1),
761 );
762 assert_eq!(entry.initrd.len(), 3);
763 entry.set(
764 BLSKey::Initrd,
765 String::from("first"),
766 None,
767 ValueSetPolicy::Prepend,
768 );
769 assert!(matches!(entry.initrd.first(), Some(BLSValue::Value(s)) if s == "first"));
770 entry.set(
771 BLSKey::Initrd,
772 String::from("only"),
773 None,
774 ValueSetPolicy::ReplaceAll,
775 );
776 assert_eq!(entry.initrd.len(), 1);
777 entry.set(
778 BLSKey::GrubClass,
779 String::from("class1"),
780 None,
781 ValueSetPolicy::Append,
782 );
783 entry.set(
784 BLSKey::GrubClass,
785 String::from("class2"),
786 None,
787 ValueSetPolicy::Append,
788 );
789 assert_eq!(entry.grub_class.len(), 2);
790 }
791
792 #[test]
793 fn clear_every_key() {
794 let mut entry = BLSEntry::new();
795 entry.set(
796 BLSKey::Linux,
797 String::from("/vmlinuz"),
798 None,
799 ValueSetPolicy::ReplaceAll,
800 );
801 entry.set(
802 BLSKey::Title,
803 String::from("T"),
804 None,
805 ValueSetPolicy::ReplaceAll,
806 );
807 entry.set(
808 BLSKey::Version,
809 String::from("1"),
810 None,
811 ValueSetPolicy::ReplaceAll,
812 );
813 entry.set(
814 BLSKey::MachineId,
815 String::from("m"),
816 None,
817 ValueSetPolicy::ReplaceAll,
818 );
819 entry.set(
820 BLSKey::SortKey,
821 String::from("s"),
822 None,
823 ValueSetPolicy::ReplaceAll,
824 );
825 entry.set(
826 BLSKey::Efi,
827 String::from("e"),
828 None,
829 ValueSetPolicy::ReplaceAll,
830 );
831 entry.set(
832 BLSKey::Initrd,
833 String::from("i"),
834 None,
835 ValueSetPolicy::Append,
836 );
837 entry.set(
838 BLSKey::Options,
839 String::from("o"),
840 None,
841 ValueSetPolicy::Append,
842 );
843 entry.set(
844 BLSKey::Devicetree,
845 String::from("d"),
846 None,
847 ValueSetPolicy::ReplaceAll,
848 );
849 entry.set(
850 BLSKey::DevicetreeOverlay,
851 String::from("do"),
852 None,
853 ValueSetPolicy::ReplaceAll,
854 );
855 entry.set(
856 BLSKey::Architecture,
857 String::from("a"),
858 None,
859 ValueSetPolicy::ReplaceAll,
860 );
861 entry.set(
862 BLSKey::GrubHotkey,
863 String::from("g"),
864 None,
865 ValueSetPolicy::ReplaceAll,
866 );
867 entry.set(
868 BLSKey::GrubUsers,
869 String::from("u"),
870 None,
871 ValueSetPolicy::ReplaceAll,
872 );
873 entry.set(
874 BLSKey::GrubClass,
875 String::from("c"),
876 None,
877 ValueSetPolicy::Append,
878 );
879 entry.set(
880 BLSKey::GrubArg,
881 String::from("ga"),
882 None,
883 ValueSetPolicy::ReplaceAll,
884 );
885 entry.clear(BLSKey::Title);
886 entry.clear(BLSKey::Version);
887 entry.clear(BLSKey::MachineId);
888 entry.clear(BLSKey::SortKey);
889 entry.clear(BLSKey::Efi);
890 entry.clear(BLSKey::Initrd);
891 entry.clear(BLSKey::Options);
892 entry.clear(BLSKey::Devicetree);
893 entry.clear(BLSKey::DevicetreeOverlay);
894 entry.clear(BLSKey::Architecture);
895 entry.clear(BLSKey::GrubHotkey);
896 entry.clear(BLSKey::GrubUsers);
897 entry.clear(BLSKey::GrubClass);
898 entry.clear(BLSKey::GrubArg);
899 entry.clear(BLSKey::Linux);
900 assert!(entry.title.is_none());
901 assert!(entry.version.is_none());
902 assert!(entry.machine_id.is_none());
903 assert!(entry.sort_key.is_none());
904 assert!(entry.efi.is_none());
905 assert!(entry.initrd.is_empty());
906 assert!(entry.options.is_empty());
907 assert!(entry.devicetree.is_none());
908 assert!(entry.devicetree_overlay.is_none());
909 assert!(entry.architecture.is_none());
910 assert!(entry.grub_hotkey.is_none());
911 assert!(entry.grub_users.is_none());
912 assert!(entry.grub_class.is_empty());
913 assert!(entry.grub_arg.is_none());
914 assert!(matches!(&entry.linux, BLSValue::Value(s) if s.is_empty()));
915 }
916
917 #[test]
918 fn bls_value_value_with_comment() {
919 let v = BLSValue::Value(String::from("arg"));
920 let vc = BLSValue::ValueWithComment(String::from("arg"), String::from("comment"));
921 assert!(matches!(&v, BLSValue::Value(s) if s == "arg"));
922 assert!(matches!(&vc, BLSValue::ValueWithComment(a, c) if a == "arg" && c == "comment"));
923 }
924
925 #[test]
926 fn set_value_policies() {
927 let mut entry = BLSEntry::new();
929 let _ = entry.set(
930 BLSKey::Options,
931 String::from("foo"),
932 None,
933 ValueSetPolicy::Append,
934 );
935 let _ = entry.set(
936 BLSKey::Options,
937 String::from("bar"),
938 None,
939 ValueSetPolicy::Append,
940 );
941 let _ = entry.set(
942 BLSKey::Options,
943 String::from("baz"),
944 None,
945 ValueSetPolicy::Append,
946 );
947
948 assert_eq!(
949 entry.options,
950 vec![
951 BLSValue::Value(String::from("foo")),
952 BLSValue::Value(String::from("bar")),
953 BLSValue::Value(String::from("baz"))
954 ]
955 );
956
957 let _ = entry.set(
959 BLSKey::Options,
960 String::from("lol"),
961 None,
962 ValueSetPolicy::InsertAt(1),
963 );
964 assert_eq!(
965 entry.options,
966 vec![
967 BLSValue::Value(String::from("foo")),
968 BLSValue::Value(String::from("lol")),
969 BLSValue::Value(String::from("bar")),
970 BLSValue::Value(String::from("baz"))
971 ]
972 );
973
974 let _ = entry.set(
976 BLSKey::Options,
977 String::from("wtf"),
978 None,
979 ValueSetPolicy::ReplaceAll,
980 );
981 assert_eq!(entry.options, vec![BLSValue::Value(String::from("wtf"))]);
982
983 let _ = entry.set(
985 BLSKey::Options,
986 String::from("uwu"),
987 None,
988 ValueSetPolicy::Prepend,
989 );
990 assert_eq!(
991 entry.options,
992 vec![
993 BLSValue::Value(String::from("uwu")),
994 BLSValue::Value(String::from("wtf"))
995 ]
996 );
997
998 entry.clear(BLSKey::Options);
1000 assert_eq!(entry.options, vec![]);
1001
1002 entry.set(
1003 BLSKey::Title,
1004 String::from("foobar"),
1005 None,
1006 ValueSetPolicy::Append,
1007 );
1008 assert_eq!(entry.title, Some(BLSValue::Value(String::from("foobar"))));
1009
1010 entry.clear(BLSKey::Title);
1011 assert_eq!(entry.title, None);
1012 }
1013}