multiboot2/
command_line.rs1use crate::tag::TagHeader;
4use crate::{StringError, TagType, parse_slice_as_string};
5use core::fmt::{Debug, Formatter};
6use core::mem;
7use core::str;
8use multiboot2_common::{MaybeDynSized, Tag};
9#[cfg(feature = "builder")]
10use {alloc::boxed::Box, multiboot2_common::new_boxed};
11
12#[derive(ptr_meta::Pointee, PartialEq, Eq, PartialOrd, Ord, Hash)]
17#[repr(C, align(8))]
18pub struct CommandLineTag {
19 header: TagHeader,
20 cmdline: [u8],
22}
23
24impl CommandLineTag {
25 #[cfg(feature = "builder")]
27 #[must_use]
28 pub fn new(command_line: &str) -> Box<Self> {
29 let header = TagHeader::new(Self::ID, 0);
30 let bytes = command_line.as_bytes();
31 if bytes.ends_with(&[0]) {
32 new_boxed(header, &[bytes])
33 } else {
34 new_boxed(header, &[bytes, &[0]])
35 }
36 }
37
38 pub fn cmdline(&self) -> Result<&str, StringError> {
58 parse_slice_as_string(&self.cmdline)
59 }
60}
61
62impl Debug for CommandLineTag {
63 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
64 f.debug_struct("CommandLineTag")
65 .field("typ", &self.header.typ)
66 .field("size", &self.header.size)
67 .field("cmdline", &self.cmdline())
68 .finish()
69 }
70}
71
72impl MaybeDynSized for CommandLineTag {
73 type Header = TagHeader;
74
75 const BASE_SIZE: usize = mem::size_of::<TagHeader>();
76
77 fn dst_len(header: &TagHeader) -> usize {
78 assert!(header.size as usize >= Self::BASE_SIZE);
79 header.size as usize - Self::BASE_SIZE
80 }
81}
82
83impl Tag for CommandLineTag {
84 type IDType = TagType;
85
86 const ID: TagType = TagType::Cmdline;
87}
88
89#[cfg(test)]
90mod tests {
91 use super::*;
92 use crate::GenericInfoTag;
93 use core::borrow::Borrow;
94 use multiboot2_common::test_utils::AlignedBytes;
95
96 #[rustfmt::skip]
97 fn get_bytes() -> AlignedBytes<16> {
98 AlignedBytes::new([
99 TagType::Cmdline.val() as u8, 0, 0, 0,
100 14, 0, 0, 0,
101 b'h', b'e', b'l', b'l', b'o', b'\0',
102 0, 0
104 ])
105 }
106
107 #[test]
109 fn test_parse_str() {
110 let bytes = get_bytes();
111 let tag = GenericInfoTag::ref_from_slice(bytes.borrow()).unwrap();
112 let tag = tag.cast::<CommandLineTag>();
113 assert_eq!(tag.header.typ, TagType::Cmdline);
114 assert_eq!(tag.cmdline(), Ok("hello"));
115 }
116
117 #[test]
119 #[cfg(feature = "builder")]
120 fn test_build_str() {
121 let tag = CommandLineTag::new("hello");
122 let bytes = tag.as_bytes().as_ref();
123 let bytes = &bytes[..tag.header.size as usize];
124 assert_eq!(bytes, &get_bytes()[..tag.header().size as usize]);
125 assert_eq!(tag.cmdline(), Ok("hello"));
126
127 let tag = CommandLineTag::new("hello\0");
129 let bytes = tag.as_bytes().as_ref();
130 let bytes = &bytes[..tag.header.size as usize];
131 assert_eq!(bytes, &get_bytes()[..tag.header().size as usize]);
132 assert_eq!(tag.cmdline(), Ok("hello"));
133
134 let tag = CommandLineTag::new("AbCdEfGhUjK YEAH");
136 assert_eq!(tag.cmdline(), Ok("AbCdEfGhUjK YEAH"));
137 let tag = CommandLineTag::new("AbCdEfGhUjK YEAH".repeat(42).as_str());
138 assert_eq!(tag.cmdline(), Ok("AbCdEfGhUjK YEAH".repeat(42).as_str()));
139 }
140}