1use std::path::PathBuf;
4
5#[derive(Debug, Clone)]
22pub struct DiscoveredFile {
23 pub id: FileId,
25 pub path: PathBuf,
27 pub size_bytes: u64,
29}
30
31#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
49pub struct FileId(pub u32);
50
51const _: () = assert!(std::mem::size_of::<FileId>() == 4);
55#[cfg(all(target_pointer_width = "64", unix))]
56const _: () = assert!(std::mem::size_of::<DiscoveredFile>() == 40);
57
58#[derive(Debug, Clone)]
60pub struct EntryPoint {
61 pub path: PathBuf,
63 pub source: EntryPointSource,
65}
66
67impl std::fmt::Display for EntryPointSource {
68 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
69 match self {
70 Self::PackageJsonMain => f.write_str("package.json main"),
71 Self::PackageJsonModule => f.write_str("package.json module"),
72 Self::PackageJsonExports => f.write_str("package.json exports"),
73 Self::PackageJsonBin => f.write_str("package.json bin"),
74 Self::PackageJsonScript => f.write_str("package.json script"),
75 Self::Plugin { name } => write!(f, "{name}"),
76 Self::TestFile => f.write_str("test file"),
77 Self::DefaultIndex => f.write_str("default index"),
78 Self::ManualEntry => f.write_str("manual entry"),
79 Self::InfrastructureConfig => f.write_str("infrastructure config"),
80 Self::DynamicallyLoaded => f.write_str("dynamically loaded"),
81 }
82 }
83}
84
85#[derive(Debug, Clone)]
87pub enum EntryPointSource {
88 PackageJsonMain,
90 PackageJsonModule,
92 PackageJsonExports,
94 PackageJsonBin,
96 PackageJsonScript,
98 Plugin {
100 name: String,
102 },
103 TestFile,
105 DefaultIndex,
107 ManualEntry,
109 InfrastructureConfig,
111 DynamicallyLoaded,
113}
114
115#[cfg(test)]
116mod tests {
117 use super::*;
118 use std::collections::hash_map::DefaultHasher;
119 use std::hash::{Hash, Hasher};
120
121 #[test]
124 fn file_id_equality() {
125 assert_eq!(FileId(0), FileId(0));
126 assert_eq!(FileId(42), FileId(42));
127 assert_ne!(FileId(0), FileId(1));
128 }
129
130 #[test]
131 fn file_id_copy_semantics() {
132 let a = FileId(5);
133 let b = a; assert_eq!(a, b);
135 }
136
137 #[test]
138 fn file_id_hash_consistent() {
139 let id = FileId(99);
140 let hash1 = {
141 let mut h = DefaultHasher::new();
142 id.hash(&mut h);
143 h.finish()
144 };
145 let hash2 = {
146 let mut h = DefaultHasher::new();
147 id.hash(&mut h);
148 h.finish()
149 };
150 assert_eq!(hash1, hash2);
151 }
152
153 #[test]
154 fn file_id_equal_values_same_hash() {
155 let a = FileId(7);
156 let b = FileId(7);
157 let hash_a = {
158 let mut h = DefaultHasher::new();
159 a.hash(&mut h);
160 h.finish()
161 };
162 let hash_b = {
163 let mut h = DefaultHasher::new();
164 b.hash(&mut h);
165 h.finish()
166 };
167 assert_eq!(hash_a, hash_b);
168 }
169
170 #[test]
171 fn file_id_inner_value_accessible() {
172 let id = FileId(123);
173 assert_eq!(id.0, 123);
174 }
175
176 #[test]
177 fn file_id_debug_format() {
178 let id = FileId(42);
179 let debug = format!("{id:?}");
180 assert!(
181 debug.contains("42"),
182 "Debug should show inner value: {debug}"
183 );
184 }
185
186 #[test]
189 fn discovered_file_clone() {
190 let original = DiscoveredFile {
191 id: FileId(0),
192 path: PathBuf::from("/project/src/index.ts"),
193 size_bytes: 1024,
194 };
195 let cloned = original.clone();
196 assert_eq!(cloned.id, original.id);
197 assert_eq!(cloned.path, original.path);
198 assert_eq!(cloned.size_bytes, original.size_bytes);
199 }
200
201 #[test]
202 fn discovered_file_zero_size() {
203 let file = DiscoveredFile {
204 id: FileId(0),
205 path: PathBuf::from("/empty.ts"),
206 size_bytes: 0,
207 };
208 assert_eq!(file.size_bytes, 0);
209 }
210
211 #[test]
212 fn discovered_file_large_size() {
213 let file = DiscoveredFile {
214 id: FileId(0),
215 path: PathBuf::from("/large.ts"),
216 size_bytes: u64::MAX,
217 };
218 assert_eq!(file.size_bytes, u64::MAX);
219 }
220
221 #[test]
224 fn entry_point_clone() {
225 let ep = EntryPoint {
226 path: PathBuf::from("/project/src/main.ts"),
227 source: EntryPointSource::PackageJsonMain,
228 };
229 let cloned = ep.clone();
230 assert_eq!(cloned.path, ep.path);
231 assert!(matches!(cloned.source, EntryPointSource::PackageJsonMain));
232 }
233
234 #[test]
237 fn entry_point_source_all_variants_constructible() {
238 let _ = EntryPointSource::PackageJsonMain;
240 let _ = EntryPointSource::PackageJsonModule;
241 let _ = EntryPointSource::PackageJsonExports;
242 let _ = EntryPointSource::PackageJsonBin;
243 let _ = EntryPointSource::PackageJsonScript;
244 let _ = EntryPointSource::Plugin {
245 name: "next".to_string(),
246 };
247 let _ = EntryPointSource::TestFile;
248 let _ = EntryPointSource::DefaultIndex;
249 let _ = EntryPointSource::ManualEntry;
250 let _ = EntryPointSource::InfrastructureConfig;
251 let _ = EntryPointSource::DynamicallyLoaded;
252 }
253
254 #[test]
255 fn entry_point_source_plugin_preserves_name() {
256 let source = EntryPointSource::Plugin {
257 name: "vitest".to_string(),
258 };
259 match source {
260 EntryPointSource::Plugin { name } => assert_eq!(name, "vitest"),
261 _ => panic!("expected Plugin variant"),
262 }
263 }
264
265 #[test]
266 fn entry_point_source_plugin_clone_preserves_name() {
267 let source = EntryPointSource::Plugin {
268 name: "storybook".to_string(),
269 };
270 let cloned = source.clone();
272 assert!(matches!(&source, EntryPointSource::Plugin { name } if name == "storybook"));
274 match cloned {
276 EntryPointSource::Plugin { name } => assert_eq!(name, "storybook"),
277 _ => panic!("expected Plugin variant after clone"),
278 }
279 }
280
281 #[test]
282 fn entry_point_source_debug_format() {
283 let source = EntryPointSource::PackageJsonMain;
284 let debug = format!("{source:?}");
285 assert!(
286 debug.contains("PackageJsonMain"),
287 "Debug should name the variant: {debug}"
288 );
289
290 let plugin = EntryPointSource::Plugin {
291 name: "remix".to_string(),
292 };
293 let debug = format!("{plugin:?}");
294 assert!(
295 debug.contains("remix"),
296 "Debug should show plugin name: {debug}"
297 );
298 }
299
300 #[test]
301 fn entry_point_source_display_all_variants() {
302 assert_eq!(
303 EntryPointSource::PackageJsonMain.to_string(),
304 "package.json main"
305 );
306 assert_eq!(
307 EntryPointSource::PackageJsonModule.to_string(),
308 "package.json module"
309 );
310 assert_eq!(
311 EntryPointSource::PackageJsonExports.to_string(),
312 "package.json exports"
313 );
314 assert_eq!(
315 EntryPointSource::PackageJsonBin.to_string(),
316 "package.json bin"
317 );
318 assert_eq!(
319 EntryPointSource::PackageJsonScript.to_string(),
320 "package.json script"
321 );
322 assert_eq!(
323 EntryPointSource::Plugin {
324 name: "vitest".to_string()
325 }
326 .to_string(),
327 "vitest"
328 );
329 assert_eq!(EntryPointSource::TestFile.to_string(), "test file");
330 assert_eq!(EntryPointSource::DefaultIndex.to_string(), "default index");
331 assert_eq!(EntryPointSource::ManualEntry.to_string(), "manual entry");
332 assert_eq!(
333 EntryPointSource::InfrastructureConfig.to_string(),
334 "infrastructure config"
335 );
336 assert_eq!(
337 EntryPointSource::DynamicallyLoaded.to_string(),
338 "dynamically loaded"
339 );
340 }
341}