@@ -612,28 +612,56 @@ public class Enumerator: NSObject, NSFileProviderEnumerator {
612612 remoteInterface: RemoteInterface ,
613613 dbManager: FilesDatabaseManager ,
614614 numPage: Int ,
615- itemMetadatas: [ SendableItemMetadata ]
615+ itemMetadatas: [ SendableItemMetadata ] ,
616+ handleInvalidParent: Bool = true
616617 ) {
617618 Task {
618- let items = await itemMetadatas. toFileProviderItems (
619- account: account, remoteInterface: remoteInterface, dbManager: dbManager
620- )
619+ do {
620+ let items = try await itemMetadatas. toFileProviderItems (
621+ account: account, remoteInterface: remoteInterface, dbManager: dbManager
622+ )
621623
622- Task { @MainActor in
623- observer. didEnumerate ( items)
624- Self . logger. info ( " Did enumerate \( items. count) items " )
625-
626- // TODO: Handle paging properly
627- /*
628- if items.count == maxItemsPerFileProviderPage {
629- let nextPage = numPage + 1
630- let providerPage = NSFileProviderPage("\(nextPage)".data(using: .utf8)!)
631- observer.finishEnumerating(upTo: providerPage)
632- } else {
633- observer.finishEnumerating(upTo: nil)
634- }
635- */
636- observer. finishEnumerating ( upTo: fileProviderPageforNumPage ( numPage) )
624+ Task { @MainActor in
625+ observer. didEnumerate ( items)
626+ Self . logger. info ( " Did enumerate \( items. count) items " )
627+
628+ // TODO: Handle paging properly
629+ /*
630+ if items.count == maxItemsPerFileProviderPage {
631+ let nextPage = numPage + 1
632+ let providerPage = NSFileProviderPage("\(nextPage)".data(using: .utf8)!)
633+ observer.finishEnumerating(upTo: providerPage)
634+ } else {
635+ observer.finishEnumerating(upTo: nil)
636+ }
637+ */
638+ observer. finishEnumerating ( upTo: fileProviderPageforNumPage ( numPage) )
639+ }
640+ } catch let error as NSError { // This error can only mean a missing parent item identifier
641+ guard handleInvalidParent else {
642+ Self . logger. info ( " Not handling invalid parent in enumeration " )
643+ observer. finishEnumeratingWithError ( error)
644+ return
645+ }
646+ do {
647+ let metadata = try await Self . attemptInvalidParentRecovery (
648+ error: error,
649+ account: account,
650+ remoteInterface: remoteInterface,
651+ dbManager: dbManager
652+ )
653+ Self . completeEnumerationObserver (
654+ observer,
655+ account: account,
656+ remoteInterface: remoteInterface,
657+ dbManager: dbManager,
658+ numPage: numPage,
659+ itemMetadatas: [ metadata] + itemMetadatas,
660+ handleInvalidParent: false
661+ )
662+ } catch let error {
663+ observer. finishEnumeratingWithError ( error)
664+ }
637665 }
638666 }
639667 }
@@ -647,7 +675,8 @@ public class Enumerator: NSObject, NSFileProviderEnumerator {
647675 dbManager: FilesDatabaseManager ,
648676 newMetadatas: [ SendableItemMetadata ] ? ,
649677 updatedMetadatas: [ SendableItemMetadata ] ? ,
650- deletedMetadatas: [ SendableItemMetadata ] ?
678+ deletedMetadatas: [ SendableItemMetadata ] ? ,
679+ handleInvalidParent: Bool = true
651680 ) {
652681 guard newMetadatas != nil || updatedMetadatas != nil || deletedMetadatas != nil else {
653682 Self . logger. error (
@@ -687,23 +716,95 @@ public class Enumerator: NSObject, NSFileProviderEnumerator {
687716 }
688717
689718 Task { [ allUpdatedMetadatas, allDeletedMetadatas] in
690- let updatedItems = await allUpdatedMetadatas. toFileProviderItems (
691- account: account, remoteInterface: remoteInterface, dbManager: dbManager
692- )
719+ do {
720+ let updatedItems = try await allUpdatedMetadatas. toFileProviderItems (
721+ account: account, remoteInterface: remoteInterface, dbManager: dbManager
722+ )
693723
694- Task { @MainActor in
695- if !updatedItems. isEmpty {
696- observer. didUpdate ( updatedItems)
697- }
724+ Task { @MainActor in
725+ if !updatedItems. isEmpty {
726+ observer. didUpdate ( updatedItems)
727+ }
698728
699- Self . logger. info (
729+ Self . logger. info (
700730 """
701731 Processed \( updatedItems. count) new or updated metadatas.
702- \( allDeletedMetadatas. count) deleted metadatas.
732+ \( allDeletedMetadatas. count) deleted metadatas.
703733 """
704- )
705- observer. finishEnumeratingChanges ( upTo: anchor, moreComing: false )
734+ )
735+ observer. finishEnumeratingChanges ( upTo: anchor, moreComing: false )
736+ }
737+ } catch let error as NSError { // This error can only mean a missing parent item identifier
738+ guard handleInvalidParent else {
739+ Self . logger. info ( " Not handling invalid parent in change enumeration " )
740+ observer. finishEnumeratingWithError ( error)
741+ return
742+ }
743+ do {
744+ let metadata = try await Self . attemptInvalidParentRecovery (
745+ error: error,
746+ account: account,
747+ remoteInterface: remoteInterface,
748+ dbManager: dbManager
749+ )
750+ var modifiedNewMetadatas = newMetadatas
751+ modifiedNewMetadatas? . append ( metadata)
752+ Self . completeChangesObserver (
753+ observer,
754+ anchor: anchor,
755+ enumeratedItemIdentifier: enumeratedItemIdentifier,
756+ account: account,
757+ remoteInterface: remoteInterface,
758+ dbManager: dbManager,
759+ newMetadatas: modifiedNewMetadatas,
760+ updatedMetadatas: updatedMetadatas,
761+ deletedMetadatas: deletedMetadatas,
762+ handleInvalidParent: false
763+ )
764+ } catch let error {
765+ observer. finishEnumeratingWithError ( error)
766+ }
706767 }
707768 }
708769 }
770+
771+ private static func attemptInvalidParentRecovery(
772+ error: NSError ,
773+ account: Account ,
774+ remoteInterface: RemoteInterface ,
775+ dbManager: FilesDatabaseManager
776+ ) async throws -> SendableItemMetadata {
777+ Self . logger. info ( " Attempting recovery from invalid parent identifier. " )
778+ // Try to recover from errors involving missing metadata for a parent
779+ let userInfoKey =
780+ FilesDatabaseManager . ErrorUserInfoKey. missingParentServerUrlAndFileName. rawValue
781+ guard let urlToEnumerate = ( error as NSError ) . userInfo [ userInfoKey] as? String else {
782+ Self . logger. fault ( " No missing parent server url and filename in error user info. " )
783+ assert ( false )
784+ throw NSError ( )
785+ }
786+
787+ Self . logger. info (
788+ " Recovering from invalid parent identifier at \( urlToEnumerate, privacy: . public) "
789+ )
790+ let ( metadatas, _, _, _, error) = await Enumerator . readServerUrl (
791+ urlToEnumerate,
792+ account: account,
793+ remoteInterface: remoteInterface,
794+ dbManager: dbManager,
795+ depth: . target
796+ )
797+ guard error == nil || error == . success, let metadata = metadatas? . first else {
798+ Self . logger. error (
799+ """
800+ Problem retrieving parent for metadata.
801+ Error: \( error? . errorDescription ?? " NONE " , privacy: . public)
802+ Metadatas: \( metadatas? . count ?? - 1 , privacy: . public)
803+ """
804+ )
805+ throw error? . fileProviderError ?? NSFileProviderError ( . cannotSynchronize)
806+ }
807+ // Provide it to the caller method so it can ingest it into the database and fix future errs
808+ return metadata
809+ }
709810}
0 commit comments