Skip to content

Commit

Permalink
Observe installed docsets and auto index them from the search module.
Browse files Browse the repository at this point in the history
  • Loading branch information
GetToSet committed Feb 12, 2025
1 parent 92bf145 commit 332fdcd
Show file tree
Hide file tree
Showing 7 changed files with 113 additions and 17 deletions.
54 changes: 54 additions & 0 deletions BoltCombineExtensions/Sources/Publisher+withPrevious.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
//
// Copyright (C) 2025 Bolt Contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

import Combine

// https://stackoverflow.com/questions/63926305/combine-previous-value-using-combine

public extension Publisher {

/// Includes the current element as well as the previous element from the upstream publisher in a tuple where the previous element is optional.
/// The first time the upstream publisher emits an element, the previous element will be `nil`.
///
/// let range = (1...5)
/// cancellable = range.publisher
/// .withPrevious()
/// .sink { print ("(\($0.previous), \($0.current))", terminator: " ") }
/// // Prints: "(nil, 1) (Optional(1), 2) (Optional(2), 3) (Optional(3), 4) (Optional(4), 5) ".
///
/// - Returns: A publisher of a tuple of the previous and current elements from the upstream publisher.
func withPrevious() -> AnyPublisher<(previous: Output?, current: Output), Failure> {
scan(Optional<(Output?, Output)>.none) { ($0?.1, $1) }
.compactMap { $0 }
.eraseToAnyPublisher()
}

/// Includes the current element as well as the previous element from the upstream publisher in a tuple where the previous element is not optional.
/// The first time the upstream publisher emits an element, the previous element will be the `initialPreviousValue`.
///
/// let range = (1...5)
/// cancellable = range.publisher
/// .withPrevious(0)
/// .sink { print ("(\($0.previous), \($0.current))", terminator: " ") }
/// // Prints: "(0, 1) (1, 2) (2, 3) (3, 4) (4, 5) ".
///
/// - Parameter initialPreviousValue: The initial value to use as the "previous" value when the upstream publisher emits for the first time.
/// - Returns: A publisher of a tuple of the previous and current elements from the upstream publisher.
func withPrevious(_ initialPreviousValue: Output) -> AnyPublisher<(previous: Output, current: Output), Failure> {
scan((initialPreviousValue, initialPreviousValue)) { ($0.1, $1) }.eraseToAnyPublisher()
}

}
6 changes: 3 additions & 3 deletions BoltServices/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ let moduleTargets: [Target] = [
),
.target(
name: "BoltDocsets",
dependencies: [Dependencies.GRDB, "BoltArchives", "BoltDatabase", "BoltRepository", "BoltSearch", "BoltURLSchemes", "BoltUtils"],
dependencies: [Dependencies.GRDB, "BoltArchives", "BoltDatabase", "BoltRepository", "BoltURLSchemes", "BoltUtils"],
path: "./Sources/Docsets/Sources",
swiftSettings: [
.swiftLanguageMode(.v5)
Expand All @@ -98,7 +98,7 @@ let moduleTargets: [Target] = [
),
.target(
name: "BoltSearch",
dependencies: [Dependencies.GRDB, "BoltCombineExtensions", "BoltRxSwift", "BoltTypes"],
dependencies: [Dependencies.GRDB, "BoltCombineExtensions", "BoltDocsets", "BoltRxSwift", "BoltTypes"],
path: "./Sources/Search/Sources",
swiftSettings: [
.swiftLanguageMode(.v5)
Expand Down Expand Up @@ -165,7 +165,7 @@ let testTargets: [Target] = [
),
.testTarget(
name: "BoltDocsetsTests",
dependencies: ["BoltDocsets", "BoltTestingUtils"],
dependencies: ["BoltDocsets", "BoltSearch", "BoltTestingUtils"],
path: "./Sources/Docsets/",
sources: [
"./Tests"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import Factory

import BoltCombineExtensions
import BoltDatabase
import BoltSearch
import BoltTypes
import BoltUtils

Expand Down Expand Up @@ -67,15 +66,6 @@ struct DocsetInstaller {
repository: entry.feed.repository
)
try LibraryDatabase.shared.insertDocsetInstallation(docsetInstallation)

Task {
let searchService = Container.shared.searchService()
let searchIndex = await searchService.searchIndex(
forDocsetPath: docsetPath,
identifier: docsetInstallation.description
)
await searchService.queueToCreateSearchIndex(searchIndex)
}
} catch {
promise(.failure(error))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import Foundation
import XCTest

import BoltSearch
import BoltTestingUtils
import BoltTypes
import BoltUtils
Expand Down
6 changes: 3 additions & 3 deletions BoltServices/Sources/Search/Sources/DocsetIndexerWorker.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import BoltCombineExtensions
import BoltTypes
import BoltUtils

public struct DocsetIndexerWorker: LoggerProvider {
struct DocsetIndexerWorker: LoggerProvider {

struct ZIndex: Codable, FetchableRecord {

Expand Down Expand Up @@ -58,7 +58,7 @@ public struct DocsetIndexerWorker: LoggerProvider {
attributes: .concurrent
)

public static func createSearchIndex(withDatabaseQueue dbQueue: DatabaseQueue) -> AsyncThrowingStream<Double, Error> {
static func createSearchIndex(withDatabaseQueue dbQueue: DatabaseQueue) -> AsyncThrowingStream<Double, Error> {
return AsyncThrowingStream { continuation in
do {
try dbQueue.write { db in
Expand Down Expand Up @@ -126,7 +126,7 @@ public struct DocsetIndexerWorker: LoggerProvider {
}
}

public static func createQueryIndex(withDatabaseQueue dbQueue: DatabaseQueue) -> AsyncThrowingStream<Double, Error> {
static func createQueryIndex(withDatabaseQueue dbQueue: DatabaseQueue) -> AsyncThrowingStream<Double, Error> {
return AsyncThrowingStream { continuation in
do {
try dbQueue.write { db in
Expand Down
45 changes: 44 additions & 1 deletion BoltServices/Sources/Search/Sources/SearchServiceImp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,57 @@
// limitations under the License.
//

import Combine
import Factory

import BoltDocsets
import BoltTypes
import BoltUtils

final class SearchServiceImp: SearchService, LoggerProvider {

final class SearchServiceImp: SearchService {
@Injected(\.libraryDocsetsManager)
private var libraryDocsetsManager: LibraryDocsetsManager

private let docsetIndexer = DocsetIndexer(maxConcurrentTasks: 1)

private var searchIndices = [String: DocsetSearchIndex]()

private var cancellables = Set<AnyCancellable>()

init() {
libraryDocsetsManager
.installedDocsets()
.withPrevious([])
.sink { [weak self] previous, current in
guard let self = self else {
return
}

let mapToDocset: (LibraryInstallationQueryResult) -> Docset? = {
if case let .docset(docset) = $0 {
return docset
}
return nil
}

let prevDocsets = Set(previous.compactMap(mapToDocset))
let currentDocsets = Set(current.compactMap(mapToDocset))

let addedDocsets = currentDocsets.subtracting(prevDocsets)

Self.logger.info("queue to index newly added docsets: \(addedDocsets)")

Task {
for docset in addedDocsets {
let searchIndex = await self.searchIndex(forDocset: docset)
await self.queueToCreateSearchIndex(searchIndex)
}
}
}
.store(in: &cancellables)
}

func searchIndex(forDocsetPath docsetPath: String, identifier: String) async -> DocsetSearchIndex {
if let index = searchIndices[docsetPath] {
return index
Expand Down
8 changes: 8 additions & 0 deletions BoltServices/Sources/Types/Docset.swift
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,14 @@ public struct Docset: Sendable {

}

extension Docset: CustomStringConvertible {

public var description: String {
return "Docset(identifier: \(identifier), path: \(path))"
}

}

extension Docset: LibraryRecord {

public var uuid: UUID {
Expand Down

0 comments on commit 332fdcd

Please sign in to comment.