diff --git a/Sources/HummingbirdDocC/Internal/Extensions/String+Formats.swift b/Sources/HummingbirdDocC/Internal/Extensions/String+Formats.swift index d425b2b..8c7dd24 100644 --- a/Sources/HummingbirdDocC/Internal/Extensions/String+Formats.swift +++ b/Sources/HummingbirdDocC/Internal/Extensions/String+Formats.swift @@ -31,6 +31,8 @@ extension String { static let forwardSlash = "%@/" /// A format pattern used to generate relative paths for index files. static let index = "%@/%@/index.html" + /// A format pattern used to generate relative paths for resources. + static let resource = "/%@/%@" /// A format pattern used to generate relative paths that starts with the `/` string. static let root = "/%@" } diff --git a/Sources/HummingbirdDocC/Internal/Models/Resource.swift b/Sources/HummingbirdDocC/Internal/Models/Resource.swift new file mode 100644 index 0000000..ee765e5 --- /dev/null +++ b/Sources/HummingbirdDocC/Internal/Models/Resource.swift @@ -0,0 +1,55 @@ +// ===----------------------------------------------------------------------=== +// +// This source file is part of the Hummingbird DocC Middleware open source project +// +// Copyright (c) 2025 Röck+Cöde VoF. and the Hummingbird DocC Middleware project authors +// Licensed under the EUPL 1.2 or later. +// +// See LICENSE for license information +// See CONTRIBUTORS for the list of Hummingbird DocC Middleware project authors +// +// ===----------------------------------------------------------------------=== + +/// A model that encapsulates the information related to a resource in a given `DocC` documentation archive. +struct Resource { + + // MARK: Properties + + /// An archive name in which the resource belongs to. + let archiveName: String + + /// A relative URI path to the resource. + let relativePath: String + + // MARK: Initializers + + /// Initializes this resource. + /// - Parameters: + /// - archiveName: An archive name in which the resource belongs to. + /// - relativePath: A relative URI path to the resource. + init( + archiveName: String, + relativePath: String + ) { + self.archiveName = archiveName + self.relativePath = relativePath + } + + // MARK: Computed + + /// A relative URI path to a documentation archive the resource belongs to. + lazy var archivePath: String = { + .init(format: .Format.Path.archive, archiveName) + }() + + /// A reference name for the documentation archive the resource belongs to. + lazy var archiveReference: String = { + archiveName.lowercased() + }() + + /// A relative URI path to the resource in its documentation archive. + lazy var fullPath: String = { + .init(format: .Format.Path.archive, archiveName, relativePath) + }() + +} diff --git a/Tests/HummingbirdDocC/Tests/Internal/Models/ResourceTests.swift b/Tests/HummingbirdDocC/Tests/Internal/Models/ResourceTests.swift new file mode 100644 index 0000000..1a96374 --- /dev/null +++ b/Tests/HummingbirdDocC/Tests/Internal/Models/ResourceTests.swift @@ -0,0 +1,147 @@ +// ===----------------------------------------------------------------------=== +// +// This source file is part of the Hummingbird DocC Middleware open source project +// +// Copyright (c) 2025 Röck+Cöde VoF. and the Hummingbird DocC Middleware project authors +// Licensed under the EUPL 1.2 or later. +// +// See LICENSE for license information +// See CONTRIBUTORS for the list of Hummingbird DocC Middleware project authors +// +// ===----------------------------------------------------------------------=== + +import Testing + +@testable import HummingbirdDocC.Resource + +@Suite("Resource", .tags(.model)) +struct ResourceTests { + + // MARK: Properties tests + +#if swift(>=6.2) + @Test + func `archive path`() { + assertArchivePath( + archiveName: "SomeDocument", + expects: "/SomeDocument.doccarchive" + ) + } + + @Test + func `archive reference`() { + assertArchiveReference( + archiveName: "SomeDocument", + expects: "somedocument" + ) + } + + @Test + func `full path`() { + assertFullPath()( + archiveName: "SomeDocument", + relativePath: .uriResource, + expects: "/somedocument" + .uriResource + ) + } +#else + @Test("archive path") + func archivePath() { + assertArchivePath( + archiveName: "SomeDocument", + expects: "/SomeDocument.doccarchive" + ) + } + + @Test("archive reference") + func archiveReference() { + assertArchiveReference( + archiveName: "SomeDocument", + expects: "somedocument" + ) + } + + @Test("full path") + func fullPath() { + assertFullPath()( + archiveName: "SomeDocument", + relativePath: .uriResource, + expects: "/somedocument" + .uriResource + ) + } +#endif + +} + +// MARK: - Assertions + +private extension ResourceTests { + + // MARK: Functions + + /// Asserts the `archivePath` computed property of a resource. + /// - Parameters: + /// - archiveName: A name of the archive the resource belongs to. + /// - archivePath: An expected path to a documentation archive related to a given archive name. + func assertArchivePath( + archiveName: String, + expects archivePath: String + ) { + // GIVEN + let resource = Resource( + archiveName: archiveName, + relativePath: .empty + ) + + // WHEN + let result = resource.archivePath + + // THEN + #expect(result == archivePath) + } + + /// Asserts the `archiveReference` computed property of a resource. + /// - Parameters: + /// - archiveName: A name of the archive the resource belongs to. + /// - archiveReference: An expected reference related to a given archive name. + func assertArchiveReference( + archiveName: String, + expects archiveReference: String + ) { + // GIVEN + let resource = Resource( + archiveName: archiveName, + relativePath: .empty + ) + + // WHEN + let result = resource.archiveReference + + // THEN + #expect(result == archiveReference) + } + + /// Asserts the `fullPath` computed property of a resource. + /// - Parameters: + /// - archiveName: A name of the archive the resource belongs to. + /// - relativePath: A relative URI path to a resource. + /// - fullPath: An expected relative URI path to a resource in its documentation archive. + func assertFullPath( + archiveName: String, + relativePath: String, + expects fullPath: String + ) { + // GIVEN + let resource = Resource( + archiveName: archiveName, + relativePath: relativePath + ) + + // WHEN + let result = resource.fullPath + + // THEN + #expect(result == fullPath) + } + +} diff --git a/Tests/HummingbirdDocC/Types/Extensions/Tag+Constants.swift b/Tests/HummingbirdDocC/Types/Extensions/Tag+Constants.swift index 31b3733..3ec4dc1 100644 --- a/Tests/HummingbirdDocC/Types/Extensions/Tag+Constants.swift +++ b/Tests/HummingbirdDocC/Types/Extensions/Tag+Constants.swift @@ -22,6 +22,8 @@ extension Tag { @Tag static var `extension`: Self /// Tag that indicate a test case for a middleware type. @Tag static var middleware: Self + /// Tag that indicate a test case for a model type. + @Tag static var model: Self /// Tag that indicate a test case for a use case type. @Tag static var useCase: Self