chromium/ios/chrome/app/spotlight/spotlight_interface.mm

// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#import "ios/chrome/app/spotlight/spotlight_interface.h"

#import "base/check.h"
#import "ios/chrome/app/spotlight/spotlight_logger.h"
#import "ios/chrome/app/spotlight/spotlight_util.h"
#import "ios/chrome/browser/shared/public/features/features.h"

@interface SpotlightInterface ()

// Maximum retry attempts to delete/index a set of items.
@property(nonatomic, assign) NSUInteger maxAttempts;

@end

@implementation SpotlightInterface
@synthesize searchableIndex = _searchableIndex;

+ (SpotlightInterface*)defaultInterface {
  static SpotlightInterface* const kDefaultSpotlightInterface =
      [[SpotlightInterface alloc]
          initWithSearchableIndex:(base::FeatureList::IsEnabled(
                                       kSpotlightNeverRetainIndex)
                                       ? nil
                                       : [CSSearchableIndex
                                             defaultSearchableIndex])
                      maxAttempts:spotlight::kMaxAttempts - 1];
  return kDefaultSpotlightInterface;
}

// Execute blockToRetry with up to retryCount retries on error. Execute
// callback when done.
+ (void)doWithRetry:(void (^)(BlockWithError error))blockToRetry
           retryCount:(NSUInteger)retryCount
    completionHandler:(BlockWithError)completionHandler {
  DCHECK(completionHandler);

  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
                 ^{
                   blockToRetry(^(NSError* error) {
                     if (error && retryCount > 0) {
                       [SpotlightInterface doWithRetry:blockToRetry
                                            retryCount:retryCount - 1
                                     completionHandler:completionHandler];
                     } else {
                       dispatch_async(dispatch_get_main_queue(), ^{
                         completionHandler(error);
                       });
                     }
                   });
                 });
}

- (instancetype)initWithSearchableIndex:(CSSearchableIndex*)searchableIndex
                            maxAttempts:(NSUInteger)maxAttempts {
  self = [super init];
  if (self) {
    _searchableIndex = searchableIndex;
    _maxAttempts = maxAttempts;
  }
  return self;
}

- (CSSearchableIndex*)searchableIndex {
  if (_searchableIndex) {
    return _searchableIndex;
  }
  return [CSSearchableIndex defaultSearchableIndex];
}

- (void)indexSearchableItems:(NSArray<CSSearchableItem*>*)items {
  __weak SpotlightInterface* weakSelf = self;

  void (^addItems)(BlockWithError) = ^(BlockWithError errorBlock) {
    [weakSelf.searchableIndex indexSearchableItems:items
                                 completionHandler:errorBlock];
  };

  [SpotlightInterface doWithRetry:addItems
                       retryCount:self.maxAttempts
                completionHandler:^(NSError* error) {
                  [SpotlightLogger logSpotlightError:error];
                }];
}

- (void)deleteSearchableItemsWithIdentifiers:(NSArray<NSString*>*)identifiers
                           completionHandler:(BlockWithError)completionHandler {
  __weak SpotlightInterface* weakSelf = self;

  BlockWithError augmentedCallback = ^(NSError* error) {
    [SpotlightLogger logSpotlightError:error];

    if (completionHandler) {
      completionHandler(error);
    }
  };

  void (^deleteItems)(BlockWithError) = ^(BlockWithError errorBlock) {
    [weakSelf.searchableIndex deleteSearchableItemsWithIdentifiers:identifiers
                                                 completionHandler:errorBlock];
  };

  [SpotlightInterface doWithRetry:deleteItems
                       retryCount:self.maxAttempts
                completionHandler:augmentedCallback];
}

- (void)deleteSearchableItemsWithDomainIdentifiers:
            (NSArray<NSString*>*)domainIdentifiers
                                 completionHandler:
                                     (BlockWithError)completionHandler {
  __weak SpotlightInterface* weakSelf = self;

  BlockWithError augmentedCallback = ^(NSError* error) {
    [SpotlightLogger logSpotlightError:error];

    if (completionHandler) {
      completionHandler(error);
    }
  };

  void (^deleteItems)(BlockWithError) = ^(BlockWithError errorBlock) {
    [weakSelf.searchableIndex
        deleteSearchableItemsWithDomainIdentifiers:domainIdentifiers
                                 completionHandler:errorBlock];
  };

  [SpotlightInterface doWithRetry:deleteItems
                       retryCount:self.maxAttempts
                completionHandler:augmentedCallback];
}

- (void)deleteAllSearchableItemsWithCompletionHandler:
    (BlockWithError)completionHandler {
  __weak SpotlightInterface* weakSelf = self;

  BlockWithError augmentedCallback = ^(NSError* error) {
    [SpotlightLogger logSpotlightError:error];

    if (completionHandler) {
      completionHandler(error);
    }
  };

  void (^deleteItems)(BlockWithError) = ^(BlockWithError errorBlock) {
    [weakSelf.searchableIndex
        deleteAllSearchableItemsWithCompletionHandler:errorBlock];
  };

  [SpotlightInterface doWithRetry:deleteItems
                       retryCount:self.maxAttempts
                completionHandler:augmentedCallback];
}

@end