vChewing-macOS/Packages/vChewing_LangModelAssembly/Sources/LangModelAssembly/SubLMs/lmReplacements.swift

119 lines
3.3 KiB
Swift

// (c) 2021 and onwards The vChewing Project (MIT-NTL License).
// StringView Ranges extension by (c) 2022 and onwards Isaac Xen (MIT License).
// ====================
// This code is released under the MIT license (SPDX-License-Identifier: MIT)
// ... with NTL restriction stating that:
// No trademark license is granted to use the trade names, trademarks, service
// marks, or product names of Contributor, except as required to fulfill notice
// requirements defined in MIT License.
import Shared
extension vChewingLM {
@frozen public struct LMReplacements {
public private(set) var filePath: String?
var rangeMap: [String: Range<String.Index>] = [:]
var strData: String = ""
public var count: Int { rangeMap.count }
public init() {
rangeMap = [:]
}
public var isLoaded: Bool { !rangeMap.isEmpty }
@discardableResult public mutating func open(_ path: String) -> Bool {
if isLoaded { return false }
let oldPath = filePath
filePath = nil
LMConsolidator.fixEOF(path: path)
LMConsolidator.consolidate(path: path, pragma: true)
do {
let rawStrData = try String(contentsOfFile: path, encoding: .utf8)
replaceData(textData: rawStrData)
} catch {
filePath = oldPath
vCLog("\(error)")
vCLog("↑ Exception happened when reading data at: \(path).")
return false
}
filePath = path
return true
}
///
/// - parameters:
/// - path:
public mutating func replaceData(textData rawStrData: String) {
if strData == rawStrData { return }
strData = rawStrData
strData.ranges(splitBy: "\n").filter { !$0.isEmpty }.forEach {
let neta = strData[$0].split(separator: " ")
if neta.count >= 2 {
let theKey = String(neta[0])
if !neta[0].isEmpty, !neta[1].isEmpty, theKey.first != "#" {
let theValue = $0
rangeMap[theKey] = theValue
}
}
}
}
public mutating func clear() {
filePath = nil
strData.removeAll()
rangeMap.removeAll()
}
public func saveData() {
guard let filePath = filePath else { return }
do {
try strData.write(toFile: filePath, atomically: true, encoding: .utf8)
} catch {
vCLog("Failed to save current database to: \(filePath)")
}
}
public func dump() {
var strDump = ""
for entry in rangeMap {
strDump += strData[entry.value] + "\n"
}
vCLog(strDump)
}
public func valuesFor(key: String) -> String {
guard let range = rangeMap[key] else {
return ""
}
let arrNeta = strData[range].split(separator: " ")
guard arrNeta.count >= 2 else {
return ""
}
return String(arrNeta[1])
}
public func hasValuesFor(key: String) -> Bool {
rangeMap[key] != nil
}
}
}
// MARK: - StringView Ranges Extension (by Isaac Xen)
extension String {
fileprivate func ranges(splitBy separator: Element) -> [Range<String.Index>] {
var startIndex = startIndex
return split(separator: separator).reduce(into: []) { ranges, substring in
_ = range(of: substring, range: startIndex..<endIndex).map { range in
ranges.append(range)
startIndex = range.upperBound
}
}
}
}