diff --git a/.github/workflows/build_and_test_packages.yml b/.github/workflows/build_and_test_packages.yml
new file mode 100644
index 0000000..e2532c2
--- /dev/null
+++ b/.github/workflows/build_and_test_packages.yml
@@ -0,0 +1,11 @@
+name: Swift Package Manager
+
+on:
+ pull_request:
+ branches:
+ - main
+ - develop
+
+jobs:
+ use-reusable:
+ uses: EasyPackages/.github/.github/workflows/build_and_test_packages.yml@main
\ No newline at end of file
diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/EasyCore.xcscheme b/.swiftpm/xcode/xcshareddata/xcschemes/EasyCore.xcscheme
new file mode 100644
index 0000000..211dd0f
--- /dev/null
+++ b/.swiftpm/xcode/xcshareddata/xcschemes/EasyCore.xcscheme
@@ -0,0 +1,84 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/EasyCore.xctestplan b/EasyCore.xctestplan
new file mode 100644
index 0000000..f198ec2
--- /dev/null
+++ b/EasyCore.xctestplan
@@ -0,0 +1,33 @@
+{
+ "configurations" : [
+ {
+ "id" : "CF935032-BC1F-44BE-B770-A175A1872A51",
+ "name" : "Test Scheme Action",
+ "options" : {
+
+ }
+ }
+ ],
+ "defaultOptions" : {
+ "codeCoverage" : {
+ "targets" : [
+ {
+ "containerPath" : "container:",
+ "identifier" : "EasyCore",
+ "name" : "EasyCore"
+ }
+ ]
+ },
+ "testExecutionOrdering" : "random"
+ },
+ "testTargets" : [
+ {
+ "target" : {
+ "containerPath" : "container:",
+ "identifier" : "EasyCoreTests",
+ "name" : "EasyCoreTests"
+ }
+ }
+ ],
+ "version" : 1
+}
diff --git a/Sources/EasyCore/Collection/ArrayExtension.swift b/Sources/EasyCore/Collection/ArrayExtension.swift
new file mode 100644
index 0000000..7c3b1fd
--- /dev/null
+++ b/Sources/EasyCore/Collection/ArrayExtension.swift
@@ -0,0 +1,58 @@
+import Foundation
+
+public extension Array {
+
+ ///
+ /// Safely accesses an element at the specified index.
+ ///
+ /// Returns the element at the given index, or `nil` if the index is out of bounds.
+ ///
+ /// Use this subscript to avoid runtime crashes caused by out-of-range index access, especially when working with dynamic indices.
+ ///
+ /// - Parameter index: The index of the desired element.
+ /// - Returns: The element at the given index, or `nil` if the index is invalid.
+ ///
+ /// ### Example:
+ /// ```swift
+ /// let names = ["Anna", "Brian", "Carlos"]
+ /// names[safe: 1] // "Brian"
+ /// names[safe: 5] // nil
+ /// ```
+ ///
+ subscript(safe index: Int) -> Element? {
+ indices.contains(index) ? self[index] : nil
+ }
+
+ ///
+ /// Splits the array into multiple subarrays ("chunks") with a maximum specified size.
+ ///
+ /// Each subarray will contain at most `size` elements. The last chunk may contain fewer elements if the total count is not a multiple of `size`.
+ ///
+ /// - Parameter size: The maximum size of each chunk. Must be greater than zero.
+ /// - Returns: An array of subarrays containing the original elements in order.
+ ///
+ /// ### Example:
+ /// ```swift
+ /// let numbers = [1, 2, 3, 4, 5, 6, 7]
+ /// let chunks = numbers.chunked(by: 3)
+ /// // Result: [[1, 2, 3], [4, 5, 6], [7]]
+ /// ```
+ ///
+ /// - Note: If `size <= 0`, the result will be an empty array (`[]`).
+ ///
+ func chunked(by size: Int) -> [[Element]] {
+ guard size > 0 else { return [] }
+
+ var chunks: [[Element]] = []
+ var currentIndex = 0
+
+ while currentIndex < count {
+ let endIndex = Swift.min(currentIndex + size, count)
+ let chunk = Array(self[currentIndex.. = []
+ return filter { seen.insert($0).inserted }
+ }
+}
diff --git a/Sources/EasyCore/Collection/CollectionExtension.swift b/Sources/EasyCore/Collection/CollectionExtension.swift
new file mode 100644
index 0000000..0c267ed
--- /dev/null
+++ b/Sources/EasyCore/Collection/CollectionExtension.swift
@@ -0,0 +1,28 @@
+import Foundation
+
+public extension Collection {
+
+ ///
+ /// Checks whether a given optional collection is either `nil` or empty.
+ ///
+ /// This static method is a convenient utility for validating optional collections in a safe and concise way.
+ ///
+ /// - Parameter collection: An optional collection of the same type as `Self`.
+ /// - Returns: `true` if the collection is `nil` or contains no elements; otherwise, `false`.
+ ///
+ /// ### Example:
+ /// ```swift
+ /// let items: [String]? = nil
+ /// Collection.isNilOrEmpty(items) // true
+ ///
+ /// let emptyList: [Int]? = []
+ /// Collection.isNilOrEmpty(emptyList) // true
+ ///
+ /// let numbers: [Int]? = [1, 2, 3]
+ /// Collection.isNilOrEmpty(numbers) // false
+ /// ```
+ ///
+ static func isNilOrEmpty(_ collection: Self?) -> Bool {
+ collection?.isEmpty ?? true
+ }
+}
diff --git a/Sources/EasyCore/Debouncer.swift b/Sources/EasyCore/Debouncer.swift
new file mode 100644
index 0000000..b9758db
--- /dev/null
+++ b/Sources/EasyCore/Debouncer.swift
@@ -0,0 +1,58 @@
+import Foundation
+
+///
+/// A utility class that delays the execution of a block of code until a specified time interval has passed since the last call.
+///
+/// Useful for scenarios where you want to limit the number of times a function is executed,
+/// such as reacting to user input, search queries, or other high-frequency events.
+///
+/// Each call to `call(_:)` resets the timer. The block will only be executed if no further calls are made within the delay interval.
+///
+public final class Debouncer {
+
+ ///
+ /// The delay interval before the block is executed.
+ ///
+ private let delay: TimeInterval
+
+ ///
+ /// The currently scheduled work item, if any.
+ ///
+ private var workItem: DispatchWorkItem?
+
+ ///
+ /// Initializes a new `Debouncer` with the specified delay interval.
+ ///
+ /// - Parameter delay: The time interval (in seconds) to wait after the last call before executing the block.
+ ///
+ /// ### Example:
+ /// ```swift
+ /// let debouncer = Debouncer(delay: 0.3)
+ /// ```
+ ///
+ public init(delay: TimeInterval) {
+ self.delay = delay
+ }
+
+ ///
+ /// Schedules a block to be executed after the specified delay.
+ ///
+ /// If this method is called again before the delay elapses, the previous block is cancelled and the timer resets.
+ ///
+ /// - Parameter block: The closure to execute after the delay.
+ ///
+ /// ### Example:
+ /// ```swift
+ /// debouncer.call {
+ /// print("This will only print if no other call occurs in the next 0.3 seconds.")
+ /// }
+ /// ```
+ ///
+ public func call(_ block: @escaping () -> Void) {
+ workItem?.cancel()
+ workItem = DispatchWorkItem(block: block)
+ if let workItem = workItem {
+ DispatchQueue.main.asyncAfter(deadline: .now() + delay, execute: workItem)
+ }
+ }
+}
diff --git a/Sources/EasyCore/Decoder/DecodableDataExtension.swift b/Sources/EasyCore/Decoder/DecodableDataExtension.swift
new file mode 100644
index 0000000..5148722
--- /dev/null
+++ b/Sources/EasyCore/Decoder/DecodableDataExtension.swift
@@ -0,0 +1,36 @@
+import Foundation
+
+public extension Decodable where Self == Data {
+
+ ///
+ /// Decodes the current `Data` instance into a specified `Decodable` type.
+ ///
+ /// This method provides a convenient way to decode JSON data directly into a model object using a provided or default `JSONDecoder`.
+ ///
+ /// - Parameters:
+ /// - type: The `Decodable` type to decode from the data.
+ /// - decoder: An optional `JSONDecoder` to use. Defaults to `.standard`.
+ /// - Returns: An instance of the decoded type `T` if decoding is successful; otherwise, `nil`.
+ ///
+ /// ### Example:
+ /// ```swift
+ /// struct User: Decodable {
+ /// let name: String
+ /// let age: Int
+ /// }
+ ///
+ /// let jsonData = """
+ /// {
+ /// "name": "Letícia",
+ /// "age": 28
+ /// }
+ /// """.data(using: .utf8)!
+ ///
+ /// let user = jsonData.decode(User.self)
+ /// // user is of type `User?`
+ /// ```
+ ///
+ func decode(_ type: T.Type, decoder: JSONDecoder = .standard) -> T? {
+ try? decoder.decode(T.self, from: self)
+ }
+}
diff --git a/Sources/EasyCore/Decoder/DecodableStringExtension.swift b/Sources/EasyCore/Decoder/DecodableStringExtension.swift
new file mode 100644
index 0000000..39f2fbb
--- /dev/null
+++ b/Sources/EasyCore/Decoder/DecodableStringExtension.swift
@@ -0,0 +1,39 @@
+import Foundation
+
+public extension Decodable where Self == String {
+
+ ///
+ /// Decodes the current `String` instance, assumed to be a JSON string, into a specified `Decodable` type.
+ ///
+ /// This method first attempts to convert the string to UTF-8 encoded `Data`, then decodes it into the desired type using the provided or default `JSONDecoder`.
+ ///
+ /// - Parameters:
+ /// - type: The `Decodable` type you want to decode from the JSON string.
+ /// - decoder: An optional `JSONDecoder` to use for decoding. Defaults to `.standard`.
+ /// - Returns: An instance of the decoded type `T` if decoding is successful; otherwise, `nil`.
+ ///
+ /// ### Example:
+ /// ```swift
+ /// struct User: Decodable {
+ /// let name: String
+ /// let age: Int
+ /// }
+ ///
+ /// let json = """
+ /// {
+ /// "name": "Letícia",
+ /// "age": 28
+ /// }
+ /// """
+ ///
+ /// let user = json.decode(User.self)
+ /// // user is of type `User?`
+ /// ```
+ ///
+ /// - Note: This method fails silently and returns `nil` if the string cannot be converted to UTF-8 `Data` or if decoding fails.
+ ///
+ func decode(_ type: T.Type, decoder: JSONDecoder = .standard) -> T? {
+ guard let data = self.data(using: .utf8) else { return nil }
+ return data.decode(T.self, decoder: decoder)
+ }
+}
diff --git a/Sources/EasyCore/Decoder/JSONDecoderExtension.swift b/Sources/EasyCore/Decoder/JSONDecoderExtension.swift
new file mode 100644
index 0000000..a60f8ee
--- /dev/null
+++ b/Sources/EasyCore/Decoder/JSONDecoderExtension.swift
@@ -0,0 +1,122 @@
+import Foundation
+
+///
+/// A set of convenient static and instance properties to create preconfigured `JSONDecoder` instances
+/// with commonly used strategies for decoding keys and dates.
+///
+public extension JSONDecoder {
+
+ ///
+ /// A standard `JSONDecoder` instance with default decoding strategies.
+ ///
+ /// This is the base decoder without any custom strategies applied.
+ ///
+ /// ### Example:
+ /// ```swift
+ /// let decoder = JSONDecoder.standard
+ /// ```
+ ///
+ @inlinable static var standard: JSONDecoder {
+ JSONDecoder()
+ }
+
+ ///
+ /// A `JSONDecoder` configured to decode snake_case keys into camelCase properties.
+ ///
+ /// Equivalent to setting `.keyDecodingStrategy = .convertFromSnakeCase`.
+ ///
+ /// ### Example:
+ /// ```swift
+ /// let decoder = JSONDecoder.snakeCase
+ /// ```
+ ///
+ static var snakeCase: JSONDecoder {
+ standard.snakeCase
+ }
+
+ ///
+ /// A `JSONDecoder` configured to decode ISO 8601 date strings.
+ ///
+ /// Equivalent to setting `.dateDecodingStrategy = .iso8601`.
+ ///
+ /// ### Example:
+ /// ```swift
+ /// let decoder = JSONDecoder.iso8601
+ /// ```
+ ///
+ static var iso8601: JSONDecoder {
+ standard.iso8601
+ }
+
+ ///
+ /// A `JSONDecoder` configured to decode dates as seconds since 1970 (Unix timestamp).
+ ///
+ /// Equivalent to setting `.dateDecodingStrategy = .secondsSince1970`.
+ ///
+ /// ### Example:
+ /// ```swift
+ /// let decoder = JSONDecoder.secondsSince1970
+ /// ```
+ ///
+ static var secondsSince1970: JSONDecoder {
+ standard.secondsSince1970
+ }
+
+ ///
+ /// Creates a `JSONDecoder` configured to decode dates using a custom `DateFormatter`.
+ ///
+ /// - Parameter formatter: The `DateFormatter` used to parse date strings.
+ /// - Returns: A configured `JSONDecoder` instance.
+ ///
+ /// ### Example:
+ /// ```swift
+ /// let formatter = DateFormatter()
+ /// formatter.dateFormat = "yyyy-MM-dd"
+ /// let decoder = JSONDecoder.formatted(using: formatter)
+ /// ```
+ ///
+ static func formatted(using formatter: DateFormatter) -> JSONDecoder {
+ standard.formatted(using: formatter)
+ }
+
+ ///
+ /// Applies snake_case key decoding to the current decoder instance.
+ ///
+ /// - Returns: The same decoder with `.convertFromSnakeCase` strategy applied.
+ ///
+ var snakeCase: JSONDecoder {
+ keyDecodingStrategy = .convertFromSnakeCase
+ return self
+ }
+
+ ///
+ /// Applies ISO 8601 date decoding to the current decoder instance.
+ ///
+ /// - Returns: The same decoder with `.iso8601` date strategy applied.
+ ///
+ var iso8601: JSONDecoder {
+ dateDecodingStrategy = .iso8601
+ return self
+ }
+
+ ///
+ /// Applies Unix timestamp (seconds since 1970) date decoding to the current decoder instance.
+ ///
+ /// - Returns: The same decoder with `.secondsSince1970` date strategy applied.
+ ///
+ var secondsSince1970: JSONDecoder {
+ dateDecodingStrategy = .secondsSince1970
+ return self
+ }
+
+ ///
+ /// Applies a custom `DateFormatter` for decoding dates to the current decoder instance.
+ ///
+ /// - Parameter formatter: The `DateFormatter` to use.
+ /// - Returns: The same decoder with `.formatted(formatter)` date decoding strategy applied.
+ ///
+ func formatted(using formatter: DateFormatter) -> JSONDecoder {
+ dateDecodingStrategy = .formatted(formatter)
+ return self
+ }
+}
diff --git a/Sources/EasyCore/Encoder/EncodableExtension.swift b/Sources/EasyCore/Encoder/EncodableExtension.swift
new file mode 100644
index 0000000..803c518
--- /dev/null
+++ b/Sources/EasyCore/Encoder/EncodableExtension.swift
@@ -0,0 +1,68 @@
+import Foundation
+
+///
+/// Utility extensions for `Encodable` types to simplify JSON encoding and conversions to common formats.
+///
+public extension Encodable {
+
+ ///
+ /// Encodes the current instance into `Data` using the specified `JSONEncoder`.
+ ///
+ /// - Parameter encoder: The `JSONEncoder` to use. Defaults to `.standard`.
+ /// - Returns: A `Data` representation of the encoded object, or `nil` if encoding fails.
+ ///
+ /// ### Example:
+ /// ```swift
+ /// struct User: Encodable {
+ /// let name: String
+ /// let age: Int
+ /// }
+ ///
+ /// let user = User(name: "Letícia", age: 28)
+ /// let data = user.encode()
+ /// ```
+ ///
+ func encode(encoder: JSONEncoder = .standard) -> Data? {
+ try? encoder.encode(self)
+ }
+
+ ///
+ /// Encodes the current instance into a JSON-formatted `String`.
+ ///
+ /// - Parameter prettyPrinted: If `true`, the output will be formatted with line breaks and indentation. Defaults to `false`.
+ /// - Returns: A JSON `String` representation of the object, or `nil` if encoding fails.
+ ///
+ /// ### Example:
+ /// ```swift
+ /// let user = User(name: "Letícia", age: 28)
+ /// let json = user.encodeToString(prettyPrinted: true)
+ /// ```
+ ///
+ func encodeToString(prettyPrinted: Bool = false) -> String? {
+ let encoder = JSONEncoder()
+ if prettyPrinted {
+ encoder.outputFormatting = [.prettyPrinted, .sortedKeys]
+ }
+ guard let data = try? encoder.encode(self) else { return nil }
+ return String(data: data, encoding: .utf8)
+ }
+
+ ///
+ /// Converts the current instance into a `[String: Any]` dictionary using `JSONSerialization`.
+ ///
+ /// This property encodes the instance to `Data`, then deserializes it back into a dictionary.
+ ///
+ /// - Returns: A dictionary representation of the object, or `nil` if encoding or serialization fails.
+ ///
+ /// ### Example:
+ /// ```swift
+ /// let user = User(name: "Letícia", age: 28)
+ /// let dict = user.dictionary
+ /// // dict = ["name": "Letícia", "age": 28]
+ /// ```
+ ///
+ var dictionary: [String: Any]? {
+ guard let data = encode() else { return nil }
+ return (try? JSONSerialization.jsonObject(with: data)) as? [String: Any]
+ }
+}
diff --git a/Sources/EasyCore/Encoder/JSONEncoderExtension.swift b/Sources/EasyCore/Encoder/JSONEncoderExtension.swift
new file mode 100644
index 0000000..73b0cf2
--- /dev/null
+++ b/Sources/EasyCore/Encoder/JSONEncoderExtension.swift
@@ -0,0 +1,122 @@
+import Foundation
+
+///
+/// A set of convenient static and instance properties to create preconfigured `JSONEncoder`
+/// instances with commonly used strategies for encoding keys and dates.
+///
+public extension JSONEncoder {
+
+ ///
+ /// A standard `JSONEncoder` instance with default encoding strategies.
+ ///
+ /// This encoder uses the default key and date encoding strategies.
+ ///
+ /// ### Example:
+ /// ```swift
+ /// let encoder = JSONEncoder.standard
+ /// ```
+ ///
+ @inlinable static var standard: JSONEncoder {
+ JSONEncoder()
+ }
+
+ ///
+ /// A `JSONEncoder` configured to convert camelCase keys to snake_case when encoding.
+ ///
+ /// Equivalent to setting `.keyEncodingStrategy = .convertToSnakeCase`.
+ ///
+ /// ### Example:
+ /// ```swift
+ /// let encoder = JSONEncoder.snakeCase
+ /// ```
+ ///
+ static var snakeCase: JSONEncoder {
+ standard.snakeCase
+ }
+
+ ///
+ /// A `JSONEncoder` configured to encode dates using ISO 8601 format.
+ ///
+ /// Equivalent to setting `.dateEncodingStrategy = .iso8601`.
+ ///
+ /// ### Example:
+ /// ```swift
+ /// let encoder = JSONEncoder.iso8601
+ /// ```
+ ///
+ static var iso8601: JSONEncoder {
+ standard.iso8601
+ }
+
+ ///
+ /// A `JSONEncoder` configured to encode dates as seconds since 1970 (Unix timestamp).
+ ///
+ /// Equivalent to setting `.dateEncodingStrategy = .secondsSince1970`.
+ ///
+ /// ### Example:
+ /// ```swift
+ /// let encoder = JSONEncoder.secondsSince1970
+ /// ```
+ ///
+ static var secondsSince1970: JSONEncoder {
+ standard.secondsSince1970
+ }
+
+ ///
+ /// Creates a `JSONEncoder` configured to encode dates using a custom `DateFormatter`.
+ ///
+ /// - Parameter formatter: The `DateFormatter` to use when encoding `Date` values.
+ /// - Returns: A configured `JSONEncoder` instance.
+ ///
+ /// ### Example:
+ /// ```swift
+ /// let formatter = DateFormatter()
+ /// formatter.dateFormat = "yyyy-MM-dd"
+ /// let encoder = JSONEncoder.formatted(using: formatter)
+ /// ```
+ ///
+ static func formatted(using formatter: DateFormatter) -> JSONEncoder {
+ standard.formatted(using: formatter)
+ }
+
+ ///
+ /// Applies the `.convertToSnakeCase` key encoding strategy to the current encoder instance.
+ ///
+ /// - Returns: The same encoder instance with `.convertToSnakeCase` applied.
+ ///
+ var snakeCase: JSONEncoder {
+ keyEncodingStrategy = .convertToSnakeCase
+ return self
+ }
+
+ ///
+ /// Applies the `.iso8601` date encoding strategy to the current encoder instance.
+ ///
+ /// - Returns: The same encoder instance with `.iso8601` applied.
+ ///
+ var iso8601: JSONEncoder {
+ dateEncodingStrategy = .iso8601
+ return self
+ }
+
+ ///
+ /// Applies the `.secondsSince1970` date encoding strategy to the current encoder instance.
+ ///
+ /// - Returns: The same encoder instance with `.secondsSince1970` applied.
+ ///
+ var secondsSince1970: JSONEncoder {
+ dateEncodingStrategy = .secondsSince1970
+ return self
+ }
+
+ ///
+ /// Applies a custom `DateFormatter` to encode date values in the current encoder instance.
+ ///
+ /// - Parameter formatter: The `DateFormatter` to use.
+ /// - Returns: The same encoder instance with the custom formatter applied.
+ ///
+ func formatted(using formatter: DateFormatter) -> JSONEncoder {
+ dateEncodingStrategy = .formatted(formatter)
+ return self
+ }
+}
diff --git a/Sources/EasyCore/MainThread.swift b/Sources/EasyCore/MainThread.swift
new file mode 100644
index 0000000..9879c1b
--- /dev/null
+++ b/Sources/EasyCore/MainThread.swift
@@ -0,0 +1,49 @@
+import Foundation
+
+///
+/// A utility namespace that provides safe and convenient methods for executing code on the main thread.
+///
+public enum MainThread {
+
+ ///
+ /// Executes a block of code on the main thread, immediately if already on it, or asynchronously if not.
+ ///
+ /// This ensures thread-safety when updating UI or performing main-thread-bound operations from a background thread.
+ ///
+ /// - Parameter block: A `@Sendable` closure to execute on the main thread.
+ ///
+ /// ### Example:
+ /// ```swift
+ /// MainThread.asyncSafe {
+ /// self.label.text = "Updated safely"
+ /// }
+ /// ```
+ ///
+ public static func asyncSafe(_ block: @Sendable @escaping () -> Void) {
+ if Thread.isMainThread {
+ block()
+ } else {
+ DispatchQueue.main.async { [block] in
+ block()
+ }
+ }
+ }
+
+ ///
+ /// Executes a block of code on the main thread after a specified delay.
+ ///
+ /// - Parameters:
+ /// - seconds: The delay in seconds before the block is executed.
+ /// - block: A `@Sendable` closure to execute after the delay.
+ ///
+ /// ### Example:
+ /// ```swift
+ /// MainThread.asyncAfter(seconds: 1.0) {
+ /// print("Executed after 1 second on the main thread")
+ /// }
+ /// ```
+ ///
+ public static func asyncAfter(seconds: TimeInterval, _ block: @Sendable @escaping () -> Void) {
+ DispatchQueue.main.asyncAfter(deadline: .now() + seconds, execute: block)
+ }
+}
diff --git a/Sources/EasyCore/Number/DoubleExtension.swift b/Sources/EasyCore/Number/DoubleExtension.swift
new file mode 100644
index 0000000..c09c130
--- /dev/null
+++ b/Sources/EasyCore/Number/DoubleExtension.swift
@@ -0,0 +1,65 @@
+import Foundation
+
+public extension Double {
+
+ ///
+ /// Indicates whether the value is greater than zero.
+ ///
+ /// - Returns: `true` if the value is positive; otherwise, `false`.
+ ///
+ /// ### Example:
+ /// ```swift
+ /// let value = 3.14
+ /// value.isPositive // true
+ /// ```
+ ///
+ var isPositive: Bool {
+ self > 0
+ }
+
+ ///
+ /// Indicates whether the value is less than zero.
+ ///
+ /// - Returns: `true` if the value is negative; otherwise, `false`.
+ ///
+ /// ### Example:
+ /// ```swift
+ /// let value = -7.5
+ /// value.isNegative // true
+ /// ```
+ ///
+ var isNegative: Bool {
+ self < 0
+ }
+
+ ///
+ /// Indicates whether the value is exactly zero.
+ ///
+ /// - Returns: `true` if the value is zero; otherwise, `false`.
+ ///
+ /// ### Example:
+ /// ```swift
+ /// let value = 0.0
+ /// value.isZero // true
+ /// ```
+ ///
+ var isZero: Bool {
+ self == 0
+ }
+
+ ///
+ /// Checks whether the current value falls within the specified closed range.
+ ///
+ /// - Parameter range: A closed range (`ClosedRange`) to check against.
+ /// - Returns: `true` if the value is within the range (inclusive); otherwise, `false`.
+ ///
+ /// ### Example:
+ /// ```swift
+ /// let temperature = 22.5
+ /// temperature.isInRange(20.0...25.0) // true
+ /// ```
+ ///
+ func isInRange(_ range: ClosedRange) -> Bool {
+ range.contains(self)
+ }
+}
diff --git a/Sources/EasyCore/Number/FloatExtension.swift b/Sources/EasyCore/Number/FloatExtension.swift
new file mode 100644
index 0000000..3a18959
--- /dev/null
+++ b/Sources/EasyCore/Number/FloatExtension.swift
@@ -0,0 +1,65 @@
+import Foundation
+
+public extension Float {
+
+ ///
+ /// Indicates whether the value is greater than zero.
+ ///
+ /// - Returns: `true` if the value is positive; otherwise, `false`.
+ ///
+ /// ### Example:
+ /// ```swift
+ /// let value: Float = 3.14
+ /// value.isPositive // true
+ /// ```
+ ///
+ var isPositive: Bool {
+ self > 0
+ }
+
+ ///
+ /// Indicates whether the value is less than zero.
+ ///
+ /// - Returns: `true` if the value is negative; otherwise, `false`.
+ ///
+ /// ### Example:
+ /// ```swift
+ /// let value: Float = -2.7
+ /// value.isNegative // true
+ /// ```
+ ///
+ var isNegative: Bool {
+ self < 0
+ }
+
+ ///
+ /// Indicates whether the value is exactly zero.
+ ///
+ /// - Returns: `true` if the value is zero; otherwise, `false`.
+ ///
+ /// ### Example:
+ /// ```swift
+ /// let value: Float = 0.0
+ /// value.isZero // true
+ /// ```
+ ///
+ var isZero: Bool {
+ self == 0
+ }
+
+ ///
+ /// Checks whether the current value falls within the specified closed range.
+ ///
+ /// - Parameter range: A closed range (`ClosedRange`) to check against.
+ /// - Returns: `true` if the value is within the range (inclusive); otherwise, `false`.
+ ///
+ /// ### Example:
+ /// ```swift
+ /// let score: Float = 7.5
+ /// score.isInRange(0.0...10.0) // true
+ /// ```
+ ///
+ func isInRange(_ range: ClosedRange) -> Bool {
+ range.contains(self)
+ }
+}
diff --git a/Sources/EasyCore/Number/IntExtension.swift b/Sources/EasyCore/Number/IntExtension.swift
new file mode 100644
index 0000000..0dda853
--- /dev/null
+++ b/Sources/EasyCore/Number/IntExtension.swift
@@ -0,0 +1,65 @@
+import Foundation
+
+public extension Int {
+
+ ///
+ /// Indicates whether the value is greater than zero.
+ ///
+ /// - Returns: `true` if the value is positive; otherwise, `false`.
+ ///
+ /// ### Example:
+ /// ```swift
+ /// let count = 5
+ /// count.isPositive // true
+ /// ```
+ ///
+ var isPositive: Bool {
+ self > 0
+ }
+
+ ///
+ /// Indicates whether the value is less than zero.
+ ///
+ /// - Returns: `true` if the value is negative; otherwise, `false`.
+ ///
+ /// ### Example:
+ /// ```swift
+ /// let delta = -3
+ /// delta.isNegative // true
+ /// ```
+ ///
+ var isNegative: Bool {
+ self < 0
+ }
+
+ ///
+ /// Indicates whether the value is exactly zero.
+ ///
+ /// - Returns: `true` if the value is zero; otherwise, `false`.
+ ///
+ /// ### Example:
+ /// ```swift
+ /// let total = 0
+ /// total.isZero // true
+ /// ```
+ ///
+ var isZero: Bool {
+ self == 0
+ }
+
+ ///
+ /// Checks whether the current value falls within the specified closed range.
+ ///
+ /// - Parameter range: A closed range (`ClosedRange`) to check against.
+ /// - Returns: `true` if the value is within the range (inclusive); otherwise, `false`.
+ ///
+ /// ### Example:
+ /// ```swift
+ /// let score = 85
+ /// score.isInRange(0...100) // true
+ /// ```
+ ///
+ func isInRange(_ range: ClosedRange) -> Bool {
+ range.contains(self)
+ }
+}
diff --git a/Sources/EasyCore/String/OptionalStringExtension.swift b/Sources/EasyCore/String/OptionalStringExtension.swift
new file mode 100644
index 0000000..471150d
--- /dev/null
+++ b/Sources/EasyCore/String/OptionalStringExtension.swift
@@ -0,0 +1,30 @@
+import Foundation
+
+public extension Optional where Wrapped == String {
+
+ ///
+ /// Indicates whether the optional string is `nil`, empty, or contains only whitespace and newline characters.
+ ///
+ /// This is useful for validating optional text input where both `nil` and `" "` should be treated as blank.
+ ///
+ /// - Returns: `true` if the string is `nil` or blank; otherwise, `false`.
+ ///
+ /// ### Example:
+ /// ```swift
+ /// let name: String? = nil
+ /// name.isBlank // true
+ ///
+ /// let email: String? = " "
+ /// email.isBlank // true
+ ///
+ /// let username: String? = "John"
+ /// username.isBlank // false
+ /// ```
+ ///
+ var isBlank: Bool {
+ switch self {
+ case .none: true
+ case .some(let value): value.isBlank
+ }
+ }
+}
diff --git a/Sources/EasyCore/String/StringExtension.swift b/Sources/EasyCore/String/StringExtension.swift
new file mode 100644
index 0000000..b300b25
--- /dev/null
+++ b/Sources/EasyCore/String/StringExtension.swift
@@ -0,0 +1,101 @@
+import Foundation
+
+public extension String {
+
+ ///
+ /// Removes all occurrences of the specified substring from the current string.
+ ///
+ /// - Parameter value: The substring to be removed.
+ /// - Returns: A new string with all instances of `value` removed.
+ ///
+ /// ### Example:
+ /// ```swift
+ /// let result = "123.45 BRL".removeOcurrencing("BRL")
+ /// // result == "123.45 "
+ /// ```
+ ///
+ func removeOcurrencing(_ value: String) -> String {
+ replacingOccurrences(of: value, with: String())
+ }
+
+ ///
+ /// Indicates whether the string contains only numeric characters.
+ ///
+ /// - Returns: `true` if the string is not blank and consists entirely of digits; otherwise, `false`.
+ ///
+ /// ### Example:
+ /// ```swift
+ /// "12345".isNumeric // true
+ /// "12a45".isNumeric // false
+ /// "".isNumeric // false
+ /// ```
+ ///
+ var isNumeric: Bool {
+ return !isBlank && allSatisfy { $0.isNumber }
+ }
+
+ ///
+ /// Indicates whether the string contains only alphabetic characters.
+ ///
+ /// - Returns: `true` if the string is not blank and consists entirely of letters; otherwise, `false`.
+ ///
+ /// ### Example:
+ /// ```swift
+ /// "Hello".isAlphabetic // true
+ /// "Hello1".isAlphabetic // false
+ /// " ".isAlphabetic // false
+ /// ```
+ ///
+ var isAlphabetic: Bool {
+ return !isBlank && allSatisfy { $0.isLetter }
+ }
+
+ ///
+ /// Indicates whether the string is empty or consists only of whitespace and newline characters.
+ ///
+ /// - Returns: `true` if the string is blank; otherwise, `false`.
+ ///
+ /// ### Example:
+ /// ```swift
+ /// "".isBlank // true
+ /// " \n".isBlank // true
+ /// "Hello".isBlank // false
+ /// ```
+ ///
+ var isBlank: Bool {
+ trimmed().isEmpty
+ }
+
+ ///
+ /// Returns a copy of the string with leading and trailing whitespace and newline characters removed.
+ ///
+ /// - Returns: A trimmed version of the string.
+ ///
+ /// ### Example:
+ /// ```swift
+ /// " Hello \n".trimmed() // "Hello"
+ /// ```
+ ///
+ func trimmed() -> String {
+ trimmingCharacters(in: .whitespacesAndNewlines)
+ }
+
+ ///
+ /// Indicates whether the string is in a valid email format.
+ ///
+ /// - Returns: `true` if the string matches a basic email pattern; otherwise, `false`.
+ ///
+ /// ### Example:
+ /// ```swift
+ /// "user@example.com".isEmail // true
+ /// "invalid-email.com".isEmail // false
+ /// ```
+ ///
+ var isEmail: Bool {
+ NSPredicate(
+ format: "SELF MATCHES %@",
+ #"^[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$"#
+ )
+ .evaluate(with: self)
+ }
+}
diff --git a/Sources/EasyCore/StringExtension.swift b/Sources/EasyCore/StringExtension.swift
deleted file mode 100644
index c76f585..0000000
--- a/Sources/EasyCore/StringExtension.swift
+++ /dev/null
@@ -1,5 +0,0 @@
-public extension String {
- func removeOcurrencing(_ value: String) -> String {
- replacingOccurrences(of: value, with: String())
- }
-}
diff --git a/Tests/EasyCoreTests/CollectionExtensionTests.swift b/Tests/EasyCoreTests/CollectionExtensionTests.swift
new file mode 100644
index 0000000..923e73c
--- /dev/null
+++ b/Tests/EasyCoreTests/CollectionExtensionTests.swift
@@ -0,0 +1,150 @@
+import Testing
+
+@testable import EasyCore
+
+@Suite("Collection")
+struct CollectionExtensionTests {
+ @Suite("Array")
+ struct ArrayTests {
+ @Test("[safe:]")
+ func sage() {
+ let a = [1, 2, 3]
+ #expect(a[safe: 0] == 1)
+ #expect(a[safe: 1] == 2)
+ #expect(a[safe: 2] == 3)
+ #expect(a[safe: 3] == nil)
+ }
+
+ @Suite(".chunked(by:)")
+ struct ChunkedTests {
+
+ @Test("Returns chunks of equal size when array size is divisible by chunk size")
+ func equalChunks() {
+ let input = [1, 2, 3, 4, 5, 6]
+ let result = input.chunked(by: 2)
+ #expect(result == [[1, 2], [3, 4], [5, 6]])
+ }
+
+ @Test("Returns last chunk with fewer elements when not divisible")
+ func unevenChunks() {
+ let input = [1, 2, 3, 4, 5]
+ let result = input.chunked(by: 2)
+ #expect(result == [[1, 2], [3, 4], [5]])
+ }
+
+ @Test("Returns single chunk when chunk size is greater than array count")
+ func chunkSizeGreaterThanArray() {
+ let input = [1, 2, 3]
+ let result = input.chunked(by: 10)
+ #expect(result == [[1, 2, 3]])
+ }
+
+ @Test("Returns empty array when chunk size is zero")
+ func zeroChunkSize() {
+ let input = [1, 2, 3]
+ let result = input.chunked(by: 0)
+ #expect(result.isEmpty)
+ }
+
+ @Test("Returns empty array when chunk size is negative")
+ func negativeChunkSize() {
+ let input = [1, 2, 3]
+ let result = input.chunked(by: -1)
+ #expect(result.isEmpty)
+ }
+
+ @Test("Returns empty array when input is empty")
+ func emptyArray() {
+ let input: [Int] = []
+ let result = input.chunked(by: 3)
+ #expect(result == [])
+ }
+
+ @Test("Returns correct number of chunks")
+ func numberOfChunks() {
+ let input = Array(1...10)
+ let result = input.chunked(by: 4)
+ #expect(result.count == 3)
+ }
+ }
+
+ @Suite(".uniqued")
+ struct UniquedTests {
+
+ @Test("Removes duplicated integers while preserving order")
+ func integerArray() {
+ let input = [1, 2, 2, 3, 1, 4]
+ let result = input.uniqued
+ #expect(result == [1, 2, 3, 4])
+ }
+
+ @Test("Removes duplicated strings while preserving order")
+ func stringArray() {
+ let input = ["apple", "banana", "apple", "orange"]
+ let result = input.uniqued
+ #expect(result == ["apple", "banana", "orange"])
+ }
+
+ @Test("Returns same array when all elements are unique")
+ func allUnique() {
+ let input = [1, 2, 3, 4, 5]
+ let result = input.uniqued
+ #expect(result == input)
+ }
+
+ @Test("Returns empty array when input is empty")
+ func emptyArray() {
+ let input: [Int] = []
+ let result = input.uniqued
+ #expect(result.isEmpty)
+ }
+
+ @Test("Removes repeated characters in order")
+ func characterArray() {
+ let input: [Character] = ["a", "b", "a", "c", "b"]
+ let result = input.uniqued
+ #expect(result == ["a", "b", "c"])
+ }
+ }
+ }
+
+ @Suite(".isNilOrEmpty")
+ struct IsNilOrEmptyTests {
+
+ @Test("Returns true for nil array")
+ func nilArray() {
+ let array: [Int]? = nil
+ #expect(Array.isNilOrEmpty(array))
+ }
+
+ @Test("Returns true for empty array")
+ func emptyArray() {
+ let array: [String]? = []
+ #expect(Array.isNilOrEmpty(array))
+ }
+
+ @Test("Returns false for non-empty array")
+ func nonEmptyArray() {
+ let array: [Int]? = [1, 2, 3]
+ #expect(!Array.isNilOrEmpty(array))
+ }
+
+ @Test("Returns true for nil set")
+ func nilSet() {
+ let set: Set? = nil
+ #expect(Set.isNilOrEmpty(set))
+ }
+
+ @Test("Returns true for empty set")
+ func emptySet() {
+ let set: Set? = []
+ #expect(Set.isNilOrEmpty(set))
+ }
+
+ @Test("Returns false for non-empty set")
+ func nonEmptySet() {
+ let set: Set? = ["A", "B"]
+ #expect(!Set.isNilOrEmpty(set))
+ }
+ }
+}
diff --git a/Tests/EasyCoreTests/Counter.swift b/Tests/EasyCoreTests/Counter.swift
new file mode 100644
index 0000000..d90f14a
--- /dev/null
+++ b/Tests/EasyCoreTests/Counter.swift
@@ -0,0 +1,5 @@
+actor Counter {
+ private var count = 0
+ func increment() { count += 1 }
+ func value() -> Int { count }
+}
diff --git a/Tests/EasyCoreTests/DebouncerTests.swift b/Tests/EasyCoreTests/DebouncerTests.swift
new file mode 100644
index 0000000..5f6417b
--- /dev/null
+++ b/Tests/EasyCoreTests/DebouncerTests.swift
@@ -0,0 +1,69 @@
+import Foundation
+import Testing
+
+@testable import EasyCore
+
+@Suite("Debouncer")
+struct DebouncerTests {
+
+ @Test("Executes block after delay")
+ func executesAfterDelay() async throws {
+ let debouncer = Debouncer(delay: 0.2)
+ var didRun = false
+
+ debouncer.call {
+ didRun = true
+ }
+
+ try await Task.sleep(nanoseconds: 300_000_000) // 0.3s
+ #expect(didRun)
+ }
+
+ @Test("Cancels previous block if called again before delay")
+ func cancelsPreviousBlock() async throws {
+ let debouncer = Debouncer(delay: 0.3)
+ var counter = 0
+
+ debouncer.call {
+ counter += 1
+ }
+
+ try await Task.sleep(nanoseconds: 100_000_000) // 0.1s
+
+ debouncer.call {
+ counter += 1
+ }
+
+ try await Task.sleep(nanoseconds: 400_000_000) // 0.4s
+ #expect(counter == 1)
+ }
+
+ @Test("Only last call executes when called multiple times rapidly")
+ func onlyLastCallExecutes() async throws {
+ let debouncer = Debouncer(delay: 0.2)
+ var values: [Int] = []
+
+ for i in 1...5 {
+ debouncer.call {
+ values.append(i)
+ }
+ try await Task.sleep(nanoseconds: 50_000_000) // 0.05s
+ }
+
+ try await Task.sleep(nanoseconds: 300_000_000) // wait for final call
+ #expect(values == [5])
+ }
+
+ @Test("Does not execute if delay has not elapsed")
+ func doesNotRunTooSoon() async throws {
+ let debouncer = Debouncer(delay: 0.5)
+ var didRun = false
+
+ debouncer.call {
+ didRun = true
+ }
+
+ try await Task.sleep(nanoseconds: 200_000_000) // 0.2s
+ #expect(didRun == false)
+ }
+}
diff --git a/Tests/EasyCoreTests/DecoderTests.swift b/Tests/EasyCoreTests/DecoderTests.swift
new file mode 100644
index 0000000..207e479
--- /dev/null
+++ b/Tests/EasyCoreTests/DecoderTests.swift
@@ -0,0 +1,190 @@
+import Testing
+import Foundation
+
+@testable import EasyCore
+
+@Suite("Decoder")
+struct DecoderTests {
+ private struct User: Decodable, Equatable {
+ let name: String
+ let age: Int
+ }
+
+ @Suite("Data.decode(_:decoder:)")
+ struct DataDecodeTests {
+
+ @Test("Decodes valid JSON into expected model")
+ func validDecoding() {
+ let json = """
+ {
+ "name": "Letícia",
+ "age": 28
+ }
+ """
+ let data = Data(json.utf8)
+ let user = data.decode(User.self)
+
+ #expect(user == User(name: "Letícia", age: 28))
+ }
+
+ @Test("Returns nil when decoding invalid JSON")
+ func invalidJSON() {
+ let json = """
+ {
+ "name": "Letícia"
+ "age": 28
+ }
+ """ // missing comma
+ let data = Data(json.utf8)
+ let user = data.decode(User.self)
+
+ #expect(user == nil)
+ }
+
+ @Test("Returns nil when structure doesn't match expected model")
+ func mismatchedStructure() {
+ let json = """
+ {
+ "username": "Letícia",
+ "yearsOld": 28
+ }
+ """
+ let data = Data(json.utf8)
+ let user = data.decode(User.self)
+
+ #expect(user == nil)
+ }
+
+ @Test("Supports decoding with custom decoder")
+ func customDecoder() {
+ struct DateModel: Decodable, Equatable {
+ let createdAt: Date
+ }
+
+ let json = #"{"createdAt": "2023-10-15T12:00:00Z"}"#
+ let data = Data(json.utf8)
+
+ let decoder = JSONDecoder()
+ decoder.dateDecodingStrategy = .iso8601
+
+ let result = data.decode(DateModel.self, decoder: decoder)
+ #expect(result?.createdAt == ISO8601DateFormatter().date(from: "2023-10-15T12:00:00Z"))
+ }
+ }
+
+ @Suite("String.decode(_:decoder:)")
+ struct StringDecodeTests {
+
+ @Test("Decodes a valid JSON string into a model")
+ func validJSONString() {
+ let json = """
+ {
+ "name": "Letícia",
+ "age": 28
+ }
+ """
+ let result = json.decode(User.self)
+ #expect(result == User(name: "Letícia", age: 28))
+ }
+
+ @Test("Returns nil when string is not valid JSON")
+ func invalidJSONString() {
+ let json = """
+ {
+ "name": "Letícia"
+ "age": 28
+ }
+ """ // Missing comma
+ let result = json.decode(User.self)
+ #expect(result == nil)
+ }
+
+ @Test("Returns nil when JSON structure does not match model")
+ func mismatchedStructure() {
+ let json = """
+ {
+ "username": "Letícia",
+ "years": 28
+ }
+ """
+ let result = json.decode(User.self)
+ #expect(result == nil)
+ }
+
+ @Test("Returns nil when string is not UTF-8 encodable")
+ func invalidEncoding() {
+ let string = "👩💻".applyingTransform(.toLatin, reverse: false) ?? "" // creates a non-UTF-8 safe string
+ let result = string.decode(User.self)
+ #expect(result == nil)
+ }
+
+ @Test("Decodes with custom decoder strategy")
+ func customDateDecoder() {
+ struct Event: Decodable, Equatable {
+ let date: Date
+ }
+
+ let json = #"{"date":"2023-05-01T14:30:00Z"}"#
+ let decoder = JSONDecoder()
+ decoder.dateDecodingStrategy = .iso8601
+
+ let result = json.decode(Event.self, decoder: decoder)
+ let expected = ISO8601DateFormatter().date(from: "2023-05-01T14:30:00Z")
+ #expect(result?.date == expected)
+ }
+ }
+
+ @Suite("JSONDecoder Presets")
+ @MainActor
+ struct JSONDecoderPresetsTests {
+
+ @Test("standard returns a fresh decoder instance")
+ func standardIsDefault() {
+ let decoder = JSONDecoder.standard
+ #expect(String(describing: decoder.keyDecodingStrategy) == String(describing: JSONDecoder.KeyDecodingStrategy.useDefaultKeys))
+ #expect(String(describing: decoder.dateDecodingStrategy) == String(describing: JSONDecoder.DateDecodingStrategy.deferredToDate))
+ }
+
+ @Test("snakeCase applies convertFromSnakeCase strategy")
+ func snakeCaseDecoder() {
+ let decoder = JSONDecoder.snakeCase
+
+ #expect(String(describing: decoder.keyDecodingStrategy) == String(describing: JSONDecoder.KeyDecodingStrategy.convertFromSnakeCase))
+ }
+
+ @Test("iso8601 applies ISO8601 date decoding strategy")
+ func iso8601Decoder() {
+ let decoder = JSONDecoder.iso8601
+
+ #expect(String(describing: decoder.dateDecodingStrategy) == String(describing: JSONDecoder.DateDecodingStrategy.iso8601))
+ }
+
+ @Test("secondsSince1970 applies secondsSince1970 date decoding strategy")
+ func secondsSince1970Decoder() {
+ let decoder = JSONDecoder.secondsSince1970
+ #expect(String(describing: decoder.dateDecodingStrategy) == String(describing: JSONDecoder.DateDecodingStrategy.secondsSince1970))
+ }
+
+ @Test("formatted(using:) applies custom date formatter")
+ func formattedUsingCustomFormatter() {
+ let formatter = DateFormatter()
+ formatter.dateFormat = "yyyy-MM-dd"
+
+ let decoder = JSONDecoder.formatted(using: formatter)
+ #expect(String(describing: decoder.dateDecodingStrategy) == String(describing: JSONDecoder.DateDecodingStrategy.formatted(formatter)))
+ }
+
+ @Test("Chained modifiers preserve strategy")
+ func chainingModifiersWorks() {
+ let formatter = DateFormatter()
+ formatter.dateFormat = "yyyy-MM-dd"
+
+ let decoder = JSONDecoder.standard
+ .snakeCase
+ .formatted(using: formatter)
+
+
+ #expect(String(describing: decoder.dateDecodingStrategy) == String(describing: JSONDecoder.DateDecodingStrategy.formatted(formatter)))
+ }
+ }
+}
diff --git a/Tests/EasyCoreTests/EasyCoreTests.swift b/Tests/EasyCoreTests/EasyCoreTests.swift
deleted file mode 100644
index 499ea4e..0000000
--- a/Tests/EasyCoreTests/EasyCoreTests.swift
+++ /dev/null
@@ -1,6 +0,0 @@
-import Testing
-@testable import EasyCore
-
-@Test func example() async throws {
- // Write your test here and use APIs like `#expect(...)` to check expected conditions.
-}
diff --git a/Tests/EasyCoreTests/EncoderTests.swift b/Tests/EasyCoreTests/EncoderTests.swift
new file mode 100644
index 0000000..f3d18ca
--- /dev/null
+++ b/Tests/EasyCoreTests/EncoderTests.swift
@@ -0,0 +1,105 @@
+import Foundation
+import Testing
+
+@testable import EasyCore
+
+@Suite("Encoder")
+struct EncoderTests {
+ private struct User: Encodable, Equatable {
+ let name: String
+ let age: Int
+ }
+
+ @Suite("Encodable extensions")
+ struct EncodableTests {
+
+ @Test("Encodes object to Data successfully")
+ func encodeToData() {
+ let user = User(name: "Letícia", age: 28)
+ let data = user.encode()
+
+ #expect(data != nil)
+
+ let json = try? JSONSerialization.jsonObject(with: data!)
+ #expect(json is [String: Any])
+ }
+
+ @Test("Encodes object to JSON string")
+ func encodeToJSONString() {
+ let user = User(name: "Letícia", age: 28)
+ let json = user.encodeToString()
+
+ #expect(json?.contains("\"name\"") == true)
+ #expect(json?.contains("\"Letícia\"") == true)
+ }
+
+ @Test("Encodes object to pretty-printed JSON string")
+ func encodeToPrettyPrintedJSONString() {
+ let user = User(name: "Letícia", age: 28)
+ let json = user.encodeToString(prettyPrinted: true)
+
+ #expect(json?.contains("\n") == true)
+ #expect(json?.contains(" ") == true)
+ }
+
+ @Test("Converts object to dictionary representation")
+ func encodeToDictionary() {
+ let user = User(name: "Letícia", age: 28)
+ let dict = user.dictionary
+
+ #expect(dict?["name"] as? String == "Letícia")
+ #expect(dict?["age"] as? Int == 28)
+ }
+ }
+
+ @Suite("JSONEncoder Presets")
+ struct JSONEncoderPresetsTests {
+
+ @Test("standard returns default configuration")
+ func standardReturnsDefaults() {
+ let encoder = JSONEncoder.standard
+
+ #expect(String(describing: encoder.keyEncodingStrategy) == String(describing: JSONEncoder.KeyEncodingStrategy.useDefaultKeys))
+ #expect(String(describing: encoder.dateEncodingStrategy) == String(describing: JSONEncoder.DateEncodingStrategy.deferredToDate))
+ }
+
+ @Test("snakeCase applies convertToSnakeCase")
+ func snakeCaseApplied() {
+ let encoder = JSONEncoder.snakeCase
+ #expect(String(describing: encoder.keyEncodingStrategy) == String(describing: JSONEncoder.KeyEncodingStrategy.convertToSnakeCase))
+ }
+
+ @Test("iso8601 applies ISO8601 date encoding strategy")
+ func iso8601Applied() {
+ let encoder = JSONEncoder.iso8601
+ #expect(String(describing: encoder.dateEncodingStrategy) == String(describing: JSONEncoder.DateEncodingStrategy.iso8601))
+ }
+
+ @Test("secondsSince1970 applies secondsSince1970 date encoding strategy")
+ func secondsSince1970Applied() {
+ let encoder = JSONEncoder.secondsSince1970
+ #expect(String(describing: encoder.dateEncodingStrategy) == String(describing: JSONEncoder.DateEncodingStrategy.secondsSince1970))
+ }
+
+ @Test("formatted(using:) applies custom DateFormatter strategy")
+ func formattedWithCustomDateFormatter() {
+ let formatter = DateFormatter()
+ formatter.dateFormat = "yyyy-MM-dd"
+ let encoder = JSONEncoder.formatted(using: formatter)
+ #expect(String(describing: encoder.dateEncodingStrategy).contains("formatted"))
+ }
+
+ @Test("Chaining snakeCase and formatted(using:) applies both strategies")
+ func chainingModifiersWorks() {
+ let formatter = DateFormatter()
+ formatter.dateFormat = "yyyy-MM-dd"
+
+ let encoder = JSONEncoder.standard
+ .snakeCase
+ .formatted(using: formatter)
+
+ #expect(String(describing: encoder.keyEncodingStrategy) == String(describing: JSONEncoder.KeyEncodingStrategy.convertToSnakeCase))
+ #expect(String(describing: encoder.dateEncodingStrategy).contains("formatted"))
+ }
+ }
+}
diff --git a/Tests/EasyCoreTests/LocaleExtensionTests.swift b/Tests/EasyCoreTests/LocaleExtensionTests.swift
new file mode 100644
index 0000000..7dd2fba
--- /dev/null
+++ b/Tests/EasyCoreTests/LocaleExtensionTests.swift
@@ -0,0 +1,453 @@
+import Foundation
+
+import Testing
+
+@testable import EasyCore
+
+@Suite("Locale")
+struct Localetests {
+
+ @Test("All locales")
+ func locales() {
+ #expect(Locale.aaDJ == LocaleIdentifier.aaDJ.locale!)
+ #expect(Locale.aaER == LocaleIdentifier.aaER.locale!)
+ #expect(Locale.aaET == LocaleIdentifier.aaET.locale!)
+ #expect(Locale.abGE == LocaleIdentifier.abGE.locale!)
+ #expect(Locale.afNA == LocaleIdentifier.afNA.locale!)
+ #expect(Locale.afZA == LocaleIdentifier.afZA.locale!)
+ #expect(Locale.akGH == LocaleIdentifier.akGH.locale!)
+ #expect(Locale.amET == LocaleIdentifier.amET.locale!)
+ #expect(Locale.anES == LocaleIdentifier.anES.locale!)
+ #expect(Locale.arAE == LocaleIdentifier.arAE.locale!)
+ #expect(Locale.arBH == LocaleIdentifier.arBH.locale!)
+ #expect(Locale.arDJ == LocaleIdentifier.arDJ.locale!)
+ #expect(Locale.arDZ == LocaleIdentifier.arDZ.locale!)
+ #expect(Locale.arEG == LocaleIdentifier.arEG.locale!)
+ #expect(Locale.arEH == LocaleIdentifier.arEH.locale!)
+ #expect(Locale.arER == LocaleIdentifier.arER.locale!)
+ #expect(Locale.arIL == LocaleIdentifier.arIL.locale!)
+ #expect(Locale.arIQ == LocaleIdentifier.arIQ.locale!)
+ #expect(Locale.arJO == LocaleIdentifier.arJO.locale!)
+ #expect(Locale.arKM == LocaleIdentifier.arKM.locale!)
+ #expect(Locale.arKW == LocaleIdentifier.arKW.locale!)
+ #expect(Locale.arLB == LocaleIdentifier.arLB.locale!)
+ #expect(Locale.arLY == LocaleIdentifier.arLY.locale!)
+ #expect(Locale.arMA == LocaleIdentifier.arMA.locale!)
+ #expect(Locale.arMR == LocaleIdentifier.arMR.locale!)
+ #expect(Locale.arOM == LocaleIdentifier.arOM.locale!)
+ #expect(Locale.arPS == LocaleIdentifier.arPS.locale!)
+ #expect(Locale.arQA == LocaleIdentifier.arQA.locale!)
+ #expect(Locale.arSA == LocaleIdentifier.arSA.locale!)
+ #expect(Locale.arSD == LocaleIdentifier.arSD.locale!)
+ #expect(Locale.arSO == LocaleIdentifier.arSO.locale!)
+ #expect(Locale.arSS == LocaleIdentifier.arSS.locale!)
+ #expect(Locale.arSY == LocaleIdentifier.arSY.locale!)
+ #expect(Locale.arTD == LocaleIdentifier.arTD.locale!)
+ #expect(Locale.arTN == LocaleIdentifier.arTN.locale!)
+ #expect(Locale.arYE == LocaleIdentifier.arYE.locale!)
+ #expect(Locale.asIN == LocaleIdentifier.asIN.locale!)
+ #expect(Locale.baRU == LocaleIdentifier.baRU.locale!)
+ #expect(Locale.beBY == LocaleIdentifier.beBY.locale!)
+ #expect(Locale.bgBG == LocaleIdentifier.bgBG.locale!)
+ #expect(Locale.bmML == LocaleIdentifier.bmML.locale!)
+ #expect(Locale.bnBD == LocaleIdentifier.bnBD.locale!)
+ #expect(Locale.bnIN == LocaleIdentifier.bnIN.locale!)
+ #expect(Locale.boCN == LocaleIdentifier.boCN.locale!)
+ #expect(Locale.boIN == LocaleIdentifier.boIN.locale!)
+ #expect(Locale.brFR == LocaleIdentifier.brFR.locale!)
+ #expect(Locale.caAD == LocaleIdentifier.caAD.locale!)
+ #expect(Locale.caES == LocaleIdentifier.caES.locale!)
+ #expect(Locale.caESVALENCIA == LocaleIdentifier.caESVALENCIA.locale!)
+ #expect(Locale.caFR == LocaleIdentifier.caFR.locale!)
+ #expect(Locale.caIT == LocaleIdentifier.caIT.locale!)
+ #expect(Locale.ceRU == LocaleIdentifier.ceRU.locale!)
+ #expect(Locale.coFR == LocaleIdentifier.coFR.locale!)
+ #expect(Locale.csCZ == LocaleIdentifier.csCZ.locale!)
+ #expect(Locale.cuRU == LocaleIdentifier.cuRU.locale!)
+ #expect(Locale.cvRU == LocaleIdentifier.cvRU.locale!)
+ #expect(Locale.cyGB == LocaleIdentifier.cyGB.locale!)
+ #expect(Locale.daDK == LocaleIdentifier.daDK.locale!)
+ #expect(Locale.daGL == LocaleIdentifier.daGL.locale!)
+ #expect(Locale.deAT == LocaleIdentifier.deAT.locale!)
+ #expect(Locale.deBE == LocaleIdentifier.deBE.locale!)
+ #expect(Locale.deCH == LocaleIdentifier.deCH.locale!)
+ #expect(Locale.deDE == LocaleIdentifier.deDE.locale!)
+ #expect(Locale.deIT == LocaleIdentifier.deIT.locale!)
+ #expect(Locale.deLI == LocaleIdentifier.deLI.locale!)
+ #expect(Locale.deLU == LocaleIdentifier.deLU.locale!)
+ #expect(Locale.dvMV == LocaleIdentifier.dvMV.locale!)
+ #expect(Locale.dzBT == LocaleIdentifier.dzBT.locale!)
+ #expect(Locale.eeGH == LocaleIdentifier.eeGH.locale!)
+ #expect(Locale.eeTG == LocaleIdentifier.eeTG.locale!)
+ #expect(Locale.elCY == LocaleIdentifier.elCY.locale!)
+ #expect(Locale.elGR == LocaleIdentifier.elGR.locale!)
+ #expect(Locale.enAE == LocaleIdentifier.enAE.locale!)
+ #expect(Locale.enAG == LocaleIdentifier.enAG.locale!)
+ #expect(Locale.enAI == LocaleIdentifier.enAI.locale!)
+ #expect(Locale.enAS == LocaleIdentifier.enAS.locale!)
+ #expect(Locale.enAT == LocaleIdentifier.enAT.locale!)
+ #expect(Locale.enAU == LocaleIdentifier.enAU.locale!)
+ #expect(Locale.enBB == LocaleIdentifier.enBB.locale!)
+ #expect(Locale.enBE == LocaleIdentifier.enBE.locale!)
+ #expect(Locale.enBI == LocaleIdentifier.enBI.locale!)
+ #expect(Locale.enBM == LocaleIdentifier.enBM.locale!)
+ #expect(Locale.enBS == LocaleIdentifier.enBS.locale!)
+ #expect(Locale.enBW == LocaleIdentifier.enBW.locale!)
+ #expect(Locale.enBZ == LocaleIdentifier.enBZ.locale!)
+ #expect(Locale.enCA == LocaleIdentifier.enCA.locale!)
+ #expect(Locale.enCC == LocaleIdentifier.enCC.locale!)
+ #expect(Locale.enCH == LocaleIdentifier.enCH.locale!)
+ #expect(Locale.enCK == LocaleIdentifier.enCK.locale!)
+ #expect(Locale.enCM == LocaleIdentifier.enCM.locale!)
+ #expect(Locale.enCX == LocaleIdentifier.enCX.locale!)
+ #expect(Locale.enCY == LocaleIdentifier.enCY.locale!)
+ #expect(Locale.enDE == LocaleIdentifier.enDE.locale!)
+ #expect(Locale.enDG == LocaleIdentifier.enDG.locale!)
+ #expect(Locale.enDK == LocaleIdentifier.enDK.locale!)
+ #expect(Locale.enDM == LocaleIdentifier.enDM.locale!)
+ #expect(Locale.enER == LocaleIdentifier.enER.locale!)
+ #expect(Locale.enFI == LocaleIdentifier.enFI.locale!)
+ #expect(Locale.enFJ == LocaleIdentifier.enFJ.locale!)
+ #expect(Locale.enFK == LocaleIdentifier.enFK.locale!)
+ #expect(Locale.enFM == LocaleIdentifier.enFM.locale!)
+ #expect(Locale.enGB == LocaleIdentifier.enGB.locale!)
+ #expect(Locale.enGD == LocaleIdentifier.enGD.locale!)
+ #expect(Locale.enGG == LocaleIdentifier.enGG.locale!)
+ #expect(Locale.enGH == LocaleIdentifier.enGH.locale!)
+ #expect(Locale.enGI == LocaleIdentifier.enGI.locale!)
+ #expect(Locale.enGM == LocaleIdentifier.enGM.locale!)
+ #expect(Locale.enGU == LocaleIdentifier.enGU.locale!)
+ #expect(Locale.enGY == LocaleIdentifier.enGY.locale!)
+ #expect(Locale.enHK == LocaleIdentifier.enHK.locale!)
+ #expect(Locale.enID == LocaleIdentifier.enID.locale!)
+ #expect(Locale.enIE == LocaleIdentifier.enIE.locale!)
+ #expect(Locale.enIL == LocaleIdentifier.enIL.locale!)
+ #expect(Locale.enIM == LocaleIdentifier.enIM.locale!)
+ #expect(Locale.enIN == LocaleIdentifier.enIN.locale!)
+ #expect(Locale.enIO == LocaleIdentifier.enIO.locale!)
+ #expect(Locale.enJE == LocaleIdentifier.enJE.locale!)
+ #expect(Locale.enJM == LocaleIdentifier.enJM.locale!)
+ #expect(Locale.enKE == LocaleIdentifier.enKE.locale!)
+ #expect(Locale.enKI == LocaleIdentifier.enKI.locale!)
+ #expect(Locale.enKN == LocaleIdentifier.enKN.locale!)
+ #expect(Locale.enKY == LocaleIdentifier.enKY.locale!)
+ #expect(Locale.enLC == LocaleIdentifier.enLC.locale!)
+ #expect(Locale.enLR == LocaleIdentifier.enLR.locale!)
+ #expect(Locale.enLS == LocaleIdentifier.enLS.locale!)
+ #expect(Locale.enMG == LocaleIdentifier.enMG.locale!)
+ #expect(Locale.enMH == LocaleIdentifier.enMH.locale!)
+ #expect(Locale.enMO == LocaleIdentifier.enMO.locale!)
+ #expect(Locale.enMP == LocaleIdentifier.enMP.locale!)
+ #expect(Locale.enMS == LocaleIdentifier.enMS.locale!)
+ #expect(Locale.enMT == LocaleIdentifier.enMT.locale!)
+ #expect(Locale.enMU == LocaleIdentifier.enMU.locale!)
+ #expect(Locale.enMV == LocaleIdentifier.enMV.locale!)
+ #expect(Locale.enMW == LocaleIdentifier.enMW.locale!)
+ #expect(Locale.enMY == LocaleIdentifier.enMY.locale!)
+ #expect(Locale.enNA == LocaleIdentifier.enNA.locale!)
+ #expect(Locale.enNF == LocaleIdentifier.enNF.locale!)
+ #expect(Locale.enNG == LocaleIdentifier.enNG.locale!)
+ #expect(Locale.enNL == LocaleIdentifier.enNL.locale!)
+ #expect(Locale.enNR == LocaleIdentifier.enNR.locale!)
+ #expect(Locale.enNU == LocaleIdentifier.enNU.locale!)
+ #expect(Locale.enNZ == LocaleIdentifier.enNZ.locale!)
+ #expect(Locale.enPG == LocaleIdentifier.enPG.locale!)
+ #expect(Locale.enPH == LocaleIdentifier.enPH.locale!)
+ #expect(Locale.enPK == LocaleIdentifier.enPK.locale!)
+ #expect(Locale.enPN == LocaleIdentifier.enPN.locale!)
+ #expect(Locale.enPR == LocaleIdentifier.enPR.locale!)
+ #expect(Locale.enPW == LocaleIdentifier.enPW.locale!)
+ #expect(Locale.enRW == LocaleIdentifier.enRW.locale!)
+ #expect(Locale.enSB == LocaleIdentifier.enSB.locale!)
+ #expect(Locale.enSC == LocaleIdentifier.enSC.locale!)
+ #expect(Locale.enSD == LocaleIdentifier.enSD.locale!)
+ #expect(Locale.enSE == LocaleIdentifier.enSE.locale!)
+ #expect(Locale.enSG == LocaleIdentifier.enSG.locale!)
+ #expect(Locale.enSH == LocaleIdentifier.enSH.locale!)
+ #expect(Locale.enSI == LocaleIdentifier.enSI.locale!)
+ #expect(Locale.enSL == LocaleIdentifier.enSL.locale!)
+ #expect(Locale.enSS == LocaleIdentifier.enSS.locale!)
+ #expect(Locale.enSX == LocaleIdentifier.enSX.locale!)
+ #expect(Locale.enSZ == LocaleIdentifier.enSZ.locale!)
+ #expect(Locale.enTC == LocaleIdentifier.enTC.locale!)
+ #expect(Locale.enTK == LocaleIdentifier.enTK.locale!)
+ #expect(Locale.enTO == LocaleIdentifier.enTO.locale!)
+ #expect(Locale.enTT == LocaleIdentifier.enTT.locale!)
+ #expect(Locale.enTV == LocaleIdentifier.enTV.locale!)
+ #expect(Locale.enTZ == LocaleIdentifier.enTZ.locale!)
+ #expect(Locale.enUG == LocaleIdentifier.enUG.locale!)
+ #expect(Locale.enUM == LocaleIdentifier.enUM.locale!)
+ #expect(Locale.enUS == LocaleIdentifier.enUS.locale!)
+ #expect(Locale.enUSPOSIX == LocaleIdentifier.enUSPOSIX.locale!)
+ #expect(Locale.enVC == LocaleIdentifier.enVC.locale!)
+ #expect(Locale.enVG == LocaleIdentifier.enVG.locale!)
+ #expect(Locale.enVI == LocaleIdentifier.enVI.locale!)
+ #expect(Locale.enVU == LocaleIdentifier.enVU.locale!)
+ #expect(Locale.enWS == LocaleIdentifier.enWS.locale!)
+ #expect(Locale.enZA == LocaleIdentifier.enZA.locale!)
+ #expect(Locale.enZM == LocaleIdentifier.enZM.locale!)
+ #expect(Locale.enZW == LocaleIdentifier.enZW.locale!)
+ #expect(Locale.esAR == LocaleIdentifier.esAR.locale!)
+ #expect(Locale.esBO == LocaleIdentifier.esBO.locale!)
+ #expect(Locale.esBR == LocaleIdentifier.esBR.locale!)
+ #expect(Locale.esBZ == LocaleIdentifier.esBZ.locale!)
+ #expect(Locale.esCL == LocaleIdentifier.esCL.locale!)
+ #expect(Locale.esCO == LocaleIdentifier.esCO.locale!)
+ #expect(Locale.esCR == LocaleIdentifier.esCR.locale!)
+ #expect(Locale.esCU == LocaleIdentifier.esCU.locale!)
+ #expect(Locale.esDO == LocaleIdentifier.esDO.locale!)
+ #expect(Locale.esEA == LocaleIdentifier.esEA.locale!)
+ #expect(Locale.esEC == LocaleIdentifier.esEC.locale!)
+ #expect(Locale.esES == LocaleIdentifier.esES.locale!)
+ #expect(Locale.esGQ == LocaleIdentifier.esGQ.locale!)
+ #expect(Locale.esGT == LocaleIdentifier.esGT.locale!)
+ #expect(Locale.esHN == LocaleIdentifier.esHN.locale!)
+ #expect(Locale.esIC == LocaleIdentifier.esIC.locale!)
+ #expect(Locale.esMX == LocaleIdentifier.esMX.locale!)
+ #expect(Locale.esNI == LocaleIdentifier.esNI.locale!)
+ #expect(Locale.esPA == LocaleIdentifier.esPA.locale!)
+ #expect(Locale.esPE == LocaleIdentifier.esPE.locale!)
+ #expect(Locale.esPH == LocaleIdentifier.esPH.locale!)
+ #expect(Locale.esPR == LocaleIdentifier.esPR.locale!)
+ #expect(Locale.esPY == LocaleIdentifier.esPY.locale!)
+ #expect(Locale.esSV == LocaleIdentifier.esSV.locale!)
+ #expect(Locale.esUS == LocaleIdentifier.esUS.locale!)
+ #expect(Locale.esUY == LocaleIdentifier.esUY.locale!)
+ #expect(Locale.esVE == LocaleIdentifier.esVE.locale!)
+ #expect(Locale.etEE == LocaleIdentifier.etEE.locale!)
+ #expect(Locale.euES == LocaleIdentifier.euES.locale!)
+ #expect(Locale.faAF == LocaleIdentifier.faAF.locale!)
+ #expect(Locale.faIR == LocaleIdentifier.faIR.locale!)
+ #expect(Locale.fiFI == LocaleIdentifier.fiFI.locale!)
+ #expect(Locale.foDK == LocaleIdentifier.foDK.locale!)
+ #expect(Locale.foFO == LocaleIdentifier.foFO.locale!)
+ #expect(Locale.frBE == LocaleIdentifier.frBE.locale!)
+ #expect(Locale.frBF == LocaleIdentifier.frBF.locale!)
+ #expect(Locale.frBI == LocaleIdentifier.frBI.locale!)
+ #expect(Locale.frBJ == LocaleIdentifier.frBJ.locale!)
+ #expect(Locale.frBL == LocaleIdentifier.frBL.locale!)
+ #expect(Locale.frCA == LocaleIdentifier.frCA.locale!)
+ #expect(Locale.frCD == LocaleIdentifier.frCD.locale!)
+ #expect(Locale.frCF == LocaleIdentifier.frCF.locale!)
+ #expect(Locale.frCG == LocaleIdentifier.frCG.locale!)
+ #expect(Locale.frCH == LocaleIdentifier.frCH.locale!)
+ #expect(Locale.frCI == LocaleIdentifier.frCI.locale!)
+ #expect(Locale.frCM == LocaleIdentifier.frCM.locale!)
+ #expect(Locale.frDJ == LocaleIdentifier.frDJ.locale!)
+ #expect(Locale.frDZ == LocaleIdentifier.frDZ.locale!)
+ #expect(Locale.frFR == LocaleIdentifier.frFR.locale!)
+ #expect(Locale.frGA == LocaleIdentifier.frGA.locale!)
+ #expect(Locale.frGF == LocaleIdentifier.frGF.locale!)
+ #expect(Locale.frGN == LocaleIdentifier.frGN.locale!)
+ #expect(Locale.frGP == LocaleIdentifier.frGP.locale!)
+ #expect(Locale.frGQ == LocaleIdentifier.frGQ.locale!)
+ #expect(Locale.frHT == LocaleIdentifier.frHT.locale!)
+ #expect(Locale.frKM == LocaleIdentifier.frKM.locale!)
+ #expect(Locale.frLU == LocaleIdentifier.frLU.locale!)
+ #expect(Locale.frMA == LocaleIdentifier.frMA.locale!)
+ #expect(Locale.frMC == LocaleIdentifier.frMC.locale!)
+ #expect(Locale.frMF == LocaleIdentifier.frMF.locale!)
+ #expect(Locale.frMG == LocaleIdentifier.frMG.locale!)
+ #expect(Locale.frML == LocaleIdentifier.frML.locale!)
+ #expect(Locale.frMQ == LocaleIdentifier.frMQ.locale!)
+ #expect(Locale.frMR == LocaleIdentifier.frMR.locale!)
+ #expect(Locale.frMU == LocaleIdentifier.frMU.locale!)
+ #expect(Locale.frNC == LocaleIdentifier.frNC.locale!)
+ #expect(Locale.frNE == LocaleIdentifier.frNE.locale!)
+ #expect(Locale.frPF == LocaleIdentifier.frPF.locale!)
+ #expect(Locale.frPM == LocaleIdentifier.frPM.locale!)
+ #expect(Locale.frRE == LocaleIdentifier.frRE.locale!)
+ #expect(Locale.frRW == LocaleIdentifier.frRW.locale!)
+ #expect(Locale.frSC == LocaleIdentifier.frSC.locale!)
+ #expect(Locale.frSN == LocaleIdentifier.frSN.locale!)
+ #expect(Locale.frSY == LocaleIdentifier.frSY.locale!)
+ #expect(Locale.frTD == LocaleIdentifier.frTD.locale!)
+ #expect(Locale.frTG == LocaleIdentifier.frTG.locale!)
+ #expect(Locale.frTN == LocaleIdentifier.frTN.locale!)
+ #expect(Locale.frVU == LocaleIdentifier.frVU.locale!)
+ #expect(Locale.frWF == LocaleIdentifier.frWF.locale!)
+ #expect(Locale.frYT == LocaleIdentifier.frYT.locale!)
+ #expect(Locale.fyNL == LocaleIdentifier.fyNL.locale!)
+ #expect(Locale.gaGB == LocaleIdentifier.gaGB.locale!)
+ #expect(Locale.gaIE == LocaleIdentifier.gaIE.locale!)
+ #expect(Locale.gdGB == LocaleIdentifier.gdGB.locale!)
+ #expect(Locale.glES == LocaleIdentifier.glES.locale!)
+ #expect(Locale.gnPY == LocaleIdentifier.gnPY.locale!)
+ #expect(Locale.guIN == LocaleIdentifier.guIN.locale!)
+ #expect(Locale.gvIM == LocaleIdentifier.gvIM.locale!)
+ #expect(Locale.haGH == LocaleIdentifier.haGH.locale!)
+ #expect(Locale.haNE == LocaleIdentifier.haNE.locale!)
+ #expect(Locale.haNG == LocaleIdentifier.haNG.locale!)
+ #expect(Locale.heIL == LocaleIdentifier.heIL.locale!)
+ #expect(Locale.hiIN == LocaleIdentifier.hiIN.locale!)
+ #expect(Locale.hrBA == LocaleIdentifier.hrBA.locale!)
+ #expect(Locale.hrHR == LocaleIdentifier.hrHR.locale!)
+ #expect(Locale.huHU == LocaleIdentifier.huHU.locale!)
+ #expect(Locale.hyAM == LocaleIdentifier.hyAM.locale!)
+ #expect(Locale.idID == LocaleIdentifier.idID.locale!)
+ #expect(Locale.ieEE == LocaleIdentifier.ieEE.locale!)
+ #expect(Locale.igNG == LocaleIdentifier.igNG.locale!)
+ #expect(Locale.iiCN == LocaleIdentifier.iiCN.locale!)
+ #expect(Locale.isIS == LocaleIdentifier.isIS.locale!)
+ #expect(Locale.itCH == LocaleIdentifier.itCH.locale!)
+ #expect(Locale.itIT == LocaleIdentifier.itIT.locale!)
+ #expect(Locale.itSM == LocaleIdentifier.itSM.locale!)
+ #expect(Locale.itVA == LocaleIdentifier.itVA.locale!)
+ #expect(Locale.iuCA == LocaleIdentifier.iuCA.locale!)
+ #expect(Locale.jaJP == LocaleIdentifier.jaJP.locale!)
+ #expect(Locale.jvID == LocaleIdentifier.jvID.locale!)
+ #expect(Locale.kaGE == LocaleIdentifier.kaGE.locale!)
+ #expect(Locale.kiKE == LocaleIdentifier.kiKE.locale!)
+ #expect(Locale.kkKZ == LocaleIdentifier.kkKZ.locale!)
+ #expect(Locale.klGL == LocaleIdentifier.klGL.locale!)
+ #expect(Locale.kmKH == LocaleIdentifier.kmKH.locale!)
+ #expect(Locale.knIN == LocaleIdentifier.knIN.locale!)
+ #expect(Locale.koCN == LocaleIdentifier.koCN.locale!)
+ #expect(Locale.koKP == LocaleIdentifier.koKP.locale!)
+ #expect(Locale.koKR == LocaleIdentifier.koKR.locale!)
+ #expect(Locale.kuTR == LocaleIdentifier.kuTR.locale!)
+ #expect(Locale.kwGB == LocaleIdentifier.kwGB.locale!)
+ #expect(Locale.kyKG == LocaleIdentifier.kyKG.locale!)
+ #expect(Locale.laVA == LocaleIdentifier.laVA.locale!)
+ #expect(Locale.lbLU == LocaleIdentifier.lbLU.locale!)
+ #expect(Locale.lgUG == LocaleIdentifier.lgUG.locale!)
+ #expect(Locale.lnAO == LocaleIdentifier.lnAO.locale!)
+ #expect(Locale.lnCD == LocaleIdentifier.lnCD.locale!)
+ #expect(Locale.lnCF == LocaleIdentifier.lnCF.locale!)
+ #expect(Locale.lnCG == LocaleIdentifier.lnCG.locale!)
+ #expect(Locale.loLA == LocaleIdentifier.loLA.locale!)
+ #expect(Locale.ltLT == LocaleIdentifier.ltLT.locale!)
+ #expect(Locale.luCD == LocaleIdentifier.luCD.locale!)
+ #expect(Locale.lvLV == LocaleIdentifier.lvLV.locale!)
+ #expect(Locale.mgMG == LocaleIdentifier.mgMG.locale!)
+ #expect(Locale.miNZ == LocaleIdentifier.miNZ.locale!)
+ #expect(Locale.mkMK == LocaleIdentifier.mkMK.locale!)
+ #expect(Locale.mlIN == LocaleIdentifier.mlIN.locale!)
+ #expect(Locale.mnMN == LocaleIdentifier.mnMN.locale!)
+ #expect(Locale.mrIN == LocaleIdentifier.mrIN.locale!)
+ #expect(Locale.msBN == LocaleIdentifier.msBN.locale!)
+ #expect(Locale.msID == LocaleIdentifier.msID.locale!)
+ #expect(Locale.msMY == LocaleIdentifier.msMY.locale!)
+ #expect(Locale.msSG == LocaleIdentifier.msSG.locale!)
+ #expect(Locale.mtMT == LocaleIdentifier.mtMT.locale!)
+ #expect(Locale.myMM == LocaleIdentifier.myMM.locale!)
+ #expect(Locale.nbNO == LocaleIdentifier.nbNO.locale!)
+ #expect(Locale.nbSJ == LocaleIdentifier.nbSJ.locale!)
+ #expect(Locale.ndZW == LocaleIdentifier.ndZW.locale!)
+ #expect(Locale.neIN == LocaleIdentifier.neIN.locale!)
+ #expect(Locale.neNP == LocaleIdentifier.neNP.locale!)
+ #expect(Locale.nlAW == LocaleIdentifier.nlAW.locale!)
+ #expect(Locale.nlBE == LocaleIdentifier.nlBE.locale!)
+ #expect(Locale.nlBQ == LocaleIdentifier.nlBQ.locale!)
+ #expect(Locale.nlCW == LocaleIdentifier.nlCW.locale!)
+ #expect(Locale.nlNL == LocaleIdentifier.nlNL.locale!)
+ #expect(Locale.nlSR == LocaleIdentifier.nlSR.locale!)
+ #expect(Locale.nlSX == LocaleIdentifier.nlSX.locale!)
+ #expect(Locale.nnNO == LocaleIdentifier.nnNO.locale!)
+ #expect(Locale.nrZA == LocaleIdentifier.nrZA.locale!)
+ #expect(Locale.nvUS == LocaleIdentifier.nvUS.locale!)
+ #expect(Locale.nyMW == LocaleIdentifier.nyMW.locale!)
+ #expect(Locale.ocES == LocaleIdentifier.ocES.locale!)
+ #expect(Locale.ocFR == LocaleIdentifier.ocFR.locale!)
+ #expect(Locale.omET == LocaleIdentifier.omET.locale!)
+ #expect(Locale.omKE == LocaleIdentifier.omKE.locale!)
+ #expect(Locale.orIN == LocaleIdentifier.orIN.locale!)
+ #expect(Locale.osGE == LocaleIdentifier.osGE.locale!)
+ #expect(Locale.osRU == LocaleIdentifier.osRU.locale!)
+ #expect(Locale.plPL == LocaleIdentifier.plPL.locale!)
+ #expect(Locale.psAF == LocaleIdentifier.psAF.locale!)
+ #expect(Locale.psPK == LocaleIdentifier.psPK.locale!)
+ #expect(Locale.ptAO == LocaleIdentifier.ptAO.locale!)
+ #expect(Locale.ptBR == LocaleIdentifier.ptBR.locale!)
+ #expect(Locale.ptCH == LocaleIdentifier.ptCH.locale!)
+ #expect(Locale.ptCV == LocaleIdentifier.ptCV.locale!)
+ #expect(Locale.ptGQ == LocaleIdentifier.ptGQ.locale!)
+ #expect(Locale.ptGW == LocaleIdentifier.ptGW.locale!)
+ #expect(Locale.ptLU == LocaleIdentifier.ptLU.locale!)
+ #expect(Locale.ptMO == LocaleIdentifier.ptMO.locale!)
+ #expect(Locale.ptMZ == LocaleIdentifier.ptMZ.locale!)
+ #expect(Locale.ptPT == LocaleIdentifier.ptPT.locale!)
+ #expect(Locale.ptST == LocaleIdentifier.ptST.locale!)
+ #expect(Locale.ptTL == LocaleIdentifier.ptTL.locale!)
+ #expect(Locale.quBO == LocaleIdentifier.quBO.locale!)
+ #expect(Locale.quEC == LocaleIdentifier.quEC.locale!)
+ #expect(Locale.quPE == LocaleIdentifier.quPE.locale!)
+ #expect(Locale.rmCH == LocaleIdentifier.rmCH.locale!)
+ #expect(Locale.rnBI == LocaleIdentifier.rnBI.locale!)
+ #expect(Locale.roMD == LocaleIdentifier.roMD.locale!)
+ #expect(Locale.roRO == LocaleIdentifier.roRO.locale!)
+ #expect(Locale.ruBY == LocaleIdentifier.ruBY.locale!)
+ #expect(Locale.ruKG == LocaleIdentifier.ruKG.locale!)
+ #expect(Locale.ruKZ == LocaleIdentifier.ruKZ.locale!)
+ #expect(Locale.ruMD == LocaleIdentifier.ruMD.locale!)
+ #expect(Locale.ruRU == LocaleIdentifier.ruRU.locale!)
+ #expect(Locale.ruUA == LocaleIdentifier.ruUA.locale!)
+ #expect(Locale.rwRW == LocaleIdentifier.rwRW.locale!)
+ #expect(Locale.saIN == LocaleIdentifier.saIN.locale!)
+ #expect(Locale.scIT == LocaleIdentifier.scIT.locale!)
+ #expect(Locale.seFI == LocaleIdentifier.seFI.locale!)
+ #expect(Locale.seNO == LocaleIdentifier.seNO.locale!)
+ #expect(Locale.seSE == LocaleIdentifier.seSE.locale!)
+ #expect(Locale.sgCF == LocaleIdentifier.sgCF.locale!)
+ #expect(Locale.siLK == LocaleIdentifier.siLK.locale!)
+ #expect(Locale.skSK == LocaleIdentifier.skSK.locale!)
+ #expect(Locale.slSI == LocaleIdentifier.slSI.locale!)
+ #expect(Locale.snZW == LocaleIdentifier.snZW.locale!)
+ #expect(Locale.soDJ == LocaleIdentifier.soDJ.locale!)
+ #expect(Locale.soET == LocaleIdentifier.soET.locale!)
+ #expect(Locale.soKE == LocaleIdentifier.soKE.locale!)
+ #expect(Locale.soSO == LocaleIdentifier.soSO.locale!)
+ #expect(Locale.sqAL == LocaleIdentifier.sqAL.locale!)
+ #expect(Locale.sqMK == LocaleIdentifier.sqMK.locale!)
+ #expect(Locale.sqXK == LocaleIdentifier.sqXK.locale!)
+ #expect(Locale.ssSZ == LocaleIdentifier.ssSZ.locale!)
+ #expect(Locale.ssZA == LocaleIdentifier.ssZA.locale!)
+ #expect(Locale.stLS == LocaleIdentifier.stLS.locale!)
+ #expect(Locale.stZA == LocaleIdentifier.stZA.locale!)
+ #expect(Locale.svAX == LocaleIdentifier.svAX.locale!)
+ #expect(Locale.svFI == LocaleIdentifier.svFI.locale!)
+ #expect(Locale.svSE == LocaleIdentifier.svSE.locale!)
+ #expect(Locale.swCD == LocaleIdentifier.swCD.locale!)
+ #expect(Locale.swKE == LocaleIdentifier.swKE.locale!)
+ #expect(Locale.swTZ == LocaleIdentifier.swTZ.locale!)
+ #expect(Locale.swUG == LocaleIdentifier.swUG.locale!)
+ #expect(Locale.taIN == LocaleIdentifier.taIN.locale!)
+ #expect(Locale.taLK == LocaleIdentifier.taLK.locale!)
+ #expect(Locale.taMY == LocaleIdentifier.taMY.locale!)
+ #expect(Locale.taSG == LocaleIdentifier.taSG.locale!)
+ #expect(Locale.teIN == LocaleIdentifier.teIN.locale!)
+ #expect(Locale.tgTJ == LocaleIdentifier.tgTJ.locale!)
+ #expect(Locale.thTH == LocaleIdentifier.thTH.locale!)
+ #expect(Locale.tiER == LocaleIdentifier.tiER.locale!)
+ #expect(Locale.tiET == LocaleIdentifier.tiET.locale!)
+ #expect(Locale.tkTM == LocaleIdentifier.tkTM.locale!)
+ #expect(Locale.tnBW == LocaleIdentifier.tnBW.locale!)
+ #expect(Locale.tnZA == LocaleIdentifier.tnZA.locale!)
+ #expect(Locale.toTO == LocaleIdentifier.toTO.locale!)
+ #expect(Locale.trCY == LocaleIdentifier.trCY.locale!)
+ #expect(Locale.trTR == LocaleIdentifier.trTR.locale!)
+ #expect(Locale.tsZA == LocaleIdentifier.tsZA.locale!)
+ #expect(Locale.ttRU == LocaleIdentifier.ttRU.locale!)
+ #expect(Locale.ugCN == LocaleIdentifier.ugCN.locale!)
+ #expect(Locale.ukUA == LocaleIdentifier.ukUA.locale!)
+ #expect(Locale.urIN == LocaleIdentifier.urIN.locale!)
+ #expect(Locale.urPK == LocaleIdentifier.urPK.locale!)
+ #expect(Locale.veZA == LocaleIdentifier.veZA.locale!)
+ #expect(Locale.viVN == LocaleIdentifier.viVN.locale!)
+ #expect(Locale.waBE == LocaleIdentifier.waBE.locale!)
+ #expect(Locale.woSN == LocaleIdentifier.woSN.locale!)
+ #expect(Locale.xhZA == LocaleIdentifier.xhZA.locale!)
+ #expect(Locale.yiUA == LocaleIdentifier.yiUA.locale!)
+ #expect(Locale.yoBJ == LocaleIdentifier.yoBJ.locale!)
+ #expect(Locale.yoNG == LocaleIdentifier.yoNG.locale!)
+ #expect(Locale.zaCN == LocaleIdentifier.zaCN.locale!)
+ #expect(Locale.zuZA == LocaleIdentifier.zuZA.locale!)
+ #expect(Locale.zhCN == LocaleIdentifier.zhCN.locale!)
+ #expect(Locale.zhTW == LocaleIdentifier.zhTW.locale!)
+ #expect(Locale.paIN == LocaleIdentifier.paIN.locale!)
+ #expect(Locale.azAZ == LocaleIdentifier.azAZ.locale!)
+ #expect(Locale.suID == LocaleIdentifier.suID.locale!)
+ #expect(Locale.cebPH == LocaleIdentifier.cebPH.locale!)
+ #expect(Locale.srRS == LocaleIdentifier.srRS.locale!)
+ }
+}
diff --git a/Tests/EasyCoreTests/LocaleIdentifierTests.swift b/Tests/EasyCoreTests/LocaleIdentifierTests.swift
new file mode 100644
index 0000000..306cab5
--- /dev/null
+++ b/Tests/EasyCoreTests/LocaleIdentifierTests.swift
@@ -0,0 +1,451 @@
+import Testing
+
+@testable import EasyCore
+
+@Suite("LocaleIdentifier rawValues")
+struct LocaleIdentifierRawValueTests {
+
+ @Test("All rawValues match expected strings")
+ func rawValuesMatchExpected() {
+ #expect(LocaleIdentifier.aaDJ.rawValue == "aa_DJ")
+ #expect(LocaleIdentifier.aaER.rawValue == "aa_ER")
+ #expect(LocaleIdentifier.aaET.rawValue == "aa_ET")
+ #expect(LocaleIdentifier.abGE.rawValue == "ab_GE")
+ #expect(LocaleIdentifier.afNA.rawValue == "af_NA")
+ #expect(LocaleIdentifier.afZA.rawValue == "af_ZA")
+ #expect(LocaleIdentifier.akGH.rawValue == "ak_GH")
+ #expect(LocaleIdentifier.amET.rawValue == "am_ET")
+ #expect(LocaleIdentifier.anES.rawValue == "an_ES")
+ #expect(LocaleIdentifier.arAE.rawValue == "ar_AE")
+ #expect(LocaleIdentifier.arBH.rawValue == "ar_BH")
+ #expect(LocaleIdentifier.arDJ.rawValue == "ar_DJ")
+ #expect(LocaleIdentifier.arDZ.rawValue == "ar_DZ")
+ #expect(LocaleIdentifier.arEG.rawValue == "ar_EG")
+ #expect(LocaleIdentifier.arEH.rawValue == "ar_EH")
+ #expect(LocaleIdentifier.arER.rawValue == "ar_ER")
+ #expect(LocaleIdentifier.arIL.rawValue == "ar_IL")
+ #expect(LocaleIdentifier.arIQ.rawValue == "ar_IQ")
+ #expect(LocaleIdentifier.arJO.rawValue == "ar_JO")
+ #expect(LocaleIdentifier.arKM.rawValue == "ar_KM")
+ #expect(LocaleIdentifier.arKW.rawValue == "ar_KW")
+ #expect(LocaleIdentifier.arLB.rawValue == "ar_LB")
+ #expect(LocaleIdentifier.arLY.rawValue == "ar_LY")
+ #expect(LocaleIdentifier.arMA.rawValue == "ar_MA")
+ #expect(LocaleIdentifier.arMR.rawValue == "ar_MR")
+ #expect(LocaleIdentifier.arOM.rawValue == "ar_OM")
+ #expect(LocaleIdentifier.arPS.rawValue == "ar_PS")
+ #expect(LocaleIdentifier.arQA.rawValue == "ar_QA")
+ #expect(LocaleIdentifier.arSA.rawValue == "ar_SA")
+ #expect(LocaleIdentifier.arSD.rawValue == "ar_SD")
+ #expect(LocaleIdentifier.arSO.rawValue == "ar_SO")
+ #expect(LocaleIdentifier.arSS.rawValue == "ar_SS")
+ #expect(LocaleIdentifier.arSY.rawValue == "ar_SY")
+ #expect(LocaleIdentifier.arTD.rawValue == "ar_TD")
+ #expect(LocaleIdentifier.arTN.rawValue == "ar_TN")
+ #expect(LocaleIdentifier.arYE.rawValue == "ar_YE")
+ #expect(LocaleIdentifier.asIN.rawValue == "as_IN")
+ #expect(LocaleIdentifier.baRU.rawValue == "ba_RU")
+ #expect(LocaleIdentifier.beBY.rawValue == "be_BY")
+ #expect(LocaleIdentifier.bgBG.rawValue == "bg_BG")
+ #expect(LocaleIdentifier.bmML.rawValue == "bm_ML")
+ #expect(LocaleIdentifier.bnBD.rawValue == "bn_BD")
+ #expect(LocaleIdentifier.bnIN.rawValue == "bn_IN")
+ #expect(LocaleIdentifier.boCN.rawValue == "bo_CN")
+ #expect(LocaleIdentifier.boIN.rawValue == "bo_IN")
+ #expect(LocaleIdentifier.brFR.rawValue == "br_FR")
+ #expect(LocaleIdentifier.caAD.rawValue == "ca_AD")
+ #expect(LocaleIdentifier.caES.rawValue == "ca_ES")
+ #expect(LocaleIdentifier.caESVALENCIA.rawValue == "ca_ES_VALENCIA")
+ #expect(LocaleIdentifier.caFR.rawValue == "ca_FR")
+ #expect(LocaleIdentifier.caIT.rawValue == "ca_IT")
+ #expect(LocaleIdentifier.ceRU.rawValue == "ce_RU")
+ #expect(LocaleIdentifier.coFR.rawValue == "co_FR")
+ #expect(LocaleIdentifier.csCZ.rawValue == "cs_CZ")
+ #expect(LocaleIdentifier.cuRU.rawValue == "cu_RU")
+ #expect(LocaleIdentifier.cvRU.rawValue == "cv_RU")
+ #expect(LocaleIdentifier.cyGB.rawValue == "cy_GB")
+ #expect(LocaleIdentifier.daDK.rawValue == "da_DK")
+ #expect(LocaleIdentifier.daGL.rawValue == "da_GL")
+ #expect(LocaleIdentifier.deAT.rawValue == "de_AT")
+ #expect(LocaleIdentifier.deBE.rawValue == "de_BE")
+ #expect(LocaleIdentifier.deCH.rawValue == "de_CH")
+ #expect(LocaleIdentifier.deDE.rawValue == "de_DE")
+ #expect(LocaleIdentifier.deIT.rawValue == "de_IT")
+ #expect(LocaleIdentifier.deLI.rawValue == "de_LI")
+ #expect(LocaleIdentifier.deLU.rawValue == "de_LU")
+ #expect(LocaleIdentifier.dvMV.rawValue == "dv_MV")
+ #expect(LocaleIdentifier.dzBT.rawValue == "dz_BT")
+ #expect(LocaleIdentifier.eeGH.rawValue == "ee_GH")
+ #expect(LocaleIdentifier.eeTG.rawValue == "ee_TG")
+ #expect(LocaleIdentifier.elCY.rawValue == "el_CY")
+ #expect(LocaleIdentifier.elGR.rawValue == "el_GR")
+ #expect(LocaleIdentifier.enAE.rawValue == "en_AE")
+ #expect(LocaleIdentifier.enAG.rawValue == "en_AG")
+ #expect(LocaleIdentifier.enAI.rawValue == "en_AI")
+ #expect(LocaleIdentifier.enAS.rawValue == "en_AS")
+ #expect(LocaleIdentifier.enAT.rawValue == "en_AT")
+ #expect(LocaleIdentifier.enAU.rawValue == "en_AU")
+ #expect(LocaleIdentifier.enBB.rawValue == "en_BB")
+ #expect(LocaleIdentifier.enBE.rawValue == "en_BE")
+ #expect(LocaleIdentifier.enBI.rawValue == "en_BI")
+ #expect(LocaleIdentifier.enBM.rawValue == "en_BM")
+ #expect(LocaleIdentifier.enBS.rawValue == "en_BS")
+ #expect(LocaleIdentifier.enBW.rawValue == "en_BW")
+ #expect(LocaleIdentifier.enBZ.rawValue == "en_BZ")
+ #expect(LocaleIdentifier.enCA.rawValue == "en_CA")
+ #expect(LocaleIdentifier.enCC.rawValue == "en_CC")
+ #expect(LocaleIdentifier.enCH.rawValue == "en_CH")
+ #expect(LocaleIdentifier.enCK.rawValue == "en_CK")
+ #expect(LocaleIdentifier.enCM.rawValue == "en_CM")
+ #expect(LocaleIdentifier.enCX.rawValue == "en_CX")
+ #expect(LocaleIdentifier.enCY.rawValue == "en_CY")
+ #expect(LocaleIdentifier.enDE.rawValue == "en_DE")
+ #expect(LocaleIdentifier.enDG.rawValue == "en_DG")
+ #expect(LocaleIdentifier.enDK.rawValue == "en_DK")
+ #expect(LocaleIdentifier.enDM.rawValue == "en_DM")
+ #expect(LocaleIdentifier.enER.rawValue == "en_ER")
+ #expect(LocaleIdentifier.enFI.rawValue == "en_FI")
+ #expect(LocaleIdentifier.enFJ.rawValue == "en_FJ")
+ #expect(LocaleIdentifier.enFK.rawValue == "en_FK")
+ #expect(LocaleIdentifier.enFM.rawValue == "en_FM")
+ #expect(LocaleIdentifier.enGB.rawValue == "en_GB")
+ #expect(LocaleIdentifier.enGD.rawValue == "en_GD")
+ #expect(LocaleIdentifier.enGG.rawValue == "en_GG")
+ #expect(LocaleIdentifier.enGH.rawValue == "en_GH")
+ #expect(LocaleIdentifier.enGI.rawValue == "en_GI")
+ #expect(LocaleIdentifier.enGM.rawValue == "en_GM")
+ #expect(LocaleIdentifier.enGU.rawValue == "en_GU")
+ #expect(LocaleIdentifier.enGY.rawValue == "en_GY")
+ #expect(LocaleIdentifier.enHK.rawValue == "en_HK")
+ #expect(LocaleIdentifier.enID.rawValue == "en_ID")
+ #expect(LocaleIdentifier.enIE.rawValue == "en_IE")
+ #expect(LocaleIdentifier.enIL.rawValue == "en_IL")
+ #expect(LocaleIdentifier.enIM.rawValue == "en_IM")
+ #expect(LocaleIdentifier.enIN.rawValue == "en_IN")
+ #expect(LocaleIdentifier.enIO.rawValue == "en_IO")
+ #expect(LocaleIdentifier.enJE.rawValue == "en_JE")
+ #expect(LocaleIdentifier.enJM.rawValue == "en_JM")
+ #expect(LocaleIdentifier.enKE.rawValue == "en_KE")
+ #expect(LocaleIdentifier.enKI.rawValue == "en_KI")
+ #expect(LocaleIdentifier.enKN.rawValue == "en_KN")
+ #expect(LocaleIdentifier.enKY.rawValue == "en_KY")
+ #expect(LocaleIdentifier.enLC.rawValue == "en_LC")
+ #expect(LocaleIdentifier.enLR.rawValue == "en_LR")
+ #expect(LocaleIdentifier.enLS.rawValue == "en_LS")
+ #expect(LocaleIdentifier.enMG.rawValue == "en_MG")
+ #expect(LocaleIdentifier.enMH.rawValue == "en_MH")
+ #expect(LocaleIdentifier.enMO.rawValue == "en_MO")
+ #expect(LocaleIdentifier.enMP.rawValue == "en_MP")
+ #expect(LocaleIdentifier.enMS.rawValue == "en_MS")
+ #expect(LocaleIdentifier.enMT.rawValue == "en_MT")
+ #expect(LocaleIdentifier.enMU.rawValue == "en_MU")
+ #expect(LocaleIdentifier.enMV.rawValue == "en_MV")
+ #expect(LocaleIdentifier.enMW.rawValue == "en_MW")
+ #expect(LocaleIdentifier.enMY.rawValue == "en_MY")
+ #expect(LocaleIdentifier.enNA.rawValue == "en_NA")
+ #expect(LocaleIdentifier.enNF.rawValue == "en_NF")
+ #expect(LocaleIdentifier.enNG.rawValue == "en_NG")
+ #expect(LocaleIdentifier.enNL.rawValue == "en_NL")
+ #expect(LocaleIdentifier.enNR.rawValue == "en_NR")
+ #expect(LocaleIdentifier.enNU.rawValue == "en_NU")
+ #expect(LocaleIdentifier.enNZ.rawValue == "en_NZ")
+ #expect(LocaleIdentifier.enPG.rawValue == "en_PG")
+ #expect(LocaleIdentifier.enPH.rawValue == "en_PH")
+ #expect(LocaleIdentifier.enPK.rawValue == "en_PK")
+ #expect(LocaleIdentifier.enPN.rawValue == "en_PN")
+ #expect(LocaleIdentifier.enPR.rawValue == "en_PR")
+ #expect(LocaleIdentifier.enPW.rawValue == "en_PW")
+ #expect(LocaleIdentifier.enRW.rawValue == "en_RW")
+ #expect(LocaleIdentifier.enSB.rawValue == "en_SB")
+ #expect(LocaleIdentifier.enSC.rawValue == "en_SC")
+ #expect(LocaleIdentifier.enSD.rawValue == "en_SD")
+ #expect(LocaleIdentifier.enSE.rawValue == "en_SE")
+ #expect(LocaleIdentifier.enSG.rawValue == "en_SG")
+ #expect(LocaleIdentifier.enSH.rawValue == "en_SH")
+ #expect(LocaleIdentifier.enSI.rawValue == "en_SI")
+ #expect(LocaleIdentifier.enSL.rawValue == "en_SL")
+ #expect(LocaleIdentifier.enSS.rawValue == "en_SS")
+ #expect(LocaleIdentifier.enSX.rawValue == "en_SX")
+ #expect(LocaleIdentifier.enSZ.rawValue == "en_SZ")
+ #expect(LocaleIdentifier.enTC.rawValue == "en_TC")
+ #expect(LocaleIdentifier.enTK.rawValue == "en_TK")
+ #expect(LocaleIdentifier.enTO.rawValue == "en_TO")
+ #expect(LocaleIdentifier.enTT.rawValue == "en_TT")
+ #expect(LocaleIdentifier.enTV.rawValue == "en_TV")
+ #expect(LocaleIdentifier.enTZ.rawValue == "en_TZ")
+ #expect(LocaleIdentifier.enUG.rawValue == "en_UG")
+ #expect(LocaleIdentifier.enUM.rawValue == "en_UM")
+ #expect(LocaleIdentifier.enUS.rawValue == "en_US")
+ #expect(LocaleIdentifier.enUSPOSIX.rawValue == "en_US_POSIX")
+ #expect(LocaleIdentifier.enVC.rawValue == "en_VC")
+ #expect(LocaleIdentifier.enVG.rawValue == "en_VG")
+ #expect(LocaleIdentifier.enVI.rawValue == "en_VI")
+ #expect(LocaleIdentifier.enVU.rawValue == "en_VU")
+ #expect(LocaleIdentifier.enWS.rawValue == "en_WS")
+ #expect(LocaleIdentifier.enZA.rawValue == "en_ZA")
+ #expect(LocaleIdentifier.enZM.rawValue == "en_ZM")
+ #expect(LocaleIdentifier.enZW.rawValue == "en_ZW")
+ #expect(LocaleIdentifier.esAR.rawValue == "es_AR")
+ #expect(LocaleIdentifier.esBO.rawValue == "es_BO")
+ #expect(LocaleIdentifier.esBR.rawValue == "es_BR")
+ #expect(LocaleIdentifier.esBZ.rawValue == "es_BZ")
+ #expect(LocaleIdentifier.esCL.rawValue == "es_CL")
+ #expect(LocaleIdentifier.esCO.rawValue == "es_CO")
+ #expect(LocaleIdentifier.esCR.rawValue == "es_CR")
+ #expect(LocaleIdentifier.esCU.rawValue == "es_CU")
+ #expect(LocaleIdentifier.esDO.rawValue == "es_DO")
+ #expect(LocaleIdentifier.esEA.rawValue == "es_EA")
+ #expect(LocaleIdentifier.esEC.rawValue == "es_EC")
+ #expect(LocaleIdentifier.esES.rawValue == "es_ES")
+ #expect(LocaleIdentifier.esGQ.rawValue == "es_GQ")
+ #expect(LocaleIdentifier.esGT.rawValue == "es_GT")
+ #expect(LocaleIdentifier.esHN.rawValue == "es_HN")
+ #expect(LocaleIdentifier.esIC.rawValue == "es_IC")
+ #expect(LocaleIdentifier.esMX.rawValue == "es_MX")
+ #expect(LocaleIdentifier.esNI.rawValue == "es_NI")
+ #expect(LocaleIdentifier.esPA.rawValue == "es_PA")
+ #expect(LocaleIdentifier.esPE.rawValue == "es_PE")
+ #expect(LocaleIdentifier.esPH.rawValue == "es_PH")
+ #expect(LocaleIdentifier.esPR.rawValue == "es_PR")
+ #expect(LocaleIdentifier.esPY.rawValue == "es_PY")
+ #expect(LocaleIdentifier.esSV.rawValue == "es_SV")
+ #expect(LocaleIdentifier.esUS.rawValue == "es_US")
+ #expect(LocaleIdentifier.esUY.rawValue == "es_UY")
+ #expect(LocaleIdentifier.esVE.rawValue == "es_VE")
+ #expect(LocaleIdentifier.etEE.rawValue == "et_EE")
+ #expect(LocaleIdentifier.euES.rawValue == "eu_ES")
+ #expect(LocaleIdentifier.faAF.rawValue == "fa_AF")
+ #expect(LocaleIdentifier.faIR.rawValue == "fa_IR")
+ #expect(LocaleIdentifier.fiFI.rawValue == "fi_FI")
+ #expect(LocaleIdentifier.foDK.rawValue == "fo_DK")
+ #expect(LocaleIdentifier.foFO.rawValue == "fo_FO")
+ #expect(LocaleIdentifier.frBE.rawValue == "fr_BE")
+ #expect(LocaleIdentifier.frBF.rawValue == "fr_BF")
+ #expect(LocaleIdentifier.frBI.rawValue == "fr_BI")
+ #expect(LocaleIdentifier.frBJ.rawValue == "fr_BJ")
+ #expect(LocaleIdentifier.frBL.rawValue == "fr_BL")
+ #expect(LocaleIdentifier.frCA.rawValue == "fr_CA")
+ #expect(LocaleIdentifier.frCD.rawValue == "fr_CD")
+ #expect(LocaleIdentifier.frCF.rawValue == "fr_CF")
+ #expect(LocaleIdentifier.frCG.rawValue == "fr_CG")
+ #expect(LocaleIdentifier.frCH.rawValue == "fr_CH")
+ #expect(LocaleIdentifier.frCI.rawValue == "fr_CI")
+ #expect(LocaleIdentifier.frCM.rawValue == "fr_CM")
+ #expect(LocaleIdentifier.frDJ.rawValue == "fr_DJ")
+ #expect(LocaleIdentifier.frDZ.rawValue == "fr_DZ")
+ #expect(LocaleIdentifier.frFR.rawValue == "fr_FR")
+ #expect(LocaleIdentifier.frGA.rawValue == "fr_GA")
+ #expect(LocaleIdentifier.frGF.rawValue == "fr_GF")
+ #expect(LocaleIdentifier.frGN.rawValue == "fr_GN")
+ #expect(LocaleIdentifier.frGP.rawValue == "fr_GP")
+ #expect(LocaleIdentifier.frGQ.rawValue == "fr_GQ")
+ #expect(LocaleIdentifier.frHT.rawValue == "fr_HT")
+ #expect(LocaleIdentifier.frKM.rawValue == "fr_KM")
+ #expect(LocaleIdentifier.frLU.rawValue == "fr_LU")
+ #expect(LocaleIdentifier.frMA.rawValue == "fr_MA")
+ #expect(LocaleIdentifier.frMC.rawValue == "fr_MC")
+ #expect(LocaleIdentifier.frMF.rawValue == "fr_MF")
+ #expect(LocaleIdentifier.frMG.rawValue == "fr_MG")
+ #expect(LocaleIdentifier.frML.rawValue == "fr_ML")
+ #expect(LocaleIdentifier.frMQ.rawValue == "fr_MQ")
+ #expect(LocaleIdentifier.frMR.rawValue == "fr_MR")
+ #expect(LocaleIdentifier.frMU.rawValue == "fr_MU")
+ #expect(LocaleIdentifier.frNC.rawValue == "fr_NC")
+ #expect(LocaleIdentifier.frNE.rawValue == "fr_NE")
+ #expect(LocaleIdentifier.frPF.rawValue == "fr_PF")
+ #expect(LocaleIdentifier.frPM.rawValue == "fr_PM")
+ #expect(LocaleIdentifier.frRE.rawValue == "fr_RE")
+ #expect(LocaleIdentifier.frRW.rawValue == "fr_RW")
+ #expect(LocaleIdentifier.frSC.rawValue == "fr_SC")
+ #expect(LocaleIdentifier.frSN.rawValue == "fr_SN")
+ #expect(LocaleIdentifier.frSY.rawValue == "fr_SY")
+ #expect(LocaleIdentifier.frTD.rawValue == "fr_TD")
+ #expect(LocaleIdentifier.frTG.rawValue == "fr_TG")
+ #expect(LocaleIdentifier.frTN.rawValue == "fr_TN")
+ #expect(LocaleIdentifier.frVU.rawValue == "fr_VU")
+ #expect(LocaleIdentifier.frWF.rawValue == "fr_WF")
+ #expect(LocaleIdentifier.frYT.rawValue == "fr_YT")
+ #expect(LocaleIdentifier.fyNL.rawValue == "fy_NL")
+ #expect(LocaleIdentifier.gaGB.rawValue == "ga_GB")
+ #expect(LocaleIdentifier.gaIE.rawValue == "ga_IE")
+ #expect(LocaleIdentifier.gdGB.rawValue == "gd_GB")
+ #expect(LocaleIdentifier.glES.rawValue == "gl_ES")
+ #expect(LocaleIdentifier.gnPY.rawValue == "gn_PY")
+ #expect(LocaleIdentifier.guIN.rawValue == "gu_IN")
+ #expect(LocaleIdentifier.gvIM.rawValue == "gv_IM")
+ #expect(LocaleIdentifier.haGH.rawValue == "ha_GH")
+ #expect(LocaleIdentifier.haNE.rawValue == "ha_NE")
+ #expect(LocaleIdentifier.haNG.rawValue == "ha_NG")
+ #expect(LocaleIdentifier.heIL.rawValue == "he_IL")
+ #expect(LocaleIdentifier.hiIN.rawValue == "hi_IN")
+ #expect(LocaleIdentifier.hrBA.rawValue == "hr_BA")
+ #expect(LocaleIdentifier.hrHR.rawValue == "hr_HR")
+ #expect(LocaleIdentifier.huHU.rawValue == "hu_HU")
+ #expect(LocaleIdentifier.hyAM.rawValue == "hy_AM")
+ #expect(LocaleIdentifier.idID.rawValue == "id_ID")
+ #expect(LocaleIdentifier.ieEE.rawValue == "ie_EE")
+ #expect(LocaleIdentifier.igNG.rawValue == "ig_NG")
+ #expect(LocaleIdentifier.iiCN.rawValue == "ii_CN")
+ #expect(LocaleIdentifier.isIS.rawValue == "is_IS")
+ #expect(LocaleIdentifier.itCH.rawValue == "it_CH")
+ #expect(LocaleIdentifier.itIT.rawValue == "it_IT")
+ #expect(LocaleIdentifier.itSM.rawValue == "it_SM")
+ #expect(LocaleIdentifier.itVA.rawValue == "it_VA")
+ #expect(LocaleIdentifier.iuCA.rawValue == "iu_CA")
+ #expect(LocaleIdentifier.jaJP.rawValue == "ja_JP")
+ #expect(LocaleIdentifier.jvID.rawValue == "jv_ID")
+ #expect(LocaleIdentifier.kaGE.rawValue == "ka_GE")
+ #expect(LocaleIdentifier.kiKE.rawValue == "ki_KE")
+ #expect(LocaleIdentifier.kkKZ.rawValue == "kk_KZ")
+ #expect(LocaleIdentifier.klGL.rawValue == "kl_GL")
+ #expect(LocaleIdentifier.kmKH.rawValue == "km_KH")
+ #expect(LocaleIdentifier.knIN.rawValue == "kn_IN")
+ #expect(LocaleIdentifier.koCN.rawValue == "ko_CN")
+ #expect(LocaleIdentifier.koKP.rawValue == "ko_KP")
+ #expect(LocaleIdentifier.koKR.rawValue == "ko_KR")
+ #expect(LocaleIdentifier.kuTR.rawValue == "ku_TR")
+ #expect(LocaleIdentifier.kwGB.rawValue == "kw_GB")
+ #expect(LocaleIdentifier.kyKG.rawValue == "ky_KG")
+ #expect(LocaleIdentifier.laVA.rawValue == "la_VA")
+ #expect(LocaleIdentifier.lbLU.rawValue == "lb_LU")
+ #expect(LocaleIdentifier.lgUG.rawValue == "lg_UG")
+ #expect(LocaleIdentifier.lnAO.rawValue == "ln_AO")
+ #expect(LocaleIdentifier.lnCD.rawValue == "ln_CD")
+ #expect(LocaleIdentifier.lnCF.rawValue == "ln_CF")
+ #expect(LocaleIdentifier.lnCG.rawValue == "ln_CG")
+ #expect(LocaleIdentifier.loLA.rawValue == "lo_LA")
+ #expect(LocaleIdentifier.ltLT.rawValue == "lt_LT")
+ #expect(LocaleIdentifier.luCD.rawValue == "lu_CD")
+ #expect(LocaleIdentifier.lvLV.rawValue == "lv_LV")
+ #expect(LocaleIdentifier.mgMG.rawValue == "mg_MG")
+ #expect(LocaleIdentifier.miNZ.rawValue == "mi_NZ")
+ #expect(LocaleIdentifier.mkMK.rawValue == "mk_MK")
+ #expect(LocaleIdentifier.mlIN.rawValue == "ml_IN")
+ #expect(LocaleIdentifier.mnMN.rawValue == "mn_MN")
+ #expect(LocaleIdentifier.mrIN.rawValue == "mr_IN")
+ #expect(LocaleIdentifier.msBN.rawValue == "ms_BN")
+ #expect(LocaleIdentifier.msID.rawValue == "ms_ID")
+ #expect(LocaleIdentifier.msMY.rawValue == "ms_MY")
+ #expect(LocaleIdentifier.msSG.rawValue == "ms_SG")
+ #expect(LocaleIdentifier.mtMT.rawValue == "mt_MT")
+ #expect(LocaleIdentifier.myMM.rawValue == "my_MM")
+ #expect(LocaleIdentifier.nbNO.rawValue == "nb_NO")
+ #expect(LocaleIdentifier.nbSJ.rawValue == "nb_SJ")
+ #expect(LocaleIdentifier.ndZW.rawValue == "nd_ZW")
+ #expect(LocaleIdentifier.neIN.rawValue == "ne_IN")
+ #expect(LocaleIdentifier.neNP.rawValue == "ne_NP")
+ #expect(LocaleIdentifier.nlAW.rawValue == "nl_AW")
+ #expect(LocaleIdentifier.nlBE.rawValue == "nl_BE")
+ #expect(LocaleIdentifier.nlBQ.rawValue == "nl_BQ")
+ #expect(LocaleIdentifier.nlCW.rawValue == "nl_CW")
+ #expect(LocaleIdentifier.nlNL.rawValue == "nl_NL")
+ #expect(LocaleIdentifier.nlSR.rawValue == "nl_SR")
+ #expect(LocaleIdentifier.nlSX.rawValue == "nl_SX")
+ #expect(LocaleIdentifier.nnNO.rawValue == "nn_NO")
+ #expect(LocaleIdentifier.nrZA.rawValue == "nr_ZA")
+ #expect(LocaleIdentifier.nvUS.rawValue == "nv_US")
+ #expect(LocaleIdentifier.nyMW.rawValue == "ny_MW")
+ #expect(LocaleIdentifier.ocES.rawValue == "oc_ES")
+ #expect(LocaleIdentifier.ocFR.rawValue == "oc_FR")
+ #expect(LocaleIdentifier.omET.rawValue == "om_ET")
+ #expect(LocaleIdentifier.omKE.rawValue == "om_KE")
+ #expect(LocaleIdentifier.orIN.rawValue == "or_IN")
+ #expect(LocaleIdentifier.osGE.rawValue == "os_GE")
+ #expect(LocaleIdentifier.osRU.rawValue == "os_RU")
+ #expect(LocaleIdentifier.plPL.rawValue == "pl_PL")
+ #expect(LocaleIdentifier.psAF.rawValue == "ps_AF")
+ #expect(LocaleIdentifier.psPK.rawValue == "ps_PK")
+ #expect(LocaleIdentifier.ptAO.rawValue == "pt_AO")
+ #expect(LocaleIdentifier.ptBR.rawValue == "pt_BR")
+ #expect(LocaleIdentifier.ptCH.rawValue == "pt_CH")
+ #expect(LocaleIdentifier.ptCV.rawValue == "pt_CV")
+ #expect(LocaleIdentifier.ptGQ.rawValue == "pt_GQ")
+ #expect(LocaleIdentifier.ptGW.rawValue == "pt_GW")
+ #expect(LocaleIdentifier.ptLU.rawValue == "pt_LU")
+ #expect(LocaleIdentifier.ptMO.rawValue == "pt_MO")
+ #expect(LocaleIdentifier.ptMZ.rawValue == "pt_MZ")
+ #expect(LocaleIdentifier.ptPT.rawValue == "pt_PT")
+ #expect(LocaleIdentifier.ptST.rawValue == "pt_ST")
+ #expect(LocaleIdentifier.ptTL.rawValue == "pt_TL")
+ #expect(LocaleIdentifier.quBO.rawValue == "qu_BO")
+ #expect(LocaleIdentifier.quEC.rawValue == "qu_EC")
+ #expect(LocaleIdentifier.quPE.rawValue == "qu_PE")
+ #expect(LocaleIdentifier.rmCH.rawValue == "rm_CH")
+ #expect(LocaleIdentifier.rnBI.rawValue == "rn_BI")
+ #expect(LocaleIdentifier.roMD.rawValue == "ro_MD")
+ #expect(LocaleIdentifier.roRO.rawValue == "ro_RO")
+ #expect(LocaleIdentifier.ruBY.rawValue == "ru_BY")
+ #expect(LocaleIdentifier.ruKG.rawValue == "ru_KG")
+ #expect(LocaleIdentifier.ruKZ.rawValue == "ru_KZ")
+ #expect(LocaleIdentifier.ruMD.rawValue == "ru_MD")
+ #expect(LocaleIdentifier.ruRU.rawValue == "ru_RU")
+ #expect(LocaleIdentifier.ruUA.rawValue == "ru_UA")
+ #expect(LocaleIdentifier.rwRW.rawValue == "rw_RW")
+ #expect(LocaleIdentifier.saIN.rawValue == "sa_IN")
+ #expect(LocaleIdentifier.scIT.rawValue == "sc_IT")
+ #expect(LocaleIdentifier.seFI.rawValue == "se_FI")
+ #expect(LocaleIdentifier.seNO.rawValue == "se_NO")
+ #expect(LocaleIdentifier.seSE.rawValue == "se_SE")
+ #expect(LocaleIdentifier.sgCF.rawValue == "sg_CF")
+ #expect(LocaleIdentifier.siLK.rawValue == "si_LK")
+ #expect(LocaleIdentifier.skSK.rawValue == "sk_SK")
+ #expect(LocaleIdentifier.slSI.rawValue == "sl_SI")
+ #expect(LocaleIdentifier.snZW.rawValue == "sn_ZW")
+ #expect(LocaleIdentifier.soDJ.rawValue == "so_DJ")
+ #expect(LocaleIdentifier.soET.rawValue == "so_ET")
+ #expect(LocaleIdentifier.soKE.rawValue == "so_KE")
+ #expect(LocaleIdentifier.soSO.rawValue == "so_SO")
+ #expect(LocaleIdentifier.sqAL.rawValue == "sq_AL")
+ #expect(LocaleIdentifier.sqMK.rawValue == "sq_MK")
+ #expect(LocaleIdentifier.sqXK.rawValue == "sq_XK")
+ #expect(LocaleIdentifier.ssSZ.rawValue == "ss_SZ")
+ #expect(LocaleIdentifier.ssZA.rawValue == "ss_ZA")
+ #expect(LocaleIdentifier.stLS.rawValue == "st_LS")
+ #expect(LocaleIdentifier.stZA.rawValue == "st_ZA")
+ #expect(LocaleIdentifier.svAX.rawValue == "sv_AX")
+ #expect(LocaleIdentifier.svFI.rawValue == "sv_FI")
+ #expect(LocaleIdentifier.svSE.rawValue == "sv_SE")
+ #expect(LocaleIdentifier.swCD.rawValue == "sw_CD")
+ #expect(LocaleIdentifier.swKE.rawValue == "sw_KE")
+ #expect(LocaleIdentifier.swTZ.rawValue == "sw_TZ")
+ #expect(LocaleIdentifier.swUG.rawValue == "sw_UG")
+ #expect(LocaleIdentifier.taIN.rawValue == "ta_IN")
+ #expect(LocaleIdentifier.taLK.rawValue == "ta_LK")
+ #expect(LocaleIdentifier.taMY.rawValue == "ta_MY")
+ #expect(LocaleIdentifier.taSG.rawValue == "ta_SG")
+ #expect(LocaleIdentifier.teIN.rawValue == "te_IN")
+ #expect(LocaleIdentifier.tgTJ.rawValue == "tg_TJ")
+ #expect(LocaleIdentifier.thTH.rawValue == "th_TH")
+ #expect(LocaleIdentifier.tiER.rawValue == "ti_ER")
+ #expect(LocaleIdentifier.tiET.rawValue == "ti_ET")
+ #expect(LocaleIdentifier.tkTM.rawValue == "tk_TM")
+ #expect(LocaleIdentifier.tnBW.rawValue == "tn_BW")
+ #expect(LocaleIdentifier.tnZA.rawValue == "tn_ZA")
+ #expect(LocaleIdentifier.toTO.rawValue == "to_TO")
+ #expect(LocaleIdentifier.trCY.rawValue == "tr_CY")
+ #expect(LocaleIdentifier.trTR.rawValue == "tr_TR")
+ #expect(LocaleIdentifier.tsZA.rawValue == "ts_ZA")
+ #expect(LocaleIdentifier.ttRU.rawValue == "tt_RU")
+ #expect(LocaleIdentifier.ugCN.rawValue == "ug_CN")
+ #expect(LocaleIdentifier.ukUA.rawValue == "uk_UA")
+ #expect(LocaleIdentifier.urIN.rawValue == "ur_IN")
+ #expect(LocaleIdentifier.urPK.rawValue == "ur_PK")
+ #expect(LocaleIdentifier.veZA.rawValue == "ve_ZA")
+ #expect(LocaleIdentifier.viVN.rawValue == "vi_VN")
+ #expect(LocaleIdentifier.waBE.rawValue == "wa_BE")
+ #expect(LocaleIdentifier.woSN.rawValue == "wo_SN")
+ #expect(LocaleIdentifier.xhZA.rawValue == "xh_ZA")
+ #expect(LocaleIdentifier.yiUA.rawValue == "yi_UA")
+ #expect(LocaleIdentifier.yoBJ.rawValue == "yo_BJ")
+ #expect(LocaleIdentifier.yoNG.rawValue == "yo_NG")
+ #expect(LocaleIdentifier.zaCN.rawValue == "za_CN")
+ #expect(LocaleIdentifier.zuZA.rawValue == "zu_ZA")
+ #expect(LocaleIdentifier.zhCN.rawValue == "zh_CN")
+ #expect(LocaleIdentifier.zhTW.rawValue == "zh_TW")
+ #expect(LocaleIdentifier.paIN.rawValue == "pa_IN")
+ #expect(LocaleIdentifier.azAZ.rawValue == "az_AZ")
+ #expect(LocaleIdentifier.suID.rawValue == "su_ID")
+ #expect(LocaleIdentifier.cebPH.rawValue == "ceb_PH")
+ #expect(LocaleIdentifier.srRS.rawValue == "sr_RS")
+ }
+}
diff --git a/Tests/EasyCoreTests/LocaleTests.swift b/Tests/EasyCoreTests/LocaleTests.swift
new file mode 100644
index 0000000..0661741
--- /dev/null
+++ b/Tests/EasyCoreTests/LocaleTests.swift
@@ -0,0 +1,12 @@
+import Testing
+import Foundation
+
+@testable import EasyCore
+
+@Suite("Locale")
+struct LocaleExtension {
+ @Test
+ func locales() {
+ #expect(Locale.locales.count == 440)
+ }
+}
diff --git a/Tests/EasyCoreTests/MainThreadTests.swift b/Tests/EasyCoreTests/MainThreadTests.swift
new file mode 100644
index 0000000..20511bf
--- /dev/null
+++ b/Tests/EasyCoreTests/MainThreadTests.swift
@@ -0,0 +1,90 @@
+import Foundation
+import Testing
+
+@testable import EasyCore
+
+@Suite("MainThread")
+struct MainThreadTests {
+ private actor Flag {
+ private(set) var value = false
+ func setTrue() { value = true }
+ func get() -> Bool { value }
+ }
+
+ @Test("asyncSafe executes immediately when already on main thread")
+ func asyncSafeOnMainThread() async throws {
+ let flag = Flag()
+
+ MainThread.asyncSafe {
+ Task { await flag.setTrue() }
+ }
+
+ try await Task.sleep(nanoseconds: 300_000_000) // 0.3s
+ #expect(await flag.get())
+ }
+
+ @Test("asyncSafe executes on main thread when called from background thread")
+ func asyncSafeFromBackground() async throws {
+ let flag = Flag()
+
+ try await withCheckedThrowingContinuation { continuation in
+ DispatchQueue.global().async {
+ MainThread.asyncSafe {
+ guard Thread.isMainThread else { return }
+ Task { @MainActor in
+ await flag.setTrue()
+ continuation.resume()
+ }
+ }
+ }
+ }
+
+ #expect(await flag.get())
+ }
+
+ @Test("asyncSafe executes in main thread when on main thread")
+ func asyncSafeFromMain() async throws {
+ let flag = Flag()
+
+ try await withCheckedThrowingContinuation { continuation in
+ DispatchQueue.main.async {
+ MainThread.asyncSafe {
+ guard Thread.isMainThread else { return }
+ Task { await flag.setTrue() }
+
+ // resume needs to run on main thread to avoid thread race
+ DispatchQueue.main.async {
+ continuation.resume()
+ }
+ }
+ }
+ }
+
+ #expect(await flag.get())
+ }
+
+ @Test("asyncAfter executes block after specified delay")
+ func asyncAfterExecutes() async throws {
+ let flag = Flag()
+
+ MainThread.asyncAfter(seconds: 0.2) {
+ Task { await flag.setTrue() }
+ }
+
+ try await Task.sleep(nanoseconds: 300_000_000) // 0.3s
+ #expect(await flag.get())
+ }
+
+ @Test("asyncAfter runs block on main thread")
+ func asyncAfterRunsOnMainThread() async throws {
+ let flag = Flag()
+
+ MainThread.asyncAfter(seconds: 0.1) {
+ guard Thread.isMainThread else { return }
+ Task { await flag.setTrue() }
+ }
+
+ try await Task.sleep(nanoseconds: 200_000_000)
+ #expect(await flag.get())
+ }
+}
diff --git a/Tests/EasyCoreTests/NumberExtensionTests.swift b/Tests/EasyCoreTests/NumberExtensionTests.swift
new file mode 100644
index 0000000..7b1e603
--- /dev/null
+++ b/Tests/EasyCoreTests/NumberExtensionTests.swift
@@ -0,0 +1,183 @@
+import Testing
+
+@testable import EasyCore
+
+@Suite("Number")
+struct NumberTests {
+ @Suite("Double validations")
+ struct DoubleValidationTests {
+
+ @Test("isPositive returns true for values greater than zero")
+ func positiveValue() {
+ let value: Double = 3.14
+ #expect(value.isPositive)
+ }
+
+ @Test("isPositive returns false for zero or negative values")
+ func notPositive() {
+ #expect((-1.0).isPositive == false)
+ #expect(0.0.isPositive == false)
+ }
+
+ @Test("isNegative returns true for values less than zero")
+ func negativeValue() {
+ let value: Double = -2.5
+ #expect(value.isNegative)
+ }
+
+ @Test("isNegative returns false for zero or positive values")
+ func notNegative() {
+ #expect(1.0.isNegative == false)
+ #expect(0.0.isNegative == false)
+ }
+
+ @Test("isZero returns true for exactly 0.0")
+ func isZeroValue() {
+ let value: Double = 0.0
+ #expect(value.isZero)
+ }
+
+ @Test("isZero returns false for non-zero values")
+ func notZero() {
+ #expect(1.0.isZero == false)
+ #expect((-0.1).isZero == false)
+ }
+
+ @Test("isInRange returns true for values within range")
+ func inRange() {
+ let value: Double = 5.5
+ #expect(value.isInRange(5.0...6.0))
+ }
+
+ @Test("isInRange returns false for values outside range")
+ func outOfRange() {
+ let value: Double = 7.0
+ #expect(value.isInRange(1.0...5.0) == false)
+ }
+
+ @Test("isInRange returns true for boundary values")
+ func boundaryValues() {
+ let value1: Double = 10.0
+ let value2: Double = 20.0
+ #expect(value1.isInRange(10.0...20.0))
+ #expect(value2.isInRange(10.0...20.0))
+ }
+ }
+
+ @Suite("Float validations")
+ struct FloatValidationTests {
+
+ @Test("isPositive returns true for positive values")
+ func isPositiveTrue() {
+ let value: Float = 3.14
+ #expect(value.isPositive)
+ }
+
+ @Test("isPositive returns false for zero and negative values")
+ func isPositiveFalse() {
+ #expect(Float(0).isPositive == false)
+ #expect(Float(-1).isPositive == false)
+ }
+
+ @Test("isNegative returns true for negative values")
+ func isNegativeTrue() {
+ let value: Float = -5.6
+ #expect(value.isNegative)
+ }
+
+ @Test("isNegative returns false for zero and positive values")
+ func isNegativeFalse() {
+ #expect(Float(0).isNegative == false)
+ #expect(Float(2.2).isNegative == false)
+ }
+
+ @Test("isZero returns true for exactly 0.0")
+ func isZeroTrue() {
+ let value: Float = 0.0
+ #expect(value.isZero)
+ }
+
+ @Test("isZero returns false for non-zero values")
+ func isZeroFalse() {
+ #expect(Float(1).isZero == false)
+ #expect(Float(-0.001).isZero == false)
+ }
+
+ @Test("isInRange returns true for values within range")
+ func inRangeTrue() {
+ let value: Float = 7.5
+ #expect(value.isInRange(5.0...10.0))
+ }
+
+ @Test("isInRange returns false for values outside range")
+ func inRangeFalse() {
+ let value: Float = 11.0
+ #expect(value.isInRange(0.0...10.0) == false)
+ }
+
+ @Test("isInRange returns true for values at the boundaries")
+ func inRangeBoundaries() {
+ let value1: Float = 0.0
+ let value2: Float = 10.0
+ #expect(value1.isInRange(0.0...10.0))
+ #expect(value2.isInRange(0.0...10.0))
+ }
+ }
+
+ @Suite("Int validations")
+ struct IntValidationTests {
+
+ @Test("isPositive returns true for values greater than zero")
+ func isPositiveTrue() {
+ let value = 10
+ #expect(value.isPositive)
+ }
+
+ @Test("isPositive returns false for zero and negative values")
+ func isPositiveFalse() {
+ #expect(0.isPositive == false)
+ #expect((-5).isPositive == false)
+ }
+
+ @Test("isNegative returns true for values less than zero")
+ func isNegativeTrue() {
+ let value = -42
+ #expect(value.isNegative)
+ }
+
+ @Test("isNegative returns false for zero and positive values")
+ func isNegativeFalse() {
+ #expect(0.isNegative == false)
+ #expect(3.isNegative == false)
+ }
+
+ @Test("isZero returns true for exactly zero")
+ func isZeroTrue() {
+ #expect(0.isZero)
+ }
+
+ @Test("isZero returns false for non-zero values")
+ func isZeroFalse() {
+ #expect(1.isZero == false)
+ #expect((-1).isZero == false)
+ }
+
+ @Test("isInRange returns true for values within the range")
+ func inRangeTrue() {
+ let value = 50
+ #expect(value.isInRange(0...100))
+ }
+
+ @Test("isInRange returns false for values outside the range")
+ func inRangeFalse() {
+ let value = -10
+ #expect(value.isInRange(0...100) == false)
+ }
+
+ @Test("isInRange returns true for values at the edges of the range")
+ func inRangeBoundary() {
+ #expect(0.isInRange(0...10))
+ #expect(10.isInRange(0...10))
+ }
+ }
+}
diff --git a/Tests/EasyCoreTests/OptionalTests.swift b/Tests/EasyCoreTests/OptionalTests.swift
new file mode 100644
index 0000000..4f581c9
--- /dev/null
+++ b/Tests/EasyCoreTests/OptionalTests.swift
@@ -0,0 +1,43 @@
+import Testing
+
+@testable import EasyCore
+
+@Suite("Optional")
+struct OptionalTests {
+ @Suite(".or(default:)")
+ struct OptionalOrDefaultTests {
+
+ @Test("Returns wrapped value when not nil")
+ func returnsWrappedValue() {
+ let name: String? = "Letícia"
+ let result = name.or(default: "Guest")
+ #expect(result == "Letícia")
+ }
+
+ @Test("Returns default value when nil")
+ func returnsDefaultValue() {
+ let name: String? = nil
+ let result = name.or(default: "Guest")
+ #expect(result == "Guest")
+ }
+
+ @Test("Works with numeric optionals")
+ func numericOptionals() {
+ let age: Int? = nil
+ #expect(age.or(default: 30) == 30)
+
+ let weight: Double? = 58.6
+ #expect(weight.or(default: 0.0) == 58.6)
+ }
+
+ @Test("Works with boolean optionals")
+ func booleanOptionals() {
+ let flag: Bool? = nil
+ #expect(flag.or(default: true) == true)
+
+ let anotherFlag: Bool? = false
+ #expect(anotherFlag.or(default: true) == false)
+ }
+ }
+
+}
diff --git a/Tests/EasyCoreTests/StringTests.swift b/Tests/EasyCoreTests/StringTests.swift
new file mode 100644
index 0000000..f7fd332
--- /dev/null
+++ b/Tests/EasyCoreTests/StringTests.swift
@@ -0,0 +1,111 @@
+import Testing
+
+@testable import EasyCore
+
+@Suite("String")
+struct StringTests {
+ @Suite("Optional.isBlank")
+ struct OptionalStringIsBlankTests {
+
+ @Test("Returns true for nil optional")
+ func nilOptional() {
+ let value: String? = nil
+ #expect(value.isBlank)
+ }
+
+ @Test("Returns true for empty string")
+ func emptyString() {
+ let value: String? = ""
+ #expect(value.isBlank)
+ }
+
+ @Test("Returns true for whitespace-only string")
+ func whitespaceOnly() {
+ let value: String? = " \n\t "
+ #expect(value.isBlank)
+ }
+
+ @Test("Returns false for non-blank string")
+ func nonBlank() {
+ let value: String? = "Letícia"
+ #expect(value.isBlank == false)
+ }
+
+ @Test("Returns false for string with leading/trailing whitespace but text inside")
+ func mixedWhitespace() {
+ let value: String? = " Hello "
+ #expect(value.isBlank == false)
+ }
+ }
+
+ @Suite("String validations")
+ struct StringValidationTests {
+
+ @Test("removeOcurrencing removes all instances of a substring")
+ func removeOcurrencingWorks() {
+ let input = "123.45 BRL"
+ let result = input.removeOcurrencing("BRL")
+ #expect(result == "123.45 ")
+ }
+
+ @Test("isNumeric returns true for strings with only digits")
+ func isNumericTrue() {
+ let value = "1234567890"
+ #expect(value.isNumeric)
+ }
+
+ @Test("isNumeric returns false for strings with letters or symbols")
+ func isNumericFalse() {
+ #expect("123abc".isNumeric == false)
+ #expect("".isNumeric == false)
+ #expect(" ".isNumeric == false)
+ }
+
+ @Test("isAlphabetic returns true for strings with only letters")
+ func isAlphabeticTrue() {
+ let value = "HelloWorld"
+ #expect(value.isAlphabetic)
+ }
+
+ @Test("isAlphabetic returns false for strings with numbers or symbols")
+ func isAlphabeticFalse() {
+ #expect("Hello123".isAlphabetic == false)
+ #expect(" ".isAlphabetic == false)
+ #expect("".isAlphabetic == false)
+ }
+
+ @Test("isBlank returns true for empty and whitespace-only strings")
+ func isBlankTrue() {
+ #expect("".isBlank)
+ #expect(" ".isBlank)
+ #expect("\n\t ".isBlank)
+ }
+
+ @Test("isBlank returns false for non-blank strings")
+ func isBlankFalse() {
+ #expect("text".isBlank == false)
+ #expect(" text ".isBlank == false)
+ }
+
+ @Test("trimmed removes leading and trailing whitespace and newlines")
+ func trimmedResult() {
+ let input = " Hello \n"
+ let result = input.trimmed()
+ #expect(result == "Hello")
+ }
+
+ @Test("isEmail returns true for valid email format")
+ func isEmailTrue() {
+ #expect("user@example.com".isEmail)
+ #expect("leticia.speda@domain.co".isEmail)
+ }
+
+ @Test("isEmail returns false for invalid email format")
+ func isEmailFalse() {
+ #expect("not-an-email".isEmail == false)
+ #expect("missing@domain".isEmail == false)
+ #expect("missing.com".isEmail == false)
+ #expect("@missingusername.com".isEmail == false)
+ }
+ }
+}