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