diff --git a/Sources/DiscogsService/Internal/Errors/InputValidationError.swift b/Sources/DiscogsService/Internal/Errors/InputValidationError.swift new file mode 100644 index 000000000..e4299b690 --- /dev/null +++ b/Sources/DiscogsService/Internal/Errors/InputValidationError.swift @@ -0,0 +1,21 @@ +// ===----------------------------------------------------------------------=== +// +// This source file is part of the DiscogsService open source project +// +// Copyright (c) 2025 Röck+Cöde VoF. and the DiscogsService project authors +// Licensed under Apache license v2.0 +// +// See LICENSE for license information +// See CONTRIBUTORS for the list of DiscogsService project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +// ===----------------------------------------------------------------------=== + +/// A representation of all the possible validation error that could be thrown while validating an input. +enum InputValidationError: Error { + /// An input is empty. + case inputIsEmpty + /// An input is nil. + case inputIsNil +} diff --git a/Sources/DiscogsService/Internal/Extensions/String+Constants.swift b/Sources/DiscogsService/Internal/Extensions/String+Constants.swift index 3d2af2654..852fa6a4c 100644 --- a/Sources/DiscogsService/Internal/Extensions/String+Constants.swift +++ b/Sources/DiscogsService/Internal/Extensions/String+Constants.swift @@ -31,6 +31,8 @@ extension String { static let authConsumer = "Discogs \(String.Parameter.key)=%@, \(String.Parameter.secret)=%@" /// A format for the user authentication header. static let authUser = "Discogs \(String.Parameter.token)=%@" + /// A format for the user agent header. + static let userAgent = "%@/%@ +%@" } } diff --git a/Sources/DiscogsService/Internal/Protocols/InputValidationRule.swift b/Sources/DiscogsService/Internal/Protocols/InputValidationRule.swift new file mode 100644 index 000000000..e82ad8953 --- /dev/null +++ b/Sources/DiscogsService/Internal/Protocols/InputValidationRule.swift @@ -0,0 +1,34 @@ +// ===----------------------------------------------------------------------=== +// +// This source file is part of the DiscogsService open source project +// +// Copyright (c) 2025 Röck+Cöde VoF. and the DiscogsService project authors +// Licensed under Apache license v2.0 +// +// See LICENSE for license information +// See CONTRIBUTORS for the list of DiscogsService project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +// ===----------------------------------------------------------------------=== + +/// A protocol that defines an input validation rule to be applied to an input by the ``ValidateInputUseCase`` use case. +protocol InputValidationRule { + + // MARK: Functions + +#if swift(>=6.0) + /// Validates a given input against a validation rule. + /// - Parameter input: An input to be validated. + /// - Returns: A flag that indicates whether an input has been validated or not. + /// - Throws: An error of type ``InputValidationError`` in case a given input failed a validation. + @discardableResult func validate(_ input: String?) throws(InputValidationError) -> Bool +#else + /// Validates a given input against a validation rule. + /// - Parameter input: An input to be validated. + /// - Returns: A flag that indicates whether an input has been validated or not. + /// - Throws: An error of type ``InputValidationError`` in case a given input failed a validation. + @discardableResult func validate(_ input: String?) throws -> Bool +#endif + +} diff --git a/Sources/DiscogsService/Internal/Use Cases/ValidateInputUseCase.swift b/Sources/DiscogsService/Internal/Use Cases/ValidateInputUseCase.swift new file mode 100644 index 000000000..85186c92d --- /dev/null +++ b/Sources/DiscogsService/Internal/Use Cases/ValidateInputUseCase.swift @@ -0,0 +1,53 @@ +// ===----------------------------------------------------------------------=== +// +// This source file is part of the DiscogsService open source project +// +// Copyright (c) 2025 Röck+Cöde VoF. and the DiscogsService project authors +// Licensed under Apache license v2.0 +// +// See LICENSE for license information +// See CONTRIBUTORS for the list of DiscogsService project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +// ===----------------------------------------------------------------------=== + +/// A use case that validates an input against a set of validation rules. +struct ValidateInputUseCase { + + // MARK: Properties + + /// A list of validation rules to match an input against. + private let rules: [any InputValidationRule] + + // MARK: Initializers + + /// Initializes this use case. + /// - Parameter rules: A list of validation rules to match an input against. + init(rules: any InputValidationRule...) { + self.rules = rules + } + + // MARK: Functions + +#if swift(>=6.0) + /// Validates an input against a set of validation rules. + /// - Parameter input: An input to be validated against a set of rules, if any. + /// - Throws: An error of type ``InputValidationError`` in case an input failed any validation. + func callAsFunction(_ input: String?) throws(InputValidationError) { + for rule in rules { + try rule.validate(input) + } + } +#else + /// Validates an input against a set of validation rules. + /// - Parameter input: An input to be validated against a set of rules, if any. + /// - Throws: An error of type ``InputValidationError`` in case an input failed any validation. + func callAsFunction(_ input: String?) throws { + for rule in rules { + try rule.validate(input) + } + } +#endif + +} diff --git a/Sources/DiscogsService/Internal/Validation Rules/NotEmptyValidationRule.swift b/Sources/DiscogsService/Internal/Validation Rules/NotEmptyValidationRule.swift new file mode 100644 index 000000000..c27491872 --- /dev/null +++ b/Sources/DiscogsService/Internal/Validation Rules/NotEmptyValidationRule.swift @@ -0,0 +1,63 @@ +// ===----------------------------------------------------------------------=== +// +// This source file is part of the DiscogsService open source project +// +// Copyright (c) 2025 Röck+Cöde VoF. and the DiscogsService project authors +// Licensed under Apache license v2.0 +// +// See LICENSE for license information +// See CONTRIBUTORS for the list of DiscogsService project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +// ===----------------------------------------------------------------------=== + +/// A validation rule type that checks whether an input is empty or not. +struct NotEmptyValidationRule: InputValidationRule { + + // MARK: Functions + +#if swift(>=6.0) + func validate(_ input: String?) throws(InputValidationError) -> Bool { + try validate(input: input) + } +#else + func validate(_ input: String?) throws -> Bool { + try validate(input: input) + } +#endif + +} + +// MARK: - Helpers + +private extension NotEmptyValidationRule { + + // MARK: Functions + + /// Validates a given input. + /// + /// > note: This helper function would not be necessary when support for *Swift 5.10* is discontinued. + /// + /// - Parameter input: An input to be validated. + /// - Returns: A flag that indicates whether a given input has been validated or not. + /// - Throws: An error of type ``InputValidatorError`` in case the validation failed. + func validate(input: String?) throws -> Bool { + guard let input else { + return false + } + guard !input.isEmpty else { + throw InputValidationError.inputIsEmpty + } + + return true + } + +} + +// MARK: - Constants + +extension InputValidationRule where Self == NotEmptyValidationRule { + /// A validation rule that checks whether an input is empty or not. + static var notEmpty: Self { .init() } +} diff --git a/Sources/DiscogsService/Internal/Validation Rules/NotNilValidationRule.swift b/Sources/DiscogsService/Internal/Validation Rules/NotNilValidationRule.swift new file mode 100644 index 000000000..1b9472e0d --- /dev/null +++ b/Sources/DiscogsService/Internal/Validation Rules/NotNilValidationRule.swift @@ -0,0 +1,60 @@ +// ===----------------------------------------------------------------------=== +// +// This source file is part of the DiscogsService open source project +// +// Copyright (c) 2025 Röck+Cöde VoF. and the DiscogsService project authors +// Licensed under Apache license v2.0 +// +// See LICENSE for license information +// See CONTRIBUTORS for the list of DiscogsService project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +// ===----------------------------------------------------------------------=== + +/// A validation rule type that checks whether an input is nil or not. +struct NotNilValidationRule: InputValidationRule { + + // MARK: Functions + +#if swift(>=6.0) + func validate(_ input: String?) throws(InputValidationError) -> Bool { + try validate(input: input) + } +#else + func validate(_ input: String?) throws -> Bool { + try validate(input: input) + } +#endif + +} + +// MARK: - Helpers + +private extension NotNilValidationRule { + + // MARK: Functions + + /// Validates a given input. + /// + /// > note: This helper function would not be necessary when support for *Swift 5.10* is discontinued. + /// + /// - Parameter input: An input to be validated. + /// - Returns: A flag that indicates whether a given input has been validated or not. + /// - Throws: An error of type ``InputValidatorError`` in case the validation failed. + func validate(input: String?) throws -> Bool { + guard let input else { + throw InputValidationError.inputIsNil + } + + return true + } + +} + +// MARK: - Constants + +extension InputValidationRule where Self == NotNilValidationRule { + /// A validation rule that checks whether an input is nil or not. + static var notNil: Self { .init() } +} diff --git a/Sources/DiscogsService/Public/Models/Product.swift b/Sources/DiscogsService/Public/Models/Product.swift new file mode 100644 index 000000000..b52d52b59 --- /dev/null +++ b/Sources/DiscogsService/Public/Models/Product.swift @@ -0,0 +1,48 @@ +// ===----------------------------------------------------------------------=== +// +// This source file is part of the DiscogsService open source project +// +// Copyright (c) 2025 Röck+Cöde VoF. and the DiscogsService project authors +// Licensed under Apache license v2.0 +// +// See LICENSE for license information +// See CONTRIBUTORS for the list of DiscogsService project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +// ===----------------------------------------------------------------------=== + +import Foundation + +/// A type that represents a product that uses the ``Client`` client. +public struct Product: Sendable { + + // MARK: Properties + + /// A camel-cased name of a product. + let name: String + + /// A URI link related to a product. + let url: String + + /// A semantic version of a product. + let version: String + + // MARK: Initializers + + /// Initializes this model. + /// - Parameters: + /// - name: A camel-cased name of a product. + /// - version: A URI link related to a product. + /// - url: A semantic version of a product. + public init( + name: String, + version: String, + url: String + ) { + self.name = name + self.url = url + self.version = version + } + +} diff --git a/Tests/DiscogsService/Cases/Internal/Use Cases/ValidateInputUesCaseTests.swift b/Tests/DiscogsService/Cases/Internal/Use Cases/ValidateInputUesCaseTests.swift new file mode 100644 index 000000000..631fd12ff --- /dev/null +++ b/Tests/DiscogsService/Cases/Internal/Use Cases/ValidateInputUesCaseTests.swift @@ -0,0 +1,97 @@ +// ===----------------------------------------------------------------------=== +// +// This source file is part of the DiscogsService open source project +// +// Copyright (c) 2025 Röck+Cöde VoF. and the DiscogsService project authors +// Licensed under Apache license v2.0 +// +// See LICENSE for license information +// See CONTRIBUTORS for the list of DiscogsService project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +// ===----------------------------------------------------------------------=== + +import Testing + +@testable import DiscogsService + +@Suite("Validate Input Use Cases") +struct ValidateInputUseCaseTests { + + // MARK: Functions + +#if swift(>=6.2) + @Test(arguments: zip( + Input.inputsToValidate, + Output.inputsToValidate + )) func `validate`( + input: String?, + expects error: InputValidationError? + ) async throws { + try assertValidate( + input: input, + expects: error + ) + } +#else + @Test("validate", arguments: zip( + Input.inputsToValidate, + Output.inputsToValidate + )) func validate( + input: String?, + expects error: InputValidationError? + ) async throws { + try assertValidate( + input: input, + expects: error + ) + } +#endif + +} + +// MARK: - Assertions + +private extension ValidateInputUseCaseTests { + + // MARK: Functions + + /// Asserts an input validation of a ``ValidateInputUseCase`` use case. + /// - Parameters: + /// - input: An input to validate, if any. + /// - error: An expected error, if any. + /// - Throws: An error of type ``InputValidationError`` in case of an unexpected test case scenario. + func assertValidate( + input: String?, + expects error: InputValidationError? + ) throws { + // GIVEN + let validate = ValidateInputUseCase(rules: .notNil, .notEmpty) + + // WHEN + // THEN + if let error { + #expect(throws: error) { + try validate(input) + } + } else { + #expect(throws: Never.self) { + try validate(input) + } + } + } + +} + +// MARK: - Constants + +private extension Input { + /// A list of inputs to validate against a set of validation rules. + static let inputsToValidate: [String?] = [nil, .empty, "SomeInput"] +} + +private extension Output { + /// A list of expected input validation errors to be thrown (if necessary). + static let inputsToValidate: [InputValidationError?] = [.inputIsNil, .inputIsEmpty, nil] +} diff --git a/Tests/DiscogsService/Cases/Public/Middlewares/UserAgentMiddlewareTests.swift b/Tests/DiscogsService/Cases/Public/Middlewares/UserAgentMiddlewareTests.swift new file mode 100644 index 000000000..a4c0b9a47 --- /dev/null +++ b/Tests/DiscogsService/Cases/Public/Middlewares/UserAgentMiddlewareTests.swift @@ -0,0 +1,25 @@ +// ===----------------------------------------------------------------------=== +// +// This source file is part of the DiscogsService open source project +// +// Copyright (c) 2025 Röck+Cöde VoF. and the DiscogsService project authors +// Licensed under Apache license v2.0 +// +// See LICENSE for license information +// See CONTRIBUTORS for the list of DiscogsService project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +// ===----------------------------------------------------------------------=== + +import DiscogsService +import Testing + +@Suite("User Agent Middleware", .tags(.middleware)) +struct UserAgentMiddlewareTests { + +// @Test func <#test function name#>() async throws { +// // Write your test here and use APIs like `#expect(...)` to check expected conditions. +// } + +}