Fishee/Sources/Parser.swift

168 lines
5.3 KiB
Swift
Raw Normal View History

2024-10-05 13:10:39 -07:00
//
// Copyright © 2024 Salar Rahmanian.
2025-02-09 10:34:15 -08:00
//
2024-10-05 13:10:39 -07:00
// 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
2025-02-09 10:34:15 -08:00
//
2024-10-05 13:10:39 -07:00
// http://www.apache.org/licenses/LICENSE-2.0
2025-02-09 10:34:15 -08:00
//
2024-10-05 13:10:39 -07:00
// 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 Foundation
2024-11-16 20:10:28 -08:00
/// Make a backup of the fish history.
///
/// ```
///The copied files name will have _copy appended to the filename before the file extension.
///For example:
/// if the file was example.txt the copied file will be example_copy.txt
/// ```
///
/// - Parameters
/// - path: The file path to the fish history file.
///
/// - Returns: true if backup copy successful, false if not.
2024-10-05 13:10:39 -07:00
func backupHistory(_ path: String) -> Bool {
2025-02-09 10:34:15 -08:00
let fileManager = FileManager.default
guard fileManager.fileExists(atPath: path) else {
print("File does not exist at path: \(path)")
return false
}
let fileURL = URL(fileURLWithPath: path)
let fileExtension = fileURL.pathExtension
let fileNameWithoutExtension = fileURL.deletingPathExtension().lastPathComponent
let directory = fileURL.deletingLastPathComponent()
2024-10-05 13:10:39 -07:00
2025-02-09 10:34:15 -08:00
let newFileName = "\(fileNameWithoutExtension)_copy"
let newFileURL = directory.appendingPathComponent(newFileName).appendingPathExtension(
fileExtension)
do {
try? fileManager.removeItem(at: newFileURL)
try fileManager.copyItem(at: fileURL, to: newFileURL)
print("File duplicated successfully to: \(newFileURL.path)")
return true
} catch {
print("error making a backup of \(path), got error: \(error)")
return false
}
}
2024-10-05 13:10:39 -07:00
2024-11-16 20:10:28 -08:00
/// Write fish history to file.
///
/// - Parameters
/// - path: The file path to the fish history file.
/// - history: History list to write to file.
/// - backup: Should the current history file be backed up first boolean.
///
/// - Returns: true if writing to file copy successful, false if not.
2024-10-05 14:27:18 -07:00
func writeFishHistory(to path: String, history: [FishHistoryEntry], backup: Bool = true) -> Bool {
2025-02-09 10:34:15 -08:00
var output = ""
if backup {
let result = backupHistory(path)
if !result {
print("Failed to backup \(path) so aborting!")
return false
2024-10-05 13:10:39 -07:00
}
2025-02-09 10:34:15 -08:00
}
history.forEach { output += $0.writeEntry().joined(separator: "\n") + "\n" }
if !output.isEmpty {
do {
try output.write(toFile: path, atomically: true, encoding: .utf8)
print("Successfully wrote merged history to \(path)")
return true
} catch {
print("Error writing merged history: \(error)")
return false
2024-10-05 13:10:39 -07:00
}
2025-02-09 10:34:15 -08:00
} else {
print("Nothing to write to \(path)")
return false
}
2024-10-05 13:10:39 -07:00
}
2024-11-16 20:10:28 -08:00
/// Parse the fish history file.
///
/// - Parameters
/// - filePath: The file path to the fish history file.
///
/// - Returns: List of ``FishHistoryEntry`` entries from history file.
2024-10-05 13:10:39 -07:00
func parseFishHistory(from filePath: String) -> [FishHistoryEntry]? {
2025-02-09 10:34:15 -08:00
guard let fileContents = try? String(contentsOfFile: filePath, encoding: .utf8) else {
print("Failed to open file.")
return nil
}
let lines = fileContents.split(separator: "\n").map {
String($0).trimmingCharacters(in: .whitespaces)
}
let initialState:
(entries: [FishHistoryEntry], currentCmd: String?, currentWhen: Int?, currentPaths: [String]) =
([], nil, nil, [])
let result = lines.reduce(into: initialState) { state, line in
if line.starts(with: "- cmd:") {
if let cmd = state.currentCmd, let when = state.currentWhen {
let entry = FishHistoryEntry(cmd: cmd, when: when, paths: state.currentPaths)
state.entries.append(entry)
state.currentPaths = []
}
state.currentCmd = String(line.dropFirst("- cmd:".count).trimmingCharacters(in: .whitespaces))
} else if line.starts(with: "when:") {
if let whenValue = Int(line.dropFirst("when:".count).trimmingCharacters(in: .whitespaces)) {
state.currentWhen = whenValue
}
} else if line.starts(with: "paths:") {
return
} else if line.starts(with: "- ") {
let path = String(line.dropFirst("- ".count).trimmingCharacters(in: .whitespaces))
state.currentPaths.append(path)
2024-10-05 13:10:39 -07:00
}
2025-02-09 10:34:15 -08:00
}
if let cmd = result.currentCmd, let when = result.currentWhen {
let entry = FishHistoryEntry(cmd: cmd, when: when, paths: result.currentPaths)
return result.entries + [entry]
}
return result.entries
2024-10-05 13:10:39 -07:00
}
2024-11-16 20:10:28 -08:00
/// Merge two given ``FishHistoryEntry`` lists into one list.
///
/// - Parameters
/// - left: The file path to the first fish history file.
/// - right: The file path to the second fish history file.
/// - removeDuplicates: if true, remove any duplicates found after merging the two lists.
///
/// - Returns: Single list of ``FishHistoryEntry`` entries.
2025-02-09 10:34:15 -08:00
func mergeFishHistory(
_ left: [FishHistoryEntry], _ right: [FishHistoryEntry], removeDuplicates: Bool = false
) -> [FishHistoryEntry] {
let merged = left + right
if removeDuplicates {
let finalList = merged.reduce(into: [String: FishHistoryEntry]()) { result, entry in
if result[entry.cmd] == nil {
result[entry.cmd] = entry
}
2024-10-05 13:10:39 -07:00
}
2025-02-09 10:34:15 -08:00
return Array(finalList.values)
} else {
return merged
}
2024-10-05 13:10:39 -07:00
}