@@ -20,8 +20,11 @@ final class ManifestTracker: Sendable {
2020 /// Default asset type for all recorded entries.
2121 let defaultAssetType : String
2222
23+ private let workingDirectory : String
24+
2325 init ( assetType: String ) {
2426 defaultAssetType = assetType
27+ workingDirectory = FileManager . default. currentDirectoryPath
2528 }
2629
2730 /// Pre-write filesystem state for a file path.
@@ -35,13 +38,11 @@ final class ManifestTracker: Sendable {
3538 /// Must be called BEFORE the file is written to disk, so that existing content
3639 /// can be compared for action detection (created vs. modified vs. unchanged).
3740 func capturePreState( for path: String ) -> PreWriteState {
38- let fileExisted = FileManager . default. fileExists ( atPath: path)
39- let existingChecksum : String ? = if fileExisted, let existingData = FileManager . default. contents ( atPath: path) {
40- FNV1aHasher . hashToHex ( existingData)
41+ if let existingData = FileManager . default. contents ( atPath: path) {
42+ PreWriteState ( fileExisted: true , existingChecksum: FNV1aHasher . hashToHex ( existingData) )
4143 } else {
42- nil
44+ PreWriteState ( fileExisted : false , existingChecksum : nil )
4345 }
44- return PreWriteState ( fileExisted: fileExisted, existingChecksum: existingChecksum)
4546 }
4647
4748 /// Record a file write operation after successful write.
@@ -55,14 +56,7 @@ final class ManifestTracker: Sendable {
5556 let assetType = assetType ?? defaultAssetType
5657 let relativePath = makeRelativePath ( path)
5758 let newChecksum = FNV1aHasher . hashToHex ( data)
58-
59- let action : FileAction = if !preState. fileExisted {
60- . created
61- } else if let existingChecksum = preState. existingChecksum {
62- existingChecksum == newChecksum ? . unchanged : . modified
63- } else {
64- . modified
65- }
59+ let action = determineAction ( preState: preState, newChecksum: newChecksum)
6660
6761 entries. withLock {
6862 $0. append ( ManifestEntry (
@@ -94,14 +88,12 @@ final class ManifestTracker: Sendable {
9488 nil
9589 }
9690
97- let action : FileAction = if !preState. fileExisted {
98- . created
99- } else if let existingChecksum = preState. existingChecksum, let newChecksum {
100- existingChecksum == newChecksum ? . unchanged : . modified
101- } else {
102- . modified
91+ if newChecksum == nil {
92+ WarningCollectorStorage . current? . add ( " Manifest: could not compute checksum for \( relativePath) " )
10393 }
10494
95+ let action = determineAction ( preState: preState, newChecksum: newChecksum)
96+
10597 entries. withLock {
10698 $0. append ( ManifestEntry (
10799 path: relativePath,
@@ -125,30 +117,46 @@ final class ManifestTracker: Sendable {
125117 var allEntries = entries. withLock { $0 }
126118
127119 if let previousPath = previousReportPath,
128- let previousData = FileManager . default. contents ( atPath: previousPath) ,
129- let previousReport = try ? JSONCodec . decode ( PreviousReportManifest . self, from: previousData)
120+ let previousData = FileManager . default. contents ( atPath: previousPath)
130121 {
131- let currentPaths = Set ( allEntries. map ( \. path) )
132- for previousEntry in previousReport. manifest? . files ?? [ ]
133- where !currentPaths. contains ( previousEntry. path)
134- {
135- allEntries. append ( ManifestEntry (
136- path: previousEntry. path,
137- action: . deleted,
138- checksum: nil ,
139- assetType: previousEntry. assetType
140- ) )
122+ do {
123+ let previousReport = try JSONCodec . decode ( PreviousReportManifest . self, from: previousData)
124+ let currentPaths = Set ( allEntries. map ( \. path) )
125+ for previousEntry in previousReport. manifest? . files ?? [ ]
126+ where !currentPaths. contains ( previousEntry. path)
127+ {
128+ allEntries. append ( ManifestEntry (
129+ path: previousEntry. path,
130+ action: . deleted,
131+ checksum: nil ,
132+ assetType: previousEntry. assetType
133+ ) )
134+ }
135+ } catch {
136+ let message = " Could not read previous report at \( previousPath) : "
137+ + " \( error. localizedDescription) . Deleted file detection skipped. "
138+ WarningCollectorStorage . current? . add ( message)
141139 }
142140 }
143141
144142 return AssetManifest ( files: allEntries)
145143 }
146144
147- /// Make path relative to current working directory.
145+ /// Determine file action based on pre-write state and new checksum.
146+ private func determineAction( preState: PreWriteState , newChecksum: String ? ) -> FileAction {
147+ if !preState. fileExisted {
148+ . created
149+ } else if let existingChecksum = preState. existingChecksum, let newChecksum {
150+ existingChecksum == newChecksum ? . unchanged : . modified
151+ } else {
152+ . modified
153+ }
154+ }
155+
156+ /// Make path relative to working directory captured at init time.
148157 private func makeRelativePath( _ absolutePath: String ) -> String {
149- let cwd = FileManager . default. currentDirectoryPath
150- if absolutePath. hasPrefix ( cwd + " / " ) {
151- return String ( absolutePath. dropFirst ( cwd. count + 1 ) )
158+ if absolutePath. hasPrefix ( workingDirectory + " / " ) {
159+ return String ( absolutePath. dropFirst ( workingDirectory. count + 1 ) )
152160 }
153161 return absolutePath
154162 }
0 commit comments