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