|
| 1 | +# ADR 006: File metadata scope and cost tiers |
| 2 | + |
| 3 | +## Status |
| 4 | + |
| 5 | +Accepted |
| 6 | + |
| 7 | +## Context |
| 8 | + |
| 9 | +When displaying files in the explorer, we can retrieve various metadata. macOS provides extensive file information, but |
| 10 | +each piece has different performance characteristics. With lists of 50k+ files, we must be deliberate about what to |
| 11 | +fetch eagerly vs. on-demand. |
| 12 | + |
| 13 | +## Decision |
| 14 | + |
| 15 | +We will categorize metadata into tiers by cost and load accordingly: |
| 16 | + |
| 17 | +### Tier 1: Free (from single `stat()` call, already performed) |
| 18 | + |
| 19 | +| Field | Source | Notes | |
| 20 | +| ------------- | ------------------------------------ | -------------- | |
| 21 | +| Name | `DirEntry::file_name()` | Already have | |
| 22 | +| Size | `metadata.len()` | Already have | |
| 23 | +| Is directory | `metadata.is_dir()` | Already have | |
| 24 | +| Modified date | `metadata.modified()` | Already have | |
| 25 | +| Created date | `MetadataExt::st_birthtime()` | Same syscall | |
| 26 | +| Permissions | `metadata.permissions().mode()` | Unix mode bits | |
| 27 | +| Owner uid/gid | `MetadataExt::st_uid()` / `st_gid()` | Same syscall | |
| 28 | +| Is symlink | `metadata.is_symlink()` | Same syscall | |
| 29 | + |
| 30 | +### Tier 2: Cheap (extra syscall, cacheable) |
| 31 | + |
| 32 | +| Field | How to get | Cost | |
| 33 | +| -------------- | --------------------------------- | --------------- | |
| 34 | +| Owner name | `users` crate to resolve uid→name | ~1μs, cacheable | |
| 35 | +| Symlink target | `std::fs::read_link()` | ~1μs if symlink | |
| 36 | + |
| 37 | +### Tier 3: macOS-specific (requires Objective-C APIs) |
| 38 | + |
| 39 | +| Field | API | Cost | |
| 40 | +| ------------------- | ------------------------------------------------- | ------------------- | |
| 41 | +| Added date | Spotlight / `NSURL resourceValuesForKeys:` | ~50-100μs/file | |
| 42 | +| Last opened date | Spotlight / NSURL | Same | |
| 43 | +| Locked flag | `NSURL` with `NSURLIsUserImmutableKey` | ~50μs | |
| 44 | +| Stationery pad flag | `NSURL` with `NSURLStationeryKey` | Same | |
| 45 | +| Kind (localized) | `NSURL.localizedTypeDescription` | Requires macOS APIs | |
| 46 | +| Cloud sync status | xattrs like `com.apple.icloud.itemDownloadStatus` | ~10μs | |
| 47 | + |
| 48 | +### Tier 4: Extended/content-based |
| 49 | + |
| 50 | +| Category | How to get | Cost | |
| 51 | +| -------------------- | ------------------------------ | -------------------- | |
| 52 | +| EXIF/media metadata | `kamadak-exif`, `image` crates | 1-50ms+ (reads file) | |
| 53 | +| PDF metadata | `lopdf` crate | 10-100ms+ | |
| 54 | +| Audio/video metadata | `lofty` crate | 10-100ms+ | |
| 55 | + |
| 56 | +## Chosen scope for initial implementation |
| 57 | + |
| 58 | +**Include in list view (Tier 1-2)**: |
| 59 | + |
| 60 | +- All Tier 1 fields (zero extra cost) |
| 61 | +- Owner name (cached uid→name resolution) |
| 62 | + |
| 63 | +**Defer (Tier 3-4)**: |
| 64 | + |
| 65 | +- Added/opened dates (Spotlight-dependent, unreliable) |
| 66 | +- Locked/Stationery flags (rarely used) |
| 67 | +- Kind (can derive from extension on frontend) |
| 68 | +- EXIF and media metadata (on-demand only) |
| 69 | + |
| 70 | +**Future work**: |
| 71 | + |
| 72 | +- Cloud sync status (iCloud, Dropbox, GDrive) - valuable, requires xattr reads |
| 73 | + |
| 74 | +## Consequences |
| 75 | + |
| 76 | +### Positive |
| 77 | + |
| 78 | +- Zero performance regression for current functionality |
| 79 | +- Created/permissions/owner cost nothing extra |
| 80 | +- Clear path for adding macOS-specific metadata later |
| 81 | + |
| 82 | +### Negative |
| 83 | + |
| 84 | +- Some Finder-equivalent fields not immediately available |
| 85 | +- macOS-specific features require platform-gated code |
0 commit comments