import Foundation public struct RDEPUBTextAnchor: Codable, Equatable { public let fileIndex: Int public let row: Int public let column: Int public let chapterOffset: Int public let fragmentID: String? public var spineIndex: Int { fileIndex } public init( fileIndex: Int, row: Int, column: Int, chapterOffset: Int, fragmentID: String? = nil ) { self.fileIndex = fileIndex self.row = row self.column = column self.chapterOffset = chapterOffset self.fragmentID = fragmentID } enum CodingKeys: String, CodingKey { case fileIndex case spineIndex case row case column case chapterOffset case fragmentID } public init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) let decodedFileIndex = try container.decodeIfPresent(Int.self, forKey: .fileIndex) ?? container.decode(Int.self, forKey: .spineIndex) self.fileIndex = decodedFileIndex self.row = try container.decodeIfPresent(Int.self, forKey: .row) ?? 0 self.column = try container.decodeIfPresent(Int.self, forKey: .column) ?? 0 self.chapterOffset = try container.decode(Int.self, forKey: .chapterOffset) self.fragmentID = try container.decodeIfPresent(String.self, forKey: .fragmentID) } public func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(fileIndex, forKey: .fileIndex) try container.encode(row, forKey: .row) try container.encode(column, forKey: .column) try container.encode(chapterOffset, forKey: .chapterOffset) try container.encodeIfPresent(fragmentID, forKey: .fragmentID) } } public struct RDEPUBTextRangeAnchor: Codable, Equatable { public let start: RDEPUBTextAnchor public let end: RDEPUBTextAnchor public init(start: RDEPUBTextAnchor, end: RDEPUBTextAnchor) { self.start = start self.end = end } public var nsRange: NSRange { NSRange(location: start.chapterOffset, length: max(end.chapterOffset - start.chapterOffset, 0)) } }