1use sofie_core::{Error, Result};
2
3#[derive(Clone, Copy, Debug, PartialEq, Eq)]
5pub struct File {
6 pub path: &'static str,
7 pub bytes: &'static [u8],
8}
9
10impl File {
11 #[must_use]
12 pub const fn new(path: &'static str, bytes: &'static [u8]) -> Self {
13 Self { path, bytes }
14 }
15
16 #[must_use]
17 pub fn name(&self) -> &'static str {
18 self.path.rsplit('/').next().unwrap_or(self.path)
19 }
20
21 #[must_use]
22 pub fn stem(&self) -> &'static str {
23 let name = self.name();
24 name.split_once('.').map_or(name, |(stem, _)| stem)
25 }
26
27 pub fn text(&self) -> Result<&'static str> {
28 std::str::from_utf8(self.bytes)
29 .map_err(|error| Error::internal(format!("reading {} as text", self.path), error))
30 }
31}
32
33#[macro_export]
37macro_rules! embed {
38 ($folder:literal, [$($name:literal),+ $(,)?]) => {
39 &[$($crate::embed!(concat!($folder, "/", $name))),+]
40 };
41 ($path:expr, compiled = $compiled:expr) => {
42 $crate::File {
43 path: concat!(env!("CARGO_MANIFEST_DIR"), "/", $path),
44 bytes: include_bytes!(concat!(env!("OUT_DIR"), "/", $compiled)),
45 }
46 };
47 ($path:expr) => {
48 $crate::File {
49 path: concat!(env!("CARGO_MANIFEST_DIR"), "/", $path),
50 bytes: include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/", $path)),
51 }
52 };
53}
54
55#[cfg(test)]
56mod tests {
57 use super::File;
58
59 #[test]
60 fn a_file_is_named_by_the_last_part_of_its_path() {
61 let file = File::new("/app/modules/order/lang/de.toml", b"");
62
63 assert_eq!(file.name(), "de.toml");
64 assert_eq!(file.stem(), "de");
65 assert_eq!(File::new("0001_a.sql", b"").name(), "0001_a.sql");
66 }
67
68 #[test]
69 fn embedding_keeps_the_path_a_development_build_reads() {
70 let file = crate::embed!("Cargo.toml");
71
72 assert!(file.path.ends_with("/sofie-db/Cargo.toml"), "{}", file.path);
73 assert_eq!(
74 file.text().unwrap(),
75 std::fs::read_to_string(file.path).unwrap()
76 );
77 }
78
79 #[test]
80 fn text_that_is_not_utf8_names_its_file() {
81 let error = File::new("0001_a.sql", b"\xff").text().unwrap_err();
82
83 assert!(
84 error.to_string().starts_with("reading 0001_a.sql as text"),
85 "{error}"
86 );
87 }
88}