From 39a9523ac926aa311936faa3a58242c03c1181a2 Mon Sep 17 00:00:00 2001 From: Javier Cicchelli Date: Sat, 8 Mar 2025 00:57:59 +0100 Subject: [PATCH] Renamed the Path+Functions extension in the library target as PathProvider. --- .../Internal/Extensions/Path+Functions.swift | 49 ---------- .../Internal/Extensions/String+Formats.swift | 2 + .../Sources/Internal/Protocols/Pathable.swift | 2 +- .../Internal/Providers/PathProvider.swift | 57 ++++++++++++ .../Providers/PathProviderTests.swift | 93 +++++++++++++++++++ 5 files changed, 153 insertions(+), 50 deletions(-) delete mode 100644 Library/Sources/Internal/Extensions/Path+Functions.swift create mode 100644 Library/Sources/Internal/Providers/PathProvider.swift create mode 100644 Test/Sources/Cases/Internal/Providers/PathProviderTests.swift diff --git a/Library/Sources/Internal/Extensions/Path+Functions.swift b/Library/Sources/Internal/Extensions/Path+Functions.swift deleted file mode 100644 index 51fe991..0000000 --- a/Library/Sources/Internal/Extensions/Path+Functions.swift +++ /dev/null @@ -1,49 +0,0 @@ -enum Path { - - // MARK: Functions - - static func archivePath(from path: String) -> String { - let pathComponents = path.split(separator: .forwardSlash) - - return pathComponents.count > 1 - ? .init(format: .Format.archiveDocC, String(pathComponents[1])) - : .empty - } - - static func archiveName(from path: String) -> String { - let pathComponents = path.split(separator: .forwardSlash) - - return pathComponents.count > 1 - ? .init(pathComponents[1]).lowercased() - : .empty - } - - static func resourcePath(from path: String) -> String { - let matches = path.matches(of: /\//) - - return matches.count > 2 - ? .init(path[matches[2].startIndex...]) - : .forwardSlash - } - -} - -// MARK: - String+Constants - -extension String { - static let empty = "" - static let forwardSlash = "/" - static let previousFolder = ".." -} - -// MARK: - Character+Constants - -private extension Character { - static let forwardSlash: Character = "/" -} - -// MARK: - String+Formats - -private extension String.Format { - static let archiveDocC = "/%@.doccarchive" -} diff --git a/Library/Sources/Internal/Extensions/String+Formats.swift b/Library/Sources/Internal/Extensions/String+Formats.swift index 61faa98..e64d658 100644 --- a/Library/Sources/Internal/Extensions/String+Formats.swift +++ b/Library/Sources/Internal/Extensions/String+Formats.swift @@ -3,6 +3,8 @@ extension String { enum Format { /// A namespace that defines the format patterns used to generate relative path representations. enum Path { + /// A representation of the format pattern used to generate relative paths that starts with the `/` string and finishes with the `.doccarchive` string. + static let archive = "/%@.doccarchive" /// A representation of the format pattern used to generate relative paths that starts with the `/data` string. static let data = "/data/%@" /// A representation of the format pattern used to generate relative paths that starts with the `/docs` string. diff --git a/Library/Sources/Internal/Protocols/Pathable.swift b/Library/Sources/Internal/Protocols/Pathable.swift index d84a6f4..f550cbf 100644 --- a/Library/Sources/Internal/Protocols/Pathable.swift +++ b/Library/Sources/Internal/Protocols/Pathable.swift @@ -1,4 +1,4 @@ -/// A protocol that provides a relative path representation. +/// A type that provides a relative path representation. protocol Pathable { // MARK: Properties diff --git a/Library/Sources/Internal/Providers/PathProvider.swift b/Library/Sources/Internal/Providers/PathProvider.swift new file mode 100644 index 0000000..96076e9 --- /dev/null +++ b/Library/Sources/Internal/Providers/PathProvider.swift @@ -0,0 +1,57 @@ +/// A type that provides data extraction and manipulation functions from relative path representations. +enum PathProvider { + + // MARK: Functions + + /// Extracts the root *DocC* archive relative path from a given URL relative path. + /// + /// Given that the provided URL relative path is expected to have the prefix `/archives/archive-name`, then a root *DocC* archive generated out of the + /// archive name obtained from the URL relative path is returned. Otherwise, an empty string is returned. + /// + /// - Parameter path: A URL relative path from where to extract data. + /// - Returns: A URL relative path to a root *DocC* archive or an empty string . + static func archivePath(from path: String) -> String { + let pathComponents = path.split(separator: .forwardSlash) + + return pathComponents.count > 1 + ? .init(format: .Format.Path.archive, String(pathComponents[1])) + : .empty + } + + /// Extracts the name of the *DocC* archive from a given URL relative path. + /// + /// Given that the provided URL relative path is expected to have the prefix `/archives/archive-name`, then a lowercased archive name from the URL + /// relative path is returned. Otherwise, an empty string is returned. + /// + /// - Parameter path: A URL relative path from where to extract data. + /// - Returns: A lowercased name of the archive or an empty string. + static func archiveName(from path: String) -> String { + let pathComponents = path.split(separator: .forwardSlash) + + return pathComponents.count > 1 + ? .init(pathComponents[1]).lowercased() + : .empty + } + + /// Extracts the name of the *DocC* archive from a given URL relative path. + /// + /// Given that the provided URL relative path is expected to have the format `/archives/archive-name/path/to/resource`, then the path to a + /// resource extracted from the URL relative path is returned. Otherwise, a forward slash string is returned. + /// + /// - Parameter path: A URL relative path from where to extract data. + /// - Returns: A URL relative path to a resource or a forward slash. + static func resourcePath(from path: String) -> String { + let matches = path.matches(of: /\//) + + return matches.count > 2 + ? .init(path[matches[2].startIndex...]) + : .forwardSlash + } + +} + +// MARK: - Character+Constants + +private extension Character { + static let forwardSlash: Character = "/" +} diff --git a/Test/Sources/Cases/Internal/Providers/PathProviderTests.swift b/Test/Sources/Cases/Internal/Providers/PathProviderTests.swift new file mode 100644 index 0000000..5a51d1e --- /dev/null +++ b/Test/Sources/Cases/Internal/Providers/PathProviderTests.swift @@ -0,0 +1,93 @@ +import Testing + +@testable import AppLibrary + +@Suite("PathProvider", .tags(.provider)) +struct PathProviderTests { + + // MARK: Functions tests + + @Test(arguments: zip([String].pathURLs, [String].nameArchives)) + func archiveName( + from path: String, + expects name: String + ) async throws { + // GIVEN + // WHEN + let result = PathProvider.archiveName(from: path) + + // THEN + #expect(result == name) + } + + @Test(arguments: zip([String].pathURLs, [String].pathArchives)) + func archivePath( + from path: String, + expects archive: String + ) async throws { + // GIVEN + // WHEN + let result = PathProvider.archivePath(from: path) + + // THEN + #expect(result == archive) + } + + @Test(arguments: zip([String].pathURLs, [String].pathResources)) + func resourcePath( + from path: String, + expects resource: String + ) async throws { + // GIVEN + // WHEN + let result = PathProvider.resourcePath(from: path) + + // THEN + #expect(result == resource) + } + +} + +// MARK: - Collection+Strings + +private extension Collection where Element == String { + + // MARK: Properties + + static var nameArchives: [Element] {[ + .empty, + .empty, + .empty, + "somearchive", + "somearchive", + "somearchive" + ]} + + static var pathArchives: [Element] {[ + .empty, + .empty, + .empty, + "/SomeArchive.doccarchive", + "/SomeArchive.doccarchive", + "/SomeArchive.doccarchive" + ]} + + static var pathResources: [Element] {[ + .forwardSlash, + .forwardSlash, + .forwardSlash, + .forwardSlash, + .forwardSlash, + "/path/to/resource" + ]} + + static var pathURLs: [Element] {[ + .empty, + "/archives", + "/archives/", + "/archives/SomeArchive", + "/archives/SomeArchive/", + "/archives/SomeArchive/path/to/resource" + ]} + +}