swift format

This commit is contained in:
Salar Rahmanian 2025-02-09 10:34:15 -08:00
parent f69cec36ac
commit 54edbfc36c
8 changed files with 407 additions and 395 deletions

View file

@ -1,12 +1,12 @@
// //
// Copyright © 2024 Salar Rahmanian. // Copyright © 2024 Salar Rahmanian.
// //
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
// You may obtain a copy of the License at // You may obtain a copy of the License at
// //
// http://www.apache.org/licenses/LICENSE-2.0 // http://www.apache.org/licenses/LICENSE-2.0
// //
// Unless required by applicable law or agreed to in writing, software // Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, // distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -15,30 +15,30 @@
// //
import Foundation import Foundation
/// Data Structure/Schema representing an entry in a fish history file. /// Data Structure/Schema representing an entry in a fish history file.
struct FishHistoryEntry: Equatable { struct FishHistoryEntry: Equatable {
let cmd: String let cmd: String
let when: Int let when: Int
let paths: [String] let paths: [String]
/// Converts time to Date object /// Converts time to Date object
func getDate() -> Date { func getDate() -> Date {
Date(timeIntervalSince1970: TimeInterval(when)) Date(timeIntervalSince1970: TimeInterval(when))
} }
/// Converts structure back to list of strings as expected in a fish history file. /// Converts structure back to list of strings as expected in a fish history file.
func writeEntry() -> [String] { func writeEntry() -> [String] {
var output: [String] = [] var output: [String] = []
output.append("- cmd: \(cmd)") output.append("- cmd: \(cmd)")
output.append(" when: \(when)") output.append(" when: \(when)")
if !paths.isEmpty { if !paths.isEmpty {
output.append(" paths:") output.append(" paths:")
paths.forEach { output.append(" - \(String($0))") } paths.forEach { output.append(" - \(String($0))") }
}
return output
} }
return output
}
} }

View file

@ -1,23 +1,21 @@
// //
// Copyright © 2024 Salar Rahmanian. // Copyright © 2024 Salar Rahmanian.
// //
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
// You may obtain a copy of the License at // You may obtain a copy of the License at
// //
// http://www.apache.org/licenses/LICENSE-2.0 // http://www.apache.org/licenses/LICENSE-2.0
// //
// Unless required by applicable law or agreed to in writing, software // Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, // distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
// //
import Foundation import Foundation
/// Gets the full file path to a given file. /// Gets the full file path to a given file.
/// ///
/// - Parameters /// - Parameters
@ -25,20 +23,20 @@ import Foundation
/// ///
/// - Returns: A Valid file URL or None if invalid. /// - Returns: A Valid file URL or None if invalid.
func getPath(_ pathStr: String) -> URL? { func getPath(_ pathStr: String) -> URL? {
let userHomeDirectory = FileManager.default.homeDirectoryForCurrentUser.path let userHomeDirectory = FileManager.default.homeDirectoryForCurrentUser.path
var filePath: String = pathStr var filePath: String = pathStr
if pathStr.hasPrefix("~") { if pathStr.hasPrefix("~") {
filePath = (pathStr as NSString).expandingTildeInPath filePath = (pathStr as NSString).expandingTildeInPath
} }
if pathStr.hasPrefix("$HOME") { if pathStr.hasPrefix("$HOME") {
filePath = filePath.replacingOccurrences(of: "$HOME", with: userHomeDirectory) filePath = filePath.replacingOccurrences(of: "$HOME", with: userHomeDirectory)
} }
if !FileManager.default.fileExists(atPath: filePath) { if !FileManager.default.fileExists(atPath: filePath) {
return nil return nil
} }
return URL(fileURLWithPath: filePath) return URL(fileURLWithPath: filePath)
} }

View file

