Skip to content

Commit

Permalink
Fixes to FileRepository to get it to compile and pass tests.
Browse files Browse the repository at this point in the history
  • Loading branch information
drewmccormack committed Dec 15, 2024
1 parent 717770a commit d6c0616
Show file tree
Hide file tree
Showing 4 changed files with 236 additions and 89 deletions.
94 changes: 71 additions & 23 deletions Sources/Forked/FileRepository.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public final class FileRepository: Repository {
guard let subdirectories = try? FileManager.default.contentsOfDirectory(at: rootDirectory, includingPropertiesForKeys: nil, options: .skipsHiddenFiles) else {
return []
}
return subdirectories.filter { $0.hasDirectoryPath }.map { Fork($0.lastPathComponent) }
return subdirectories.filter { $0.hasDirectoryPath }.map { Fork(name: $0.lastPathComponent) }
}

public func create(_ fork: Fork, withInitialCommit commit: Commit<Data>) throws {
Expand All @@ -35,45 +35,93 @@ public final class FileRepository: Repository {
try FileManager.default.removeItem(at: forkDirectory)
}

public func versions(storedIn fork: Fork) throws -> Set<Version> {
let forkDirectory = rootDirectory.appendingPathComponent(fork.name)
guard FileManager.default.fileExists(atPath: forkDirectory.path) else {
throw Error.attemptToAccessNonExistentFork(fork)
}
let files = try FileManager.default.contentsOfDirectory(at: forkDirectory, includingPropertiesForKeys: nil, options: .skipsHiddenFiles)
return Set(files.map { Version($0.lastPathComponent) })
private struct VersionMetadata: Codable {
let count: UInt64
let timestamp: Date
}

private func metadataURL(for version: Version, in fork: Fork) -> URL {
rootDirectory
.appendingPathComponent(fork.name)
.appendingPathComponent("\(version.count).metadata")
}

private func dataURL(for version: Version, in fork: Fork) -> URL {
rootDirectory
.appendingPathComponent(fork.name)
.appendingPathComponent(String(version.count))
}

public func removeCommit(at version: Version, from fork: Fork) throws {
public func content(of fork: Fork, at version: Version) throws -> CommitContent<Data> {
let forkDirectory = rootDirectory.appendingPathComponent(fork.name)
let fileURL = forkDirectory.appendingPathComponent(version.id)
guard FileManager.default.fileExists(atPath: fileURL.path) else {
let dataURL = forkDirectory.appendingPathComponent(String(version.count))
let metadataURL = forkDirectory.appendingPathComponent("\(version.count).metadata")

guard FileManager.default.fileExists(atPath: metadataURL.path) else {
throw Error.attemptToAccessNonExistentVersion(version, fork)
}
try FileManager.default.removeItem(at: fileURL)

if FileManager.default.fileExists(atPath: dataURL.path) {
return .resource(try Data(contentsOf: dataURL))
} else {
return .none
}
}

public func content(of fork: Fork, at version: Version) throws -> Data {
public func store(_ commit: Commit<Data>, in fork: Fork) throws {
let forkDirectory = rootDirectory.appendingPathComponent(fork.name)
let fileURL = forkDirectory.appendingPathComponent(version.id)
guard FileManager.default.fileExists(atPath: fileURL.path) else {
throw Error.attemptToAccessNonExistentVersion(version, fork)
guard FileManager.default.fileExists(atPath: forkDirectory.path) else {
throw Error.attemptToAccessNonExistentFork(fork)
}

let dataURL = forkDirectory.appendingPathComponent(String(commit.version.count))
let metadataURL = forkDirectory.appendingPathComponent("\(commit.version.count).metadata")

guard !FileManager.default.fileExists(atPath: metadataURL.path) else {
throw Error.attemptToReplaceExistingVersion(commit.version, fork)
}

// Save metadata
let metadata = VersionMetadata(count: commit.version.count, timestamp: commit.version.timestamp)
let encoder = JSONEncoder()
try encoder.encode(metadata).write(to: metadataURL)

// Save content if it's not .none
if case .resource(let data) = commit.content {
try data.write(to: dataURL)
}
return try Data(contentsOf: fileURL)
}

public func store(_ commit: Commit<Data>, in fork: Fork) throws {
public func versions(storedIn fork: Fork) throws -> Set<Version> {
let forkDirectory = rootDirectory.appendingPathComponent(fork.name)
guard FileManager.default.fileExists(atPath: forkDirectory.path) else {
throw Error.attemptToAccessNonExistentFork(fork)
}
let fileURL = forkDirectory.appendingPathComponent(commit.version.id)
guard !FileManager.default.fileExists(atPath: fileURL.path) else {
throw Error.attemptToReplaceExistingVersion(commit.version, fork)

let files = try FileManager.default.contentsOfDirectory(at: forkDirectory, includingPropertiesForKeys: nil, options: .skipsHiddenFiles)

let decoder = JSONDecoder()
return Set(files.compactMap { url in
guard url.pathExtension == "metadata" else { return nil }
guard let metadata = try? decoder.decode(VersionMetadata.self, from: Data(contentsOf: url)) else { return nil }
return Version(count: metadata.count, timestamp: metadata.timestamp)
})
}

public func removeCommit(at version: Version, from fork: Fork) throws {
let forkDirectory = rootDirectory.appendingPathComponent(fork.name)
let dataURL = forkDirectory.appendingPathComponent(String(version.count))
let metadataURL = forkDirectory.appendingPathComponent("\(version.count).metadata")

guard FileManager.default.fileExists(atPath: metadataURL.path) else {
throw Error.attemptToAccessNonExistentVersion(version, fork)
}

try FileManager.default.removeItem(at: metadataURL)
if FileManager.default.fileExists(atPath: dataURL.path) {
try FileManager.default.removeItem(at: dataURL)
}
try commit.content.write(to: fileURL)
}


private func createDirectoryIfNeeded(at url: URL) throws {
if !FileManager.default.fileExists(atPath: url.path) {
Expand Down
6 changes: 3 additions & 3 deletions Sources/Forked/Fork.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Foundation

/// A type representing a named fork.
/// A type representing a named fork.
public struct Fork: Hashable, Codable, Sendable {
/// The name of the fork
public let name: String
Expand All @@ -14,9 +14,9 @@ public struct Fork: Hashable, Codable, Sendable {
self.name = name
}

/// The only fork created by default. All other forkes
/// The only fork created by default. All other forks
/// can be merged with the main, but not directly with
/// each other.. It acts as the central hub of the
/// each other. It acts as the central hub of the
/// wheel of forks.
public static let main = Fork(name: "main")
}
Expand Down
162 changes: 162 additions & 0 deletions Tests/ForkedTests/FileRepository.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
import Foundation
import Testing
@testable import Forked

struct FileRepositorySuite {
@Test func createAndListForks() throws {
let tempDirectory = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString)
defer { try? FileManager.default.removeItem(at: tempDirectory) }

let repository = try FileRepository(rootDirectory: tempDirectory)

// Initially no forks
#expect(repository.forks.isEmpty)

// Create a fork
let fork = Fork(name: "test")
let initialCommit = Commit(content: .resource(Data()), version: .initialVersion)
try repository.create(fork, withInitialCommit: initialCommit)

// Check fork exists
#expect(repository.forks.count == 1)
#expect(repository.forks.first?.name == "test")
}

@Test func storeAndRetrieveContent() throws {
let tempDirectory = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString)
defer { try? FileManager.default.removeItem(at: tempDirectory) }

let repository = try FileRepository(rootDirectory: tempDirectory)
let fork = Fork(name: "test")
let initialCommit = Commit(content: .resource(Data()), version: .initialVersion)
try repository.create(fork, withInitialCommit: initialCommit)

// Store new content
let testData = "Hello, World!".data(using: .utf8)!
let version = Version(count: 1, timestamp: .now)
let commit = Commit(content: .resource(testData), version: version)
try repository.store(commit, in: fork)

// Retrieve and verify content
let retrieved = try repository.content(of: fork, at: version)
#expect(retrieved == .resource(testData))
}

@Test func versionListing() throws {
let tempDirectory = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString)
defer { try? FileManager.default.removeItem(at: tempDirectory) }

let repository = try FileRepository(rootDirectory: tempDirectory)
let fork = Fork(name: "test")
let initialCommit = Commit(content: .resource(Data()), version: .initialVersion)
try repository.create(fork, withInitialCommit: initialCommit)

// Check initial version
var versions = try repository.versions(storedIn: fork)
#expect(versions.count == 1)
#expect(versions.contains(.initialVersion))

// Add another version
let version = Version(count: 1, timestamp: .now)
let commit = Commit(content: .resource(Data()), version: version)
try repository.store(commit, in: fork)

// Check both versions exist
versions = try repository.versions(storedIn: fork)
#expect(versions.count == 2)
#expect(versions.contains(.initialVersion))
#expect(versions.contains(version))
}

@Test func deleteFork() throws {
let tempDirectory = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString)
defer { try? FileManager.default.removeItem(at: tempDirectory) }

let repository = try FileRepository(rootDirectory: tempDirectory)
let fork = Fork(name: "test")
let initialCommit = Commit(content: .resource(Data()), version: .initialVersion)
try repository.create(fork, withInitialCommit: initialCommit)

#expect(repository.forks.count == 1)
try repository.delete(fork)
#expect(repository.forks.isEmpty)
}

@Test func removeCommit() throws {
let tempDirectory = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString)
defer { try? FileManager.default.removeItem(at: tempDirectory) }

let repository = try FileRepository(rootDirectory: tempDirectory)
let fork = Fork(name: "test")
let initialCommit = Commit(content: .resource(Data()), version: .initialVersion)
try repository.create(fork, withInitialCommit: initialCommit)

let version = Version(count: 1, timestamp: .now)
let commit = Commit(content: .resource(Data()), version: version)
try repository.store(commit, in: fork)

var versions = try repository.versions(storedIn: fork)
#expect(versions.count == 2)

try repository.removeCommit(at: version, from: fork)

versions = try repository.versions(storedIn: fork)
#expect(versions.count == 1)
#expect(versions.contains(.initialVersion))
}

@Test func noneContent() throws {
let tempDirectory = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString)
defer { try? FileManager.default.removeItem(at: tempDirectory) }

let repository = try FileRepository(rootDirectory: tempDirectory)
let fork = Fork(name: "test")
let initialCommit = Commit<Data>(content: .none, version: .initialVersion)
try repository.create(fork, withInitialCommit: initialCommit)

let version = Version(count: 1, timestamp: .now)
let commit = Commit<Data>(content: .none, version: version)
try repository.store(commit, in: fork)

let retrieved = try repository.content(of: fork, at: version)
#expect(retrieved == .none)
}

@Test func errorConditions() throws {
let tempDirectory = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString)
defer { try? FileManager.default.removeItem(at: tempDirectory) }

let repository = try FileRepository(rootDirectory: tempDirectory)
let fork = Fork(name: "test")
let nonExistentFork = Fork(name: "nonexistent")
let version = Version(count: 1, timestamp: .now)

// Accessing non-existent fork
#expect(throws: Error.self) {
try repository.versions(storedIn: nonExistentFork)
}
#expect(throws: Error.self) {
try repository.content(of: nonExistentFork, at: version)
}

let initialCommit = Commit(content: .resource(Data()), version: .initialVersion)
try repository.create(fork, withInitialCommit: initialCommit)

// Duplicate fork creation
#expect(throws: Error.self) {
try repository.create(fork, withInitialCommit: initialCommit)
}

// Accessing non-existent version
#expect(throws: Error.self) {
try repository.content(of: fork, at: version)
}

// Store commit and try to replace it
let commit = Commit(content: .resource(Data()), version: version)
try repository.store(commit, in: fork)
#expect(throws: Error.self) {
try repository.store(commit, in: fork)
}
}
}
63 changes: 0 additions & 63 deletions Tests/ForkedTests/FileRepositoryTest.swift

This file was deleted.

0 comments on commit d6c0616

Please sign in to comment.