Original "description" of CustomStringConvertible type

I've got an enum with custom description (CustomStringConvertible), is it possible to get the original case names?

Using mirror
enum E: CustomStringConvertible {
    case payloadless
    case withPayload(String)
    case withPayloadAndLabel(label: String)
    case withPayloadAndEmptyLabel(_ label: String)
    case withPayloadAndTwoLabels(_ label: String, _ other: Int)

    var description: String {
        "Something + " + originalDescription(self)
    }
}

func originalDescription(_ value: Any) -> String {
    let mirror = Mirror(reflecting: value)
    return switch mirror.displayStyle {
        case .enum:
            if let first = mirror.children.first {
                if let label = first.label {
                    "\(label) \(first.value)"
                } else {
                    "what do I do?"
                }
            } else {
                "and what do I do here?"
            }
        default:
            fatalError("usupported yet")
    }
}

works ok for cases with associated values:

print(E.withPayload("123"))                 // Something + withPayload 123
print(E.withPayloadAndLabel(label: "123"))  // Something + withPayloadAndLabel (label: "123")
print(E.withPayloadAndEmptyLabel("123"))    // Something + withPayloadAndEmptyLabel 123
print(E.withPayloadAndTwoLabels("123", 1))  // Something + withPayloadAndTwoLabels ("123", 1)

but not for payload-less cases:

print(E.payloadless)                        // Something + and what do I do here?

Hmm… you're maybe looking for something like this?

Which maybe needs something like this?

This seems to be producing the correct output:

//  https://github.com/swiftlang/swift/blob/main/docs/StandardLibraryProgrammersManual.md#_silgen_name

@_silgen_name("swift_EnumCaseName")
internal func _getEnumCaseName<T>(_ value: T) -> UnsafePointer<CChar>?

enum E: CustomStringConvertible {
  case payloadless
  case withPayload(String)
  case withPayloadAndLabel(label: String)
  case withPayloadAndEmptyLabel(_ label: String)
  case withPayloadAndTwoLabels(_ label: String, _ other: Int)
  
  var description: String {
    "Something + " + originalDescription(self)
  }
}

func originalDescription(_ value: Any) -> String {
  let mirror = Mirror(reflecting: value)
  return switch mirror.displayStyle {
  case .enum:
    if let first = mirror.children.first {
      if let label = first.label {
        "\(label) \(first.value)"
      } else {
        "what do I do?"
      }
    } else {
      if let cString = _getEnumCaseName(value),
         let caseName = String(validatingCString: cString) {
        caseName
      } else {
        fatalError("usupported yet")
      }
    }
  default:
    fatalError("usupported yet")
  }
}

print(E.payloadless)

Whether or not you want to ship this _silgen_name solution in your production app is another question.

The docs say that shims should be preferred over _silgen_name… but the documentation for shims is a TODO:

1 Like