1extern crate self as include_zstd;
2
3pub use include_zstd_derive::{bytes, file_bytes, file_str, include_zstd, r#str};
4use std::sync::OnceLock;
5use std::time::SystemTime;
6
7pub struct ZstdMetadata {
9 pub len: u64,
10 pub modified: Option<SystemTime>,
11 pub accessed: Option<SystemTime>,
12 pub created: Option<SystemTime>,
13 pub is_file: bool,
14 pub is_dir: bool,
15}
16
17pub struct ZstdAsset {
19 metadata: ZstdMetadata,
20 compressed: &'static [u8],
21 cache: OnceLock<Box<[u8]>>,
22}
23
24impl ZstdAsset {
25 pub fn metadata(&self) -> &ZstdMetadata {
27 &self.metadata
28 }
29
30 pub fn bytes(&self) -> &[u8] {
32 self.cache
33 .get_or_init(|| __private::decompress_bytes(self.compressed))
34 .as_ref()
35 }
36}
37
38#[doc(hidden)]
39pub mod __private {
40 pub fn decode_utf8(bytes: &'static [u8]) -> &'static str {
41 std::str::from_utf8(bytes).unwrap_or_else(|err| {
42 panic!("include_zstd::str!/file_str! decoded data is not UTF-8: {err}")
43 })
44 }
45
46 pub fn decompress_bytes(compressed: &[u8]) -> Box<[u8]> {
47 zstd::stream::decode_all(compressed)
48 .unwrap_or_else(|err| panic!("include_zstd decode failed: {err}"))
49 .into_boxed_slice()
50 }
51
52 pub fn create_zstd_asset(
53 metadata: crate::ZstdMetadata,
54 compressed: &'static [u8],
55 ) -> crate::ZstdAsset {
56 crate::ZstdAsset {
57 metadata,
58 compressed,
59 cache: std::sync::OnceLock::new(),
60 }
61 }
62}
63
64#[cfg(test)]
65mod tests {
66 use crate::ZstdAsset;
67
68 fn include_str_fixture() -> &'static str {
69 crate::str!("hello include-zstd")
70 }
71
72 fn include_bytes_fixture() -> &'static [u8] {
73 crate::bytes!(b"\x00\x01\x02\x03")
74 }
75
76 fn include_file_str_fixture() -> &'static str {
77 crate::file_str!("../Cargo.toml")
78 }
79
80 fn include_file_fixture() -> &'static [u8] {
81 crate::file_bytes!("../Cargo.toml")
82 }
83
84 fn include_zstd_fixture() -> ZstdAsset {
85 crate::include_zstd!("../Cargo.toml")
87 }
88
89 #[test]
90 fn str_matches_original_text() {
91 let expected: &'static str = "hello include-zstd";
92 let actual: &'static str = include_str_fixture();
93
94 assert_eq!(actual, expected);
95 }
96
97 #[test]
98 fn binary_matches_original_bytes() {
99 let expected: &'static [u8] = b"\x00\x01\x02\x03";
100 let actual: &'static [u8] = include_bytes_fixture();
101
102 assert_eq!(actual, expected);
103 }
104
105 #[test]
106 fn file_str_matches_include_str() {
107 let expected: &'static str = include_str!("../Cargo.toml");
108 let actual: &'static str = include_file_str_fixture();
109
110 assert_eq!(actual, expected);
111 }
112
113 #[test]
114 fn file_matches_include_bytes() {
115 let expected: &'static [u8] = include_bytes!("../Cargo.toml");
116 let actual: &'static [u8] = include_file_fixture();
117
118 assert_eq!(actual, expected);
119 }
120
121 #[test]
122 fn macros_use_once_lock_for_each_callsite() {
123 let first = include_str_fixture().as_ptr();
124 let second = include_str_fixture().as_ptr();
125 assert_eq!(first, second);
126
127 let first = include_bytes_fixture().as_ptr();
128 let second = include_bytes_fixture().as_ptr();
129 assert_eq!(first, second);
130
131 let first = include_file_str_fixture().as_ptr();
132 let second = include_file_str_fixture().as_ptr();
133 assert_eq!(first, second);
134
135 let first = include_file_fixture().as_ptr();
136 let second = include_file_fixture().as_ptr();
137 assert_eq!(first, second);
138 }
139
140 #[test]
141 fn zstd_asset_bytes_matches_original() {
142 let asset = include_zstd_fixture();
143 let expected = include_bytes!("../Cargo.toml");
144 assert_eq!(asset.bytes(), expected);
145 }
146
147 #[test]
148 fn zstd_asset_metadata_len_matches() {
149 let asset = include_zstd_fixture();
150 let expected_len =
152 std::fs::metadata(std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("Cargo.toml"))
153 .unwrap()
154 .len();
155 assert_eq!(asset.metadata().len, expected_len);
156 }
157
158 #[test]
159 fn zstd_asset_bytes_cached() {
160 let asset = include_zstd_fixture();
161 let first = asset.bytes().as_ptr();
162 let second = asset.bytes().as_ptr();
163 assert_eq!(first, second);
164 }
165
166 #[test]
167 fn zstd_asset_metadata_is_file() {
168 let asset = include_zstd_fixture();
169 assert!(asset.metadata().is_file);
170 assert!(!asset.metadata().is_dir);
171 }
172}