@ -14,7 +14,6 @@
// limitations under the License. // limitations under the License.
// //
import ArgumentParser import ArgumentParser
import Foundation import Foundation
@ -22,81 +21,83 @@ let DEFAULT_FISH_HISTORY_LOCATION: String = "~/.local/share/fish/fish_history"
@main @main
struct Fishee: ParsableCommand { struct Fishee: ParsableCommand {
@Option(name: [.short, .customLong("history-file")], help: "Location of your fish history file. Will default to ~/.local/share/fish/fish_history") @Option(
var fishHistoryLocationStr: String? name: [.short, .customLong("history-file")],
help: "Location of your fish history file. Will default to ~/.local/share/fish/fish_history")
@Option(name: .shortAndLong, help: "File path to file to merge with history file.") var fishHistoryLocationStr: String?
var mergeFile: String?
@Option(name: .shortAndLong, help: "File path to file to merge with history file.")
@Option( var mergeFile: String?
name: [.short, .customLong("output-file")],
help: "File to write to. Default: same as current history file." @Option(
) name: [.short, .customLong("output-file")],
var writeFileStr: String? help: "File to write to. Default: same as current history file."
)
@Flag( var writeFileStr: String?
name: .shortAndLong,
help: "Dry run. Will only print to the console without actually modifying the history file." @Flag(
) name: .shortAndLong,
var dryRun: Bool = false help: "Dry run. Will only print to the console without actually modifying the history file."
)
@Flag( var dryRun: Bool = false
name: .shortAndLong,
help: "Remove duplicates from combined history. Default: false" @Flag(
) name: .shortAndLong,
var removeDuplicates: Bool = false help: "Remove duplicates from combined history. Default: false"
)
@Flag( var removeDuplicates: Bool = false
name: .shortAndLong,
inversion: .prefixedNo, @Flag(
help: "Backup fish history file given before writing." name: .shortAndLong,
) inversion: .prefixedNo,
var backup: Bool = true help: "Backup fish history file given before writing."
)
var fishHistoryLocation: URL? { var backup: Bool = true
let pathStr = fishHistoryLocationStr ?? DEFAULT_FISH_HISTORY_LOCATION
return getPath(pathStr) var fishHistoryLocation: URL? {
} let pathStr = fishHistoryLocationStr ?? DEFAULT_FISH_HISTORY_LOCATION
return getPath(pathStr)
var writeFileLocation: URL? { }
let pathStr = writeFileStr ?? DEFAULT_FISH_HISTORY_LOCATION
return getPath(pathStr) var writeFileLocation: URL? {
} let pathStr = writeFileStr ?? DEFAULT_FISH_HISTORY_LOCATION
return getPath(pathStr)
public func run() throws { }
let mergeFileLocation = mergeFile.flatMap { getPath($0) }
let finalHistory: [FishHistoryEntry] = switch (fishHistoryLocation, mergeFileLocation) { public func run() throws {
case let (fishHistoryLocation?, mergeFileLocation?): let mergeFileLocation = mergeFile.flatMap { getPath($0) }
{ let finalHistory: [FishHistoryEntry] =
let currentHistory = parseFishHistory(from: fishHistoryLocation.path) ?? [] switch (fishHistoryLocation, mergeFileLocation) {
let toMergeHistory = parseFishHistory(from: mergeFileLocation.path) ?? [] case let (fishHistoryLocation?, mergeFileLocation?):
return mergeFishHistory(currentHistory, toMergeHistory, removeDuplicates: removeDuplicates) {
}() let currentHistory = parseFishHistory(from: fishHistoryLocation.path) ?? []
case let (fishHistoryLocation?, nil): let toMergeHistory = parseFishHistory(from: mergeFileLocation.path) ?? []
parseFishHistory(from: fishHistoryLocation.path) ?? [] return mergeFishHistory(
default: currentHistory, toMergeHistory, removeDuplicates: removeDuplicates)
[] }()
case let (fishHistoryLocation?, nil):
parseFishHistory(from: fishHistoryLocation.path) ?? []
default:
[]
}
if dryRun {
finalHistory.forEach { print("\($0.writeEntry().joined(separator: "\n"))") }
} else {
if let writePath = writeFileLocation?.path {
let result = writeFishHistory(
to: writePath,
history: finalHistory,
backup: backup
)
if result {
print("Succussfully updated \(writePath)")
} else {
print("Failed to update \(writePath)")
} }
}
if dryRun {
finalHistory.forEach { print("\($0.writeEntry().joined(separator: "\n"))") }
}
else {
if let writePath = writeFileLocation?.path {
let result = writeFishHistory(
to: writePath,
history: finalHistory,
backup: backup
)
if result {
print("Succussfully updated \(writePath)")
}
else {
print("Failed to update \(writePath)")
}
}
}
return
} }
return
}
} }

