Repository: apple/swift-nio
File: Sources/NIOCore/EventLoop.swift (Swift)

Code before:
    ///
    /// Supports formats like:
    /// - "5s" (5 seconds)
    /// - "100ms" (100 milliseconds)
    /// - "42" (42 of default unit)
    /// - "1 hr" (1 hour)
    ///
    /// This function only supports one pair of the number and units, i.e. "5s" or "100ms" but not "5s 100ms".
    ///
    /// Supported units:
    /// - h, hr, hrs (hours)
    /// - m, min (minutes)
    /// - s, sec, secs (seconds)
    /// - ms, millis (milliseconds)
    /// - us, µs, micros (microseconds)
    /// - ns, nanos (nanoseconds)
    ///
    /// - Parameters:
    ///   - userProvidedString: The string to parse
    ///   - defaultUnit: Unit to use if no unit is specified in the string
    ///
    /// - Throws: ValidationError if the string cannot be parsed
    public init(_ userProvidedString: String, defaultUnit: String = "s") throws {
        // First parse the string into a number and supported units.
        // Clean out white space from the string.
        let string = String(userProvidedString.filter { !$0.isWhitespace }).lowercased()
        // Grab the number from the string prefix.
        let parsedNumbers = string.prefix(while: { $0.isWholeNumber || $0 == "," || $0 == "." })
        // Grab the rest of the string and match this to a known unit later.
        let parsedUnit = string.dropFirst(parsedNumbers.count)

        guard let numbers = Int64(parsedNumbers) else {
            throw ValidationError.invalidNumber("'\(userProvidedString)' cannot be parsed as number and unit")
        }
        let unit = parsedUnit.isEmpty ? defaultUnit : String(parsedUnit)

        switch unit {
        case "h", "hr", "hrs":
            self = .hours(numbers)
        case "m", "min":
            self = .minutes(numbers)
        case "s", "sec", "secs":
            self = .seconds(numbers)
        case "ms", "millis":
            self = .milliseconds(numbers)
        case "us", "µs", "micros":
            self = .microseconds(numbers)
        case "ns", "nanos":
            self = .nanoseconds(numbers)
        default:
            throw ValidationError.unsupportedUnit("Unknown unit '\(unit)' in '\(userProvidedString)'")

Review comment (performance):
Up to us whether this matters, but doing this computation on the `String` type pays a lot of performance costs for unicode-correctness. We can always merge it this way for now, of course.

Code after:
    ///
    /// Supports formats like:
    /// - "5s" (5 seconds)
    /// - "100ms" (100 milliseconds)
    /// - "42" (42 of default unit)
    /// - "1 hr" (1 hour)
    ///
    /// This function only supports one pair of the number and units, i.e. "5s" or "100ms" but not "5s 100ms".
    ///
    /// Supported units:
    /// - h, hr, hrs (hours)
    /// - m, min (minutes)
    /// - s, sec, secs (seconds)
    /// - ms, millis (milliseconds)
    /// - us, µs, micros (microseconds)
    /// - ns, nanos (nanoseconds)
    ///
    /// - Parameters:
    ///   - userProvidedString: The string to parse
    ///
    /// - Throws: ValidationError if the string cannot be parsed
    public init(_ userProvidedString: String) throws {
        let lowercased = String(userProvidedString.filter { !$0.isWhitespace }).lowercased()
        let parsedNumbers = lowercased.prefix(while: { $0.isWholeNumber || $0 == "," || $0 == "." })
        let parsedUnit = String(lowercased.dropFirst(parsedNumbers.count))

        guard let numbers = Int64(parsedNumbers) else {
            throw ValidationError.invalidNumber("'\(userProvidedString)' cannot be parsed as number and unit")
        }

        switch parsedUnit {
        case "h", "hr", "hrs":
            self = .hours(numbers)
        case "m", "min":
            self = .minutes(numbers)
        case "s", "sec", "secs":
            self = .seconds(numbers)
        case "ms", "millis":
            self = .milliseconds(numbers)
        case "us", "µs", "micros":
            self = .microseconds(numbers)
        case "ns", "nanos":
            self = .nanoseconds(numbers)
        default:
            throw ValidationError.unsupportedUnit("Unknown unit '\(parsedUnit)' in '\(userProvidedString)'")
        }
    }

    /// Returns a human-readable string representation of the time amount
    /// using the most appropriate unit
    public var description: String {