Why can’t i forward enum cases with @dynamicMemberLookup?

i have a database model with some CodingKeys that look like:

extension SomeDatabase.SomeModel
{
    enum CodingKeys:String
    {
        case id = "_id"
        case address = "a"
    }
}

rather than type out

"\(SomeDatabase.SomeModel.CodingKeys.address.rawValue)"

every time i build a query, i would like to use dynamic member lookup to shorten this to something like:

"\(SomeDatabase.SomeModel.address)"

i thought this would work:

extension SomeDatabase.SomeModel
{
    static
    subscript(dynamicMember:KeyPath<CodingKeys.Type, CodingKeys>) -> String
    {
        CodingKeys.self[keyPath: dynamicMember].rawValue
    }
}

but when i use it, i get the error:

error: dynamic key path member lookup cannot refer to enum case 'address'

what?

Key paths aren't supported for enum cases, maybe try CasePath?

what is CasePath? is it a 5.9 feature?

No it's a library, but you can't use it with @dynamicMemberLookup, I didn't realize that was the question. I don't think there's any current way to forward enum cases with dynamic member lookup without using some hacky workaround.

public enum CodingKeys: String {
  case id = "_id"
  case address = "a"

  public struct Namespace {
    var id: CodingKeys { .id }
    var address: CodingKeys { .address }
  }
}

extension SomeDatabase.SomeModel {
  static subscript(dynamicMember member: KeyPath<CodingKeys.Namespace, CodingKeys>) -> String {
    CodingKeys.Namespace()[keyPath: member].rawValue
  }
}

i ended up just adding a static subscript sugaring API to the model type:

extension Package
{
    static
    subscript(key:CodingKeys) -> String
    {
        key.rawValue
    }
}

it’s concise enough for me anyway:

$0.stage
{
    $0[.unionWith] = .init
    {
        $0[.collection] = Self.packages
        $0[.pipeline] = .init
        {
            $0.stage
            {
                $0[.sort] = .init
                {
                    $0[Package[.address]] = (-)
                }
            }
            $0.stage
            {
                $0[.limit] = 1
            }
            $0.stage
            {
                $0[.replaceWith] = .init
                {
                    $0[Package[.id]] = "\(package)"
                    $0[Package[.address]] = .init
                    {
                        $0[.add] = ("$\(Package[.address])", 1)
                    }
                }
            }
        }
    }
}

(this is a mongodb aggregation query)

Could you maybe use a result builder for that?

this is a result builder, it’s just not one of the unkeyed SwiftUI result builders that got added to the language a couple years ago.

That's what I meant

like i said, SwiftUI-style result builders are unkeyed, so they’re not really great for the kind of “key-value shaped” things like BSON. this is where subscript assignment really shines in my opinion.

how would you design this DSL?

I thought static keypaths weren’t supported at all. I’m surprised the error is what it is, but iirc I tried to get around that with a static protocol extension once and got a much more clear error message.