Skip to content

[Collections] Add search provider abstraction #3821

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Oct 27, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Sources/PackageCollections/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ add_library(PackageCollections
Providers/JSONPackageCollectionProvider.swift
Providers/PackageCollectionProvider.swift
Providers/PackageMetadataProvider.swift
Providers/PackageSearchProvider.swift
Providers/StorageBackedPackageSearchProvider.swift
Storage/FilePackageCollectionsSourcesStorage.swift
Storage/PackageCollectionsSourcesStorage.swift
Storage/PackageCollectionsStorage.swift
Expand Down
44 changes: 22 additions & 22 deletions Sources/PackageCollections/Model/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -142,34 +142,13 @@ extension PackageCollectionsModel.Package {

/// Package version description
public let summary: String?

// TODO: remove (replaced by manifests)
public var packageName: String { self.defaultManifest!.packageName }

// TODO: remove (replaced by manifests)
public var targets: [Target] { self.defaultManifest!.targets }

// TODO: remove (replaced by manifests)
public var products: [Product] { self.defaultManifest!.products }

// TODO: remove (replaced by manifests)
public var toolsVersion: ToolsVersion { self.defaultManifest!.toolsVersion }

// TODO: remove (replaced by manifests)
public var minimumPlatformVersions: [SupportedPlatform]? { nil }


/// Manifests by tools version
public let manifests: [ToolsVersion: Manifest]

/// Tools version of the default manifest
public let defaultToolsVersion: ToolsVersion

// TODO: remove (replaced by verifiedCompatibility)
public var verifiedPlatforms: [PackageModel.Platform]? { nil }

// TODO: remove (replaced by verifiedCompatibility)
public var verifiedSwiftVersions: [SwiftLanguageVersion]? { nil }

/// An array of compatible platforms and Swift versions that has been tested and verified for.
public let verifiedCompatibility: [PackageCollectionsModel.Compatibility]?

Expand All @@ -178,6 +157,27 @@ extension PackageCollectionsModel.Package {

/// When the package version was created
public let createdAt: Date?

@available(*, deprecated, message: "use manifests instead")
public var packageName: String { self.defaultManifest!.packageName }

@available(*, deprecated, message: "use manifests instead")
public var targets: [Target] { self.defaultManifest!.targets }

@available(*, deprecated, message: "use manifests instead")
public var products: [Product] { self.defaultManifest!.products }

@available(*, deprecated, message: "use manifests instead")
public var toolsVersion: ToolsVersion { self.defaultManifest!.toolsVersion }

@available(*, deprecated, message: "use manifests instead")
public var minimumPlatformVersions: [SupportedPlatform]? { nil }

@available(*, deprecated, message: "use verifiedCompatibility instead")
public var verifiedPlatforms: [PackageModel.Platform]? { nil }

@available(*, deprecated, message: "use verifiedCompatibility instead")
public var verifiedSwiftVersions: [SwiftLanguageVersion]? { nil }

public struct Manifest: Equatable, Codable {
/// The Swift tools version specified in `Package.swift`.
Expand Down
12 changes: 9 additions & 3 deletions Sources/PackageCollections/PackageCollections.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ public struct PackageCollections: PackageCollectionsProtocol {
private let storageContainer: (storage: Storage, owned: Bool)
private let collectionProviders: [Model.CollectionSourceType: PackageCollectionProvider]
let metadataProvider: PackageMetadataProvider
let searchProvider: PackageSearchProvider

private var storage: Storage {
self.storageContainer.storage
Expand All @@ -46,25 +47,30 @@ public struct PackageCollections: PackageCollectionsProtocol {
configuration: .init(authTokens: configuration.authTokens),
observabilityScope: observabilityScope
)

let searchProvider = StorageBackedPackageSearchProvider(storage: storage.collections)

self.configuration = configuration
self.observabilityScope = observabilityScope
self.storageContainer = (storage, true)
self.collectionProviders = collectionProviders
self.metadataProvider = metadataProvider
self.searchProvider = searchProvider
}

// internal initializer for testing
init(configuration: Configuration = .init(),
observabilityScope: ObservabilityScope,
storage: Storage,
collectionProviders: [Model.CollectionSourceType: PackageCollectionProvider],
metadataProvider: PackageMetadataProvider) {
metadataProvider: PackageMetadataProvider,
customSearchProvider: PackageSearchProvider? = nil) {
self.configuration = configuration
self.observabilityScope = observabilityScope
self.storageContainer = (storage, false)
self.collectionProviders = collectionProviders
self.metadataProvider = metadataProvider
self.searchProvider = customSearchProvider ?? StorageBackedPackageSearchProvider(storage: storage.collections)
}

public func shutdown() throws {
Expand Down Expand Up @@ -308,7 +314,7 @@ public struct PackageCollections: PackageCollectionsProtocol {
if identifiers.isEmpty {
return callback(.success(Model.PackageSearchResult(items: [])))
}
self.storage.collections.searchPackages(identifiers: identifiers, query: query, callback: callback)
self.searchProvider.searchPackages(query, collections: Set(identifiers), callback: callback)
}
}
}
Expand Down Expand Up @@ -466,7 +472,7 @@ public struct PackageCollections: PackageCollectionsProtocol {
if identifiers.isEmpty {
return callback(.success(.init(items: [])))
}
self.storage.collections.searchTargets(identifiers: identifiers, query: query, type: searchType, callback: callback)
self.searchProvider.searchTargets(query, searchType: searchType, collections: Set(identifiers), callback: callback)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
This source file is part of the Swift.org open source project

Copyright (c) 2021 Apple Inc. and the Swift project authors
Licensed under Apache License v2.0 with Runtime Library Exception

See http://swift.org/LICENSE.txt for license information
See http://swift.org/CONTRIBUTORS.txt for Swift project authors
*/

import PackageModel

import TSCBasic

/// Package search API provider
protocol PackageSearchProvider {
/// The name of the provider
var name: String { get }

/// Searches for packages using the given query. Packages in the result must belong to an imported package collection.
///
/// - Parameters:
/// - query: The search query.
/// - collections: Optional. The identifiers of the `PackageCollection`s to filter results on.
/// - callback: The closure to invoke when result becomes available.
func searchPackages(
_ query: String,
collections: Set<PackageCollectionsModel.CollectionIdentifier>?,
callback: @escaping (Result<PackageCollectionsModel.PackageSearchResult, Error>) -> Void
)

/// Searches for packages using the given query. Packages in the result do not have to belong to an imported package collection.
/// In other words, collection-related information in the result can be missing or empty.
///
/// - Parameters:
/// - query: The search query.
/// - callback: The closure to invoke when result becomes available.
func searchPackages(
_ query: String,
callback: @escaping (Result<PackageCollectionsModel.PackageSearchResult, Error>) -> Void
)

/// Finds targets by name and returns the corresponding packages, which must belong to an imported package collection.
///
/// This API's result items will be aggregated by target then package, with the
/// package's versions list filtered to only include those that contain the target.
///
/// - Parameters:
/// - query: The search query
/// - searchType: Target names must either match exactly or contain the prefix.
/// For more flexibility, use the `searchPackages` API instead.
/// - collections: Optional. The identifiers of the `PackageCollection`s to filter results on.
/// - callback: The closure to invoke when result becomes available
func searchTargets(
_ query: String,
searchType: PackageCollectionsModel.TargetSearchType,
collections: Set<PackageCollectionsModel.CollectionIdentifier>?,
callback: @escaping (Result<PackageCollectionsModel.TargetSearchResult, Error>) -> Void
)

/// Finds targets by name and returns the corresponding packages. Packages do not have to belong to an imported package collection.
/// In other words, collection-related information in the result can be missing or empty.
///
/// - Parameters:
/// - query: The search query
/// - searchType: Target names must either match exactly or contain the prefix.
/// For more flexibility, use the `searchPackages` API instead.
/// - callback: The closure to invoke when result becomes available
func searchTargets(
_ query: String,
searchType: PackageCollectionsModel.TargetSearchType,
callback: @escaping (Result<PackageCollectionsModel.TargetSearchResult, Error>) -> Void
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
This source file is part of the Swift.org open source project

Copyright (c) 2021 Apple Inc. and the Swift project authors
Licensed under Apache License v2.0 with Runtime Library Exception

See http://swift.org/LICENSE.txt for license information
See http://swift.org/CONTRIBUTORS.txt for Swift project authors
*/

/// A `PackageSearchProvider` backed by `PackageCollectionsStorage`.
///
/// Implicitly, packages in search results must belong to an imported collection since that is
/// the only way for a package to be added to storage.
struct StorageBackedPackageSearchProvider: PackageSearchProvider {
private let storage: PackageCollectionsStorage

let name: String = "Storage-Backed"

init(storage: PackageCollectionsStorage) {
self.storage = storage
}

/// Searches for packages in specific imported package collections.
func searchPackages(_ query: String,
collections: Set<PackageCollectionsModel.CollectionIdentifier>?,
callback: @escaping (Result<PackageCollectionsModel.PackageSearchResult, Error>) -> Void) {
self.storage.searchPackages(identifiers: collections.map(Array.init), query: query, callback: callback)
}

/// Searches for packages in all imported package collections.
func searchPackages(_ query: String,
callback: @escaping (Result<PackageCollectionsModel.PackageSearchResult, Error>) -> Void) {
self.searchPackages(query, collections: nil, callback: callback)
}

/// Searches for targets in specific imported package collections.
func searchTargets(_ query: String,
searchType: PackageCollectionsModel.TargetSearchType,
collections: Set<PackageCollectionsModel.CollectionIdentifier>?,
callback: @escaping (Result<PackageCollectionsModel.TargetSearchResult, Error>) -> Void) {
self.storage.searchTargets(identifiers: collections.map(Array.init), query: query, type: searchType, callback: callback)
}

/// Searches for packages in all imported package collections.
func searchTargets(_ query: String,
searchType: PackageCollectionsModel.TargetSearchType,
callback: @escaping (Result<PackageCollectionsModel.TargetSearchResult, Error>) -> Void) {
self.searchTargets(query, searchType: searchType, collections: nil, callback: callback)
}
}