1use std::{fmt, str::FromStr};
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
12pub enum Libc {
13 Glibc,
14 Musl,
15 Lamina,
18}
19
20impl Libc {
21 pub const fn triple_component(self) -> &'static str {
23 match self {
24 Self::Glibc => "gnu",
25 Self::Musl => "musl",
26 Self::Lamina => "lamina",
27 }
28 }
29
30 pub const fn is_implemented(self) -> bool {
35 match self {
36 Self::Glibc | Self::Musl => true,
37 Self::Lamina => false,
38 }
39 }
40
41 pub const fn unavailable_reason(self) -> Option<&'static str> {
43 match self {
44 Self::Glibc | Self::Musl => None,
45 Self::Lamina => Some(
46 "the Lamina C library is not implemented yet: there is no Lamina libc to link, no start-up objects and no dynamic loader for it",
47 ),
48 }
49 }
50}
51
52impl fmt::Display for Libc {
53 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
54 let s = match self {
55 Self::Glibc => "glibc",
56 Self::Musl => "musl",
57 Self::Lamina => "lamina",
58 };
59 write!(f, "{s}")
60 }
61}
62
63#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
68pub enum CompilerRuntime {
69 LaminaBuiltins,
73 None,
75}
76
77#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
79pub enum TargetEnvironment {
80 Gnu,
82 Musl,
84 Lamina,
90 Eabi,
92 Eabihf,
94 Freestanding,
97}
98
99impl TargetEnvironment {
100 pub const ALL: [Self; 6] = [
102 Self::Gnu,
103 Self::Musl,
104 Self::Lamina,
105 Self::Eabi,
106 Self::Eabihf,
107 Self::Freestanding,
108 ];
109
110 pub const fn token(self) -> &'static str {
112 match self {
113 Self::Gnu => "gnu",
114 Self::Musl => "musl",
115 Self::Lamina => "lamina",
116 Self::Eabi => "eabi",
117 Self::Eabihf => "eabihf",
118 Self::Freestanding => "none",
119 }
120 }
121
122 pub const fn libc(self) -> Option<Libc> {
124 match self {
125 Self::Gnu | Self::Eabi | Self::Eabihf => Some(Libc::Glibc),
126 Self::Musl => Some(Libc::Musl),
127 Self::Lamina => Some(Libc::Lamina),
128 Self::Freestanding => None,
129 }
130 }
131
132 pub const fn compiler_runtime(self) -> CompilerRuntime {
134 match self {
135 Self::Gnu | Self::Musl | Self::Lamina | Self::Eabi | Self::Eabihf => {
136 CompilerRuntime::LaminaBuiltins
137 }
138 Self::Freestanding => CompilerRuntime::None,
139 }
140 }
141
142 pub const fn hardware_float(self) -> bool {
145 match self {
146 Self::Gnu | Self::Musl | Self::Lamina | Self::Eabihf => true,
147 Self::Eabi | Self::Freestanding => false,
148 }
149 }
150
151 pub const fn is_hosted(self) -> bool {
153 self.libc().is_some()
154 }
155}
156
157impl fmt::Display for TargetEnvironment {
158 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
159 write!(f, "{}", self.token())
160 }
161}
162
163impl FromStr for TargetEnvironment {
164 type Err = &'static str;
165
166 fn from_str(s: &str) -> Result<Self, Self::Err> {
167 match s {
168 "gnu" => Ok(Self::Gnu),
169 "musl" => Ok(Self::Musl),
170 "lamina" => Ok(Self::Lamina),
171 "eabi" => Ok(Self::Eabi),
172 "eabihf" => Ok(Self::Eabihf),
173 "none" => Ok(Self::Freestanding),
174 _ => Err("Unknown target environment"),
175 }
176 }
177}
178
179#[cfg(test)]
180mod tests {
181 use super::*;
182
183 #[test]
184 fn tokens_round_trip() {
185 for env in TargetEnvironment::ALL {
186 assert_eq!(env.token().parse(), Ok(env));
187 assert_eq!(env.to_string(), env.token());
188 }
189 assert!("glibc".parse::<TargetEnvironment>().is_err());
190 }
191
192 #[test]
193 fn freestanding_links_neither_libc_nor_a_compiler_runtime() {
194 assert_eq!(TargetEnvironment::Freestanding.libc(), None);
195 assert_eq!(
196 TargetEnvironment::Freestanding.compiler_runtime(),
197 CompilerRuntime::None
198 );
199 assert!(!TargetEnvironment::Freestanding.is_hosted());
200 }
201
202 #[test]
203 fn musl_takes_the_same_compiler_runtime_as_gnu() {
204 assert_eq!(TargetEnvironment::Musl.libc(), Some(Libc::Musl));
205 assert_eq!(
206 TargetEnvironment::Musl.compiler_runtime(),
207 CompilerRuntime::LaminaBuiltins
208 );
209 assert_eq!(
210 TargetEnvironment::Gnu.compiler_runtime(),
211 CompilerRuntime::LaminaBuiltins
212 );
213 assert_eq!(Libc::Musl.triple_component(), "musl");
214 assert_eq!(Libc::Glibc.triple_component(), "gnu");
215 }
216
217 #[test]
218 fn every_hosted_environment_selects_our_own_compiler_runtime() {
219 for env in TargetEnvironment::ALL {
220 let expected = if env.is_hosted() {
221 CompilerRuntime::LaminaBuiltins
222 } else {
223 CompilerRuntime::None
224 };
225 assert_eq!(env.compiler_runtime(), expected, "{env}");
226 }
227 }
228
229 #[test]
230 fn lamina_is_a_libc_of_its_own_taking_our_own_compiler_runtime() {
231 let env = TargetEnvironment::Lamina;
232 assert_eq!(env.token(), "lamina");
233 assert_eq!(env.libc(), Some(Libc::Lamina));
234 assert_eq!(env.compiler_runtime(), CompilerRuntime::LaminaBuiltins);
235 assert!(env.is_hosted());
236 assert!(env.hardware_float());
237 assert_eq!(Libc::Lamina.triple_component(), "lamina");
238 assert_eq!(Libc::Lamina.to_string(), "lamina");
239 }
240
241 #[test]
242 fn the_lamina_libc_is_the_one_that_is_not_implemented_yet() {
243 assert!(Libc::Glibc.is_implemented());
244 assert!(Libc::Musl.is_implemented());
245 assert!(!Libc::Lamina.is_implemented());
246 assert_eq!(Libc::Glibc.unavailable_reason(), None);
247 assert_eq!(Libc::Musl.unavailable_reason(), None);
248 let reason = Libc::Lamina
249 .unavailable_reason()
250 .expect("an unimplemented libc states why");
251 assert!(reason.contains("Lamina C library"), "{reason}");
252 assert!(reason.contains("not implemented"), "{reason}");
253 }
254
255 #[test]
256 fn an_unimplemented_libc_is_still_a_hosted_environment() {
257 for env in TargetEnvironment::ALL {
258 let hosted = env.libc().is_some();
259 assert_eq!(env.is_hosted(), hosted, "{env}");
260 }
261 }
262
263 #[test]
264 fn only_eabi_and_freestanding_are_soft_float() {
265 assert!(!TargetEnvironment::Eabi.hardware_float());
266 assert!(!TargetEnvironment::Freestanding.hardware_float());
267 assert!(TargetEnvironment::Eabihf.hardware_float());
268 assert!(TargetEnvironment::Gnu.hardware_float());
269 assert!(TargetEnvironment::Musl.hardware_float());
270 }
271}