View file

@ -1,23 +1,21 @@
// //
// Copyright © 2024 Salar Rahmanian. // Copyright © 2024 Salar Rahmanian.
// //
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
// You may obtain a copy of the License at // You may obtain a copy of the License at
// //
// http://www.apache.org/licenses/LICENSE-2.0 // http://www.apache.org/licenses/LICENSE-2.0
// //
// Unless required by applicable law or agreed to in writing, software // Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, // distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
// //
import Foundation import Foundation
/// Make a backup of the fish history. /// Make a backup of the fish history.
/// ///
/// ``` /// ```
@ -31,32 +29,32 @@ import Foundation
/// ///
/// - Returns: true if backup copy successful, false if not. /// - Returns: true if backup copy successful, false if not.
func backupHistory(_ path: String) -> Bool { func backupHistory(_ path: String) -> Bool {
let fileManager = FileManager.default 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()
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
}
}
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()
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
}
}
/// Write fish history to file. /// Write fish history to file.
/// ///
@ -67,32 +65,31 @@ func backupHistory(_ path: String) -> Bool {
/// ///
/// - Returns: true if writing to file copy successful, false if not. /// - Returns: true if writing to file copy successful, false if not.
func writeFishHistory(to path: String, history: [FishHistoryEntry], backup: Bool = true) -> Bool { func writeFishHistory(to path: String, history: [FishHistoryEntry], backup: Bool = true) -> Bool {
var output = "" var output = ""
if backup { if backup {
let result = backupHistory(path) let result = backupHistory(path)
if !result { if !result {
print("Failed to backup \(path) so aborting!") print("Failed to backup \(path) so aborting!")
return false return false
}
} }
}
history.forEach { output += $0.writeEntry().joined(separator: "\n") + "\n" }
history.forEach { output += $0.writeEntry().joined(separator: "\n") + "\n" }
if !output.isEmpty {
do { if !output.isEmpty {
try output.write(toFile: path, atomically: true, encoding: .utf8) do {
print("Successfully wrote merged history to \(path)") try output.write(toFile: path, atomically: true, encoding: .utf8)
return true print("Successfully wrote merged history to \(path)")
} catch { return true
print("Error writing merged history: \(error)") } catch {
return false print("Error writing merged history: \(error)")
} return false
}
else {
print("Nothing to write to \(path)")
return false
} }
} else {
print("Nothing to write to \(path)")
return false
}
} }
/// Parse the fish history file. /// Parse the fish history file.
@ -102,41 +99,45 @@ func writeFishHistory(to path: String, history: [FishHistoryEntry], backup: Bool
/// ///
/// - Returns: List of ``FishHistoryEntry`` entries from history file. /// - Returns: List of ``FishHistoryEntry`` entries from history file.
func parseFishHistory(from filePath: String) -> [FishHistoryEntry]? { func parseFishHistory(from filePath: String) -> [FishHistoryEntry]? {
guard let fileContents = try? String(contentsOfFile: filePath, encoding: .utf8) else { guard let fileContents = try? String(contentsOfFile: filePath, encoding: .utf8) else {
print("Failed to open file.") print("Failed to open file.")
return nil 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 lines = fileContents.split(separator: "\n").map {
String($0).trimmingCharacters(in: .whitespaces)
let result = lines.reduce(into: initialState) { state, line in }
if line.starts(with: "- cmd:") {
if let cmd = state.currentCmd, let when = state.currentWhen { let initialState:
let entry = FishHistoryEntry(cmd: cmd, when: when, paths: state.currentPaths) (entries: [FishHistoryEntry], currentCmd: String?, currentWhen: Int?, currentPaths: [String]) =
state.entries.append(entry) ([], nil, nil, [])
state.currentPaths = []
} let result = lines.reduce(into: initialState) { state, line in
state.currentCmd = String(line.dropFirst("- cmd:".count).trimmingCharacters(in: .whitespaces)) if line.starts(with: "- cmd:") {
} else if line.starts(with: "when:") { if let cmd = state.currentCmd, let when = state.currentWhen {
if let whenValue = Int(line.dropFirst("when:".count).trimmingCharacters(in: .whitespaces)) { let entry = FishHistoryEntry(cmd: cmd, when: when, paths: state.currentPaths)
state.currentWhen = whenValue state.entries.append(entry)
} state.currentPaths = []
} else if line.starts(with: "paths:") { }
return state.currentCmd = String(line.dropFirst("- cmd:".count).trimmingCharacters(in: .whitespaces))
} else if line.starts(with: "- ") { } else if line.starts(with: "when:") {
let path = String(line.dropFirst("- ".count).trimmingCharacters(in: .whitespaces)) if let whenValue = Int(line.dropFirst("when:".count).trimmingCharacters(in: .whitespaces)) {
state.currentPaths.append(path) 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)
} }
}
if let cmd = result.currentCmd, let when = result.currentWhen {
let entry = FishHistoryEntry(cmd: cmd, when: when, paths: result.currentPaths) if let cmd = result.currentCmd, let when = result.currentWhen {
return result.entries + [entry] let entry = FishHistoryEntry(cmd: cmd, when: when, paths: result.currentPaths)
} return result.entries + [entry]
}
return result.entries
return result.entries
} }
/// Merge two given ``FishHistoryEntry`` lists into one list. /// Merge two given ``FishHistoryEntry`` lists into one list.
@ -147,18 +148,20 @@ func parseFishHistory(from filePath: String) -> [FishHistoryEntry]? {
/// - removeDuplicates: if true, remove any duplicates found after merging the two lists. /// - removeDuplicates: if true, remove any duplicates found after merging the two lists.
/// ///
/// - Returns: Single list of ``FishHistoryEntry`` entries. /// - Returns: Single list of ``FishHistoryEntry`` entries.
func mergeFishHistory(_ left: [FishHistoryEntry], _ right: [FishHistoryEntry], removeDuplicates: Bool = false) -> [FishHistoryEntry] { func mergeFishHistory(
_ left: [FishHistoryEntry], _ right: [FishHistoryEntry], removeDuplicates: Bool = false
let merged = left + right ) -> [FishHistoryEntry] {
if removeDuplicates { let merged = left + right
let finalList = merged.reduce(into: [String:FishHistoryEntry]()) { result, entry in
if result[entry.cmd] == nil { if removeDuplicates {
result[entry.cmd] = entry let finalList = merged.reduce(into: [String: FishHistoryEntry]()) { result, entry in
} if result[entry.cmd] == nil {
} result[entry.cmd] = entry
return Array(finalList.values) }
} else {
return merged
} }
return Array(finalList.values)
} else {
return merged
}
} }

View file

@ -1,42 +1,44 @@
// //
// Copyright © 2024 Salar Rahmanian. // Copyright © 2024 Salar Rahmanian.
// //
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
// You may obtain a copy of the License at // You may obtain a copy of the License at
// //
// http://www.apache.org/licenses/LICENSE-2.0 // http://www.apache.org/licenses/LICENSE-2.0
// //
// Unless required by applicable law or agreed to in writing, software // Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, // distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
// //
import Foundation import Foundation
import Testing import Testing
@testable import Fishee @testable import Fishee
@Suite @Suite
final class AlgebraTests { final class AlgebraTests {
let historyItem = FishHistoryEntry(cmd: "cd Projects/Fishee/", when: 1727545693, paths: ["Projects/Fishee/"]) let historyItem = FishHistoryEntry(
cmd: "cd Projects/Fishee/", when: 1_727_545_693, paths: ["Projects/Fishee/"])
@Test func dateFromHistoryTest() {
let gotDate = historyItem.getDate()
#expect(gotDate == Date(timeIntervalSince1970: 1_727_545_693))
}
@Test func writeEntryTest() {
let entry = historyItem.writeEntry()
#expect(entry.count > 0)
let expectedEntry = """
- cmd: cd Projects/Fishee/
when: 1727545693
paths:
- Projects/Fishee/
"""
#expect(entry.joined(separator: "\n") == expectedEntry)
}
@Test func dateFromHistoryTest() {
let gotDate = historyItem.getDate()
#expect(gotDate == Date(timeIntervalSince1970: 1727545693))
}
@Test func writeEntryTest() {
let entry = historyItem.writeEntry()
#expect(entry.count > 0)
let expectedEntry = """
- cmd: cd Projects/Fishee/
when: 1727545693
paths:
- Projects/Fishee/
"""
#expect(entry.joined(separator: "\n") == expectedEntry)
}
} }

View file

@ -1,48 +1,50 @@
// //
// Copyright © 2024 Salar Rahmanian. // Copyright © 2024 Salar Rahmanian.
// //
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
// You may obtain a copy of the License at // You may obtain a copy of the License at
// //
// http://www.apache.org/licenses/LICENSE-2.0 // http://www.apache.org/licenses/LICENSE-2.0
// //
// Unless required by applicable law or agreed to in writing, software // Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, // distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
// //
import Foundation import Foundation
import Testing import Testing
@testable import Fishee
@testable import Fishee
@Suite(.serialized) @Suite(.serialized)
final class FileHelpersTests { final class FileHelpersTests {
let filePath = FileManager.default.homeDirectoryForCurrentUser.appendingPathComponent("myfile.txt") let filePath = FileManager.default.homeDirectoryForCurrentUser.appendingPathComponent(
"myfile.txt")
init() {
try? "this is a test".write( init() {
to: filePath, try? "this is a test".write(
atomically: true, to: filePath,
encoding: .utf8 atomically: true,
) encoding: .utf8
} )
}
deinit {
try? FileManager.default.removeItem(at: filePath) deinit {
} try? FileManager.default.removeItem(at: filePath)
}
@Test(arguments: [
"$HOME/myfile.txt", @Test(arguments: [
"~/myfile.txt", "$HOME/myfile.txt",
"\(FileManager.default.homeDirectoryForCurrentUser.path)/myfile.txt" "~/myfile.txt",
]) "\(FileManager.default.homeDirectoryForCurrentUser.path)/myfile.txt",
func getPathTest(testPath: String) { ])
let path = getPath(testPath) func getPathTest(testPath: String) {
let expected = URL(fileURLWithPath: "\(FileManager.default.homeDirectoryForCurrentUser.path)/myfile.txt") let path = getPath(testPath)
#expect(path == expected) let expected = URL(
} fileURLWithPath: "\(FileManager.default.homeDirectoryForCurrentUser.path)/myfile.txt")
#expect(path == expected)
}
} }

View file

@ -1,77 +1,77 @@
// //
// Copyright © 2024 Salar Rahmanian. // Copyright © 2024 Salar Rahmanian.
// //
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
// You may obtain a copy of the License at // You may obtain a copy of the License at
// //
// http://www.apache.org/licenses/LICENSE-2.0 // http://www.apache.org/licenses/LICENSE-2.0
// //
// Unless required by applicable law or agreed to in writing, software // Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, // distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
// //
import Foundation import Foundation
import Testing import Testing
@testable import Fishee @testable import Fishee
@Suite @Suite
final class FisheeTests { final class FisheeTests {
@Test func DryRunTest() { @Test func DryRunTest() {
do { do {
let help = try #require(Fishee.parse(["--dry-run"]) as Fishee) let help = try #require(Fishee.parse(["--dry-run"]) as Fishee)
#expect(help.dryRun) #expect(help.dryRun)
} catch { } catch {
Issue.record("Test failed! \(error)") Issue.record("Test failed! \(error)")
}
}
@Test func HistoryFileTest() {
do {
let help = try #require(Fishee.parse(["--history-file", "/tmp/fishtest.txt"]) as Fishee)
#expect(help.fishHistoryLocationStr == "/tmp/fishtest.txt")
} catch {
Issue.record("Test failed! \(error)")
}
} }
}
@Test func OutputFileTest() { @Test func HistoryFileTest() {
do { do {
let help = try #require(Fishee.parse(["--output-file", "/tmp/fishtest.txt"]) as Fishee) let help = try #require(Fishee.parse(["--history-file", "/tmp/fishtest.txt"]) as Fishee)
#expect(help.writeFileStr == "/tmp/fishtest.txt") #expect(help.fishHistoryLocationStr == "/tmp/fishtest.txt")
} catch { } catch {
Issue.record("Test failed! \(error)") Issue.record("Test failed! \(error)")
}
} }
}
@Test func RemoveDuplicatesTest() { @Test func OutputFileTest() {
do { do {
let help = try #require(Fishee.parse(["--remove-duplicates"]) as Fishee) let help = try #require(Fishee.parse(["--output-file", "/tmp/fishtest.txt"]) as Fishee)
#expect(help.removeDuplicates) #expect(help.writeFileStr == "/tmp/fishtest.txt")
} catch { } catch {
Issue.record("Test failed! \(error)") Issue.record("Test failed! \(error)")
}
} }
}
@Test func BackupTest() { @Test func RemoveDuplicatesTest() {
do { do {
let help = try #require(Fishee.parse(["--backup"]) as Fishee) let help = try #require(Fishee.parse(["--remove-duplicates"]) as Fishee)
#expect(help.backup) #expect(help.removeDuplicates)
} catch { } catch {
Issue.record("Test failed! \(error)") Issue.record("Test failed! \(error)")
}
} }
}
@Test func NoBackupTest() { @Test func BackupTest() {
do { do {
let help = try #require(Fishee.parse(["--no-backup"]) as Fishee) let help = try #require(Fishee.parse(["--backup"]) as Fishee)
#expect(!help.backup) #expect(help.backup)
} catch { } catch {
Issue.record("Test failed! \(error)") Issue.record("Test failed! \(error)")
}
} }
}
@Test func NoBackupTest() {
do {
let help = try #require(Fishee.parse(["--no-backup"]) as Fishee)
#expect(!help.backup)
} catch {
Issue.record("Test failed! \(error)")
}
}
} }

View file

@ -1,96 +1,102 @@
// //
// Copyright © 2024 Salar Rahmanian. // Copyright © 2024 Salar Rahmanian.
// //
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
// You may obtain a copy of the License at // You may obtain a copy of the License at
// //
// http://www.apache.org/licenses/LICENSE-2.0 // http://www.apache.org/licenses/LICENSE-2.0
// //
// Unless required by applicable law or agreed to in writing, software // Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, // distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
// //
import Foundation import Foundation
import Testing import Testing
@testable import Fishee @testable import Fishee
@Suite(.serialized) @Suite(.serialized)
final class ParserTests { final class ParserTests {
let fishHistoryFile = Bundle.module.path(forResource: "fish_history_test", ofType: "txt") let fishHistoryFile = Bundle.module.path(forResource: "fish_history_test", ofType: "txt")
let historyItem = FishHistoryEntry(cmd: "cd Projects/Fishee/", when: 1727545693, paths: ["Projects/Fishee/"]) let historyItem = FishHistoryEntry(
let historyItem2 = FishHistoryEntry(cmd: "swift package tools-version", when: 1727545709, paths: []) cmd: "cd Projects/Fishee/", when: 1_727_545_693, paths: ["Projects/Fishee/"])
let filePathforWriteTest = FileManager.default.homeDirectoryForCurrentUser.appendingPathComponent("myfile.txt") let historyItem2 = FishHistoryEntry(
let filePathforFileBackupTest = FileManager.default.homeDirectoryForCurrentUser.appendingPathComponent("myfile_copy.txt") cmd: "swift package tools-version", when: 1_727_545_709, paths: [])
let filePathforWriteTest = FileManager.default.homeDirectoryForCurrentUser.appendingPathComponent(
deinit { "myfile.txt")
if FileManager.default.fileExists(atPath: filePathforWriteTest.path) { let filePathforFileBackupTest = FileManager.default.homeDirectoryForCurrentUser
_ = try? FileManager.default.removeItem(at: filePathforWriteTest) .appendingPathComponent("myfile_copy.txt")
}
if FileManager.default.fileExists(atPath: filePathforFileBackupTest.path) {
_ = try? FileManager.default.removeItem(at: filePathforFileBackupTest)
}
}
@Test func parseFishHistoryTest() { deinit {
#expect(fishHistoryFile != nil) if FileManager.default.fileExists(atPath: filePathforWriteTest.path) {
let fishHistory = parseFishHistory(from: fishHistoryFile!) _ = try? FileManager.default.removeItem(at: filePathforWriteTest)
#expect(fishHistory!.count > 0)
let expectedHistory = [historyItem, historyItem2]
#expect(fishHistory == expectedHistory)
} }
if FileManager.default.fileExists(atPath: filePathforFileBackupTest.path) {
@Test func writeFishHistoryTest() { _ = try? FileManager.default.removeItem(at: filePathforFileBackupTest)
let written = writeFishHistory(
to: filePathforWriteTest.path,
history: [historyItem],
backup: false
)
#expect(written)
let fileContent = try? String(contentsOf: filePathforWriteTest, encoding: .utf8)
let expectedEntry = """
- cmd: cd Projects/Fishee/
when: 1727545693
paths:
- Projects/Fishee/
"""
#expect(fileContent == expectedEntry)
// confirm backup functionality is working
#expect(FileManager.default.fileExists(atPath: filePathforWriteTest.path))
let write_again = writeFishHistory(
to: filePathforWriteTest.path,
history: [historyItem],
backup: true
)
#expect(write_again)
#expect(FileManager.default.fileExists(atPath: filePathforFileBackupTest.path))
}
@Test func mergeFishHistoryTest() {
let merged = mergeFishHistory([historyItem], [historyItem2])
#expect(merged.count == 2)
#expect(merged.contains(historyItem))
#expect(merged.contains(historyItem2))
}
@Test func mergeFishHistoryWithDuplicateTest() {
let merged = mergeFishHistory([historyItem], [historyItem, historyItem2])
#expect(merged.count == 3)
#expect(merged.contains(historyItem))
#expect(merged.contains(historyItem2))
} }
}
@Test func mergeFishHistoryRemoveDuplicateTest() { @Test func parseFishHistoryTest() {
let merged = mergeFishHistory([historyItem], [historyItem, historyItem2], removeDuplicates: true) #expect(fishHistoryFile != nil)
#expect(merged.count == 2) let fishHistory = parseFishHistory(from: fishHistoryFile!)
#expect(merged.contains(historyItem)) #expect(fishHistory!.count > 0)
#expect(merged.contains(historyItem2)) let expectedHistory = [historyItem, historyItem2]
} #expect(fishHistory == expectedHistory)
}
@Test func writeFishHistoryTest() {
let written = writeFishHistory(
to: filePathforWriteTest.path,
history: [historyItem],
backup: false
)
#expect(written)
let fileContent = try? String(contentsOf: filePathforWriteTest, encoding: .utf8)
let expectedEntry = """
- cmd: cd Projects/Fishee/
when: 1727545693
paths:
- Projects/Fishee/
"""
#expect(fileContent == expectedEntry)
// confirm backup functionality is working
#expect(FileManager.default.fileExists(atPath: filePathforWriteTest.path))
let write_again = writeFishHistory(
to: filePathforWriteTest.path,
history: [historyItem],
backup: true
)
#expect(write_again)
#expect(FileManager.default.fileExists(atPath: filePathforFileBackupTest.path))
}
@Test func mergeFishHistoryTest() {
let merged = mergeFishHistory([historyItem], [historyItem2])
#expect(merged.count == 2)
#expect(merged.contains(historyItem))
#expect(merged.contains(historyItem2))
}
@Test func mergeFishHistoryWithDuplicateTest() {
let merged = mergeFishHistory([historyItem], [historyItem, historyItem2])
#expect(merged.count == 3)
#expect(merged.contains(historyItem))
#expect(merged.contains(historyItem2))
}
@Test func mergeFishHistoryRemoveDuplicateTest() {
let merged = mergeFishHistory(
[historyItem], [historyItem, historyItem2], removeDuplicates: true)
#expect(merged.count == 2)
#expect(merged.contains(historyItem))
#expect(merged.contains(historyItem2))
}
} }