Repository: vapor/vapor
File: Sources/Vapor/HTTP/Headers/HTTPHeaders+ContentRange.swift (Swift)

Code before:
        let splitResult = header.split(separator: "=", omittingEmptySubsequences: false)
        guard splitResult.count == 2 else {
            return nil
        }
        let (unitStr, rangesStr) = (splitResult[0], splitResult[1])
        let ranges = rangesStr.split(separator: ",", omittingEmptySubsequences: false)
            .map(RequestedRange.from)
        if !ranges.filter({ $0 == nil }).isEmpty {
            return nil
        }
        let lowerCasedUnit = unitStr.trimmingCharacters(in: .whitespacesAndNewlines).lowercased()
        self.unit = lowerCasedUnit == "bytes"
            ? RangeUnit.bytes
            : RangeUnit.custom(value: lowerCasedUnit)
        self.ranges = ranges.compactMap { $0 }
    }
    
    func serialize() -> String {
        return "\(unit.serialize())=\(ranges.map { $0.serialize() }.joined(separator: ", "))"
    }
    
}

public struct ContentRangeHeader : Equatable {
    public let unit: RangeUnit
    public let range: ResponseContentRange
    
    public init(unit: RangeUnit, range: ResponseContentRange) {
        self.unit = unit
        self.range = range
    }
    
    public init?(responseHeader header: String) {
        let splitResult = header.split(separator: " ", omittingEmptySubsequences: false)
        guard splitResult.count == 2 else {
            return nil
        }
        let (unitStr, rangeStr) = (splitResult[0], splitResult[1])
        let lowerCasedUnit = unitStr.trimmingCharacters(in: .whitespacesAndNewlines).lowercased()
        guard let contentRange = ResponseContentRange.from(responseStr: rangeStr) else {
            return nil
        }
        self.unit = lowerCasedUnit == "bytes"
            ? RangeUnit.bytes
            : RangeUnit.custom(value: lowerCasedUnit)
        self.range = contentRange
    }
    
    func serialize() -> String {
        return "\(unit.serialize()) \(range.serialize())"
    }

Review comment (suggestion):
Instead of an enum, what about something like:
```swift
public var range: Range<Int>?
public var size: Int?
```
Where `nil` on either is serialized as `*`. 

Code after:
        public init(unit: RangeUnit, ranges: [HTTPHeaders.Range.Value]) {
            self.unit = unit
            self.ranges = ranges
        }
        
        init?(directives: [HTTPHeaders.Directive]) {
            let rangeCandidates: [HTTPHeaders.Range.Value] = directives.enumerated().compactMap {
                if $0.0 == 0, let parameter = $0.1.parameter {
                    return HTTPHeaders.Range.Value.from(requestStr: parameter)
                }
                return HTTPHeaders.Range.Value.from(requestStr: $0.1.value)
            }
            guard !rangeCandidates.isEmpty else {
                return nil
            }
            self.ranges = rangeCandidates
            let lowerCasedUnit = directives[0].value.trimmingCharacters(in: .whitespacesAndNewlines).lowercased()
            self.unit = lowerCasedUnit == "bytes"
                ? RangeUnit.bytes
                : RangeUnit.custom(value: lowerCasedUnit)
        }
        
        public func serialize() -> String {
            return "\(unit.serialize())=\(ranges.map { $0.serialize() }.joined(separator: ", "))"
        }
    }
    
    /// Represents the HTTP `Content-Range` response header.
    ///
    /// See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Range
    public struct ContentRange: Equatable {
        public let unit: RangeUnit
        public let range: HTTPHeaders.ContentRange.Value
        
        init?(directive: HTTPHeaders.Directive) {
            let splitResult = directive.value.split(separator: " ")
            guard splitResult.count == 2 else {
                return nil
            }
            let (unitStr, rangeStr) = (splitResult[0], splitResult[1])
            let lowerCasedUnit = unitStr.trimmingCharacters(in: .whitespacesAndNewlines).lowercased()
            guard let contentRange = HTTPHeaders.ContentRange.Value.from(responseStr: rangeStr) else {
                return nil
            }
            self.unit = lowerCasedUnit == "bytes"
                ? RangeUnit.bytes
                : RangeUnit.custom(value: lowerCasedUnit)
            self.range = contentRange
        }
        
        public init(unit: RangeUnit, range: HTTPHeaders.ContentRange.Value) {