1use alloc::borrow::Cow;
6use alloc::vec::Vec;
7use core::fmt;
8
9type ParserError = toml::de::Error;
11
12#[derive(Clone, Debug, PartialEq, serde::Deserialize)]
14#[serde(tag = "version")]
15pub enum Config<'a> {
16 #[serde(rename = "1")]
18 V1 {
19 #[serde(borrow)]
21 input: Input<'a>,
22
23 #[serde(default)]
25 requirements: Requirements,
26
27 #[serde(borrow)]
29 kernel: Cow<'a, str>,
30 },
31}
32
33impl Config<'_> {
34 pub const DEFAULT_PATH: &'static str = "hermit.toml";
36}
37
38#[derive(Clone, Debug, PartialEq, serde::Deserialize)]
40pub struct Input<'a> {
41 #[serde(borrow)]
43 pub kernel_args: Vec<Cow<'a, str>>,
44
45 #[serde(borrow)]
47 pub app_args: Vec<Cow<'a, str>>,
48
49 #[serde(borrow, default)]
51 pub env_vars: Vec<Cow<'a, str>>,
52}
53
54#[derive(Clone, Debug, Default, PartialEq, serde::Deserialize)]
56pub struct Requirements {
57 pub memory: Option<byte_unit::Byte>,
59
60 #[serde(default)]
62 pub cpus: u32,
63}
64
65#[derive(Clone, Debug)]
67pub struct ParseTarError(ParseTarErrorInner);
68
69impl From<ParseTarErrorInner> for ParseTarError {
70 #[inline]
71 fn from(x: ParseTarErrorInner) -> Self {
72 Self(x)
73 }
74}
75
76#[derive(Clone, Debug)]
77#[non_exhaustive]
78enum ParseTarErrorInner {
79 TarCorrupt,
81
82 ConfigResolve,
85
86 ConfigUtf8Error(core::str::Utf8Error),
88
89 ConfigTomlParseError(ParserError),
91
92 KernelResolve,
95}
96
97impl fmt::Display for ParseTarErrorInner {
98 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
99 match self {
100 Self::TarCorrupt => f.write_str("tar file in Hermit image is corrupt"),
101 Self::ConfigResolve => {
102 write!(f, "couldn't find Hermit image configuration file")
103 }
104 Self::ConfigUtf8Error(e) => write!(f, "Hermit image configuration is invalid: {e}"),
105 Self::ConfigTomlParseError(e) => {
106 write!(f, "Hermit image configuration is invalid: {e}")
107 }
108 Self::KernelResolve => write!(f, "couldn't find Hermit kernel in image"),
109 }
110 }
111}
112
113impl fmt::Display for ParseTarError {
114 #[inline]
115 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
116 fmt::Display::fmt(&self.0, f)
117 }
118}
119
120impl core::error::Error for ParseTarErrorInner {}
121impl core::error::Error for ParseTarError {}
122
123pub struct ConfigHandle<'a> {
125 pub config: Config<'a>,
127
128 pub raw_kernel: &'a [u8],
130}
131
132pub fn parse_tar(image: &[u8]) -> Result<ConfigHandle<'_>, ParseTarError> {
135 use ParseTarErrorInner as Error;
136
137 let taref = tar_no_std::TarArchiveRef::new(image).map_err(|_| Error::TarCorrupt)?;
138
139 fn lookup_in_image<'a>(
140 taref: &tar_no_std::TarArchiveRef<'a>,
141 f: &str,
142 ) -> Result<Option<&'a [u8]>, Error> {
143 let f = {
144 let f = f.as_bytes();
145 let mut tmp = [0u8; 256];
146 if f.len() >= 256 {
147 return Ok(None);
148 }
149 tmp.copy_from_slice(f);
150 tar_no_std::TarFormatString::new(tmp)
151 };
152
153 let mut ret = None;
154 for i in taref.entries() {
155 if i.filename() == f {
158 ret = Some(i.data());
159 }
160 }
161
162 Ok(ret)
163 }
164
165 let config_slice =
166 lookup_in_image(&taref, Config::DEFAULT_PATH)?.ok_or(Error::ConfigResolve)?;
167 let config_slice = core::str::from_utf8(config_slice).map_err(Error::ConfigUtf8Error)?;
168 let config: Config<'_> = toml::from_str(config_slice).map_err(Error::ConfigTomlParseError)?;
169
170 let kernel_name: &str = match &config {
171 Config::V1 { kernel, .. } => kernel,
172 };
173
174 let raw_kernel = lookup_in_image(&taref, kernel_name)?.ok_or(Error::KernelResolve)?;
175
176 Ok(ConfigHandle { config, raw_kernel })
177}
178
179#[cfg(test)]
180mod tests {
181 #[test]
182 fn test_parsing() {
183 let dat = r#"
184version = "1"
185kernel = "/kernel.elf"
186
187[input]
188kernel_args = []
189app_args = []
190
191[requirements]
192"#;
193 let parsed: super::Config = toml::from_str(dat).unwrap();
194 use alloc::vec;
195 assert_eq!(
196 parsed,
197 super::Config::V1 {
198 kernel: "/kernel.elf".into(),
199 input: super::Input {
200 kernel_args: vec![],
201 app_args: vec![],
202 env_vars: vec![],
203 },
204 requirements: super::Requirements {
205 memory: None,
206 cpus: 0,
207 },
208 }
209 );
210 }
211}