53 lines
1.5 KiB
Swift
53 lines
1.5 KiB
Swift
import Foundation
|
|
import VorleserKit
|
|
|
|
public struct Book: Sendable {
|
|
public let id: UUID
|
|
public let title: String
|
|
public let author: String?
|
|
public let chapters: [Chapter]
|
|
|
|
public init(id: UUID = UUID(), title: String, author: String?, chapters: [Chapter]) {
|
|
self.id = id
|
|
self.title = title
|
|
self.author = author
|
|
self.chapters = chapters
|
|
}
|
|
|
|
/// All sentences across all chapters, with global character offsets.
|
|
public var sentences: [Sentence] {
|
|
var result: [Sentence] = []
|
|
var offset: CharacterOffset = 0
|
|
for chapter in chapters {
|
|
let chapterSentences = SentenceSegmenter.segment(chapter.text, globalOffset: offset)
|
|
result.append(contentsOf: chapterSentences)
|
|
offset += chapter.text.count
|
|
}
|
|
return result
|
|
}
|
|
|
|
/// Returns the sentence index containing the given global character offset.
|
|
public func sentenceIndex(containing offset: CharacterOffset) -> Int? {
|
|
let allSentences = sentences
|
|
return allSentences.firstIndex { $0.range.contains(offset) }
|
|
}
|
|
|
|
/// Maps a global character offset to (chapter index, local offset within chapter).
|
|
public func chapterAndLocalOffset(for globalOffset: CharacterOffset) -> (chapterIndex: Int, localOffset: Int)? {
|
|
var offset = 0
|
|
for chapter in chapters {
|
|
let chapterEnd = offset + chapter.text.count
|
|
if globalOffset < chapterEnd {
|
|
return (chapter.index, globalOffset - offset)
|
|
}
|
|
offset = chapterEnd
|
|
}
|
|
return nil
|
|
}
|
|
|
|
/// Total character count across all chapters.
|
|
public var totalCharacters: Int {
|
|
chapters.reduce(0) { $0 + $1.text.count }
|
|
}
|
|
}
|