๐Ÿ‘”CaseReflectable

๐Ÿ’พ ็จ‹ๅผ๏ผšpaiza.io โฌ†๏ธ ้œ€่ฆ๏ผš Mirror, HasMirrors

// --------------------------
//     โญ CaseReflectable
// --------------------------

// designed for enums only (use it on other types not recommended)
public protocol CaseReflectable: Loggable {
    /// log enum case (default implementation provided)
    func logEnumCase() -> Self
}

// โญ default behavior.
extension CaseReflectable {
    
    /// โญ case name
    public var caseName: String {
        let mirror = Mirror(reflecting: self)
        // enum cases:
        // - normal case: no children
        // - case with associated values: one child (with label)
        guard let label = mirror.children.first?.label else {
            return "\(self)"    // normal case
        }
        // ase with associated values
        return label
    }
    
    /// โญ associated values
    public var associatedValues: Any? {
        // if no children, a normal case, no associated values.
        guard let firstChild = Mirror(reflecting: self).children.first else {
            return nil
        }
        // return associated values
        return firstChild.value
    }
    
    /// โญ log enum case
    @discardableResult
    public func logEnumCase() -> Self {
        if let values = self.associatedValues {
            print("case: .\(caseName), values: \(values), type: \(type(of: values))")
        } else {
            print("case: .\(caseName) (no associated values)")
        }
        return self
    }
}

// -----------------------------
//     โญ custom operator ~=
// -----------------------------

/// match enum cases with associated values, while disregarding the values themselves.
/// usage: `Enum.enumCase ~= instance`
public func ~= <Enum: CaseReflectable, AssociatedValue>(
    // an enum case (with associated values)
    enumCase: (AssociatedValue) -> Enum,    // enum case as function
    // an instance of Enum
    instance: Enum
) -> Bool 
{
    // if no associated values, `instance` can't be of `enumCase`
    guard let values = instance.associatedValues else { return false }
    // if associated values not of the same type, return false
    guard values is AssociatedValue else { return false }
    // create an instance from `enumCase` (as function)
    let case2 = enumCase(values as! AssociatedValue)
    // if same case name, return true
    return case2.caseName == instance.caseName
}

Last updated