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

Code before:
public extension HTTPHeaders {
        
    enum RangeUnit : Equatable {
        case bytes
        case custom(value: String)
        
        public func serialize() -> String {
            switch self {
                case .bytes:
                    return "bytes"
                case .custom(let value):
                    return value
            }
        }
    }
    
    struct Range : Equatable {
        public let unit: RangeUnit
        public let ranges: [HTTPHeaders.Range.Value]
        
        public init(unit: RangeUnit, ranges: [HTTPHeaders.Range.Value]) {
            self.unit = unit
            self.ranges = ranges
        }
        
        public init?(httpHeaders headers: HTTPHeaders) {
            let directives = headers.parseDirectives(name: .range).flatMap { $0 }
            guard !directives.isEmpty, directives[0].parameter != nil else {
                return nil
            }
            let rangeCandidates: [HTTPHeaders.Range.Value] = directives.enumerated()
                .compactMap { (index, value) in
                    if index == 0 {
                        return HTTPHeaders.Range.Value.from(requestStr: value.parameter!)
                    }
                    return HTTPHeaders.Range.Value.from(requestStr: value.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 init?(requestHeader header: String) {
            let splitResult = header.split(separator: "=", omittingEmptySubsequences: false)
            guard splitResult.count == 2 else {
                return nil

Review comment (refactor):
`HTTPHeaders` helpers should follow a similar pattern. `HTTPHeaders.ContentDisposition` is a good example. There should be internal `init?(directives: [Directive])` and `directives() -> [Directive]` methods that are used by a public mutable property like `public var contentDisposition: ContentDisposition?`. 

Code after:
extension HTTPHeaders {
    
    /// The unit in which `ContentRange`s and `Range`s are specified. This is usually `bytes`.
    /// See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Range
    public enum RangeUnit: Equatable {
        case bytes
        case custom(value: String)
        
        public func serialize() -> String {
            switch self {
            case .bytes:
                return "bytes"
            case .custom(let value):
                return value
            }
        }
    }
    
    /// Represents the HTTP `Range` request header.
    /// See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Range
    public struct Range: Equatable {
        public let unit: RangeUnit
        public let ranges: [HTTPHeaders.Range.Value]
        
        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: ", "))"
        }
    }