GithubHelp home page GithubHelp logo

Comments (7)

bensyverson avatar bensyverson commented on May 6, 2024 2

It turns out String enums are a bit more gnarly. I start with a global RawRepresentable extension which is identical to the Int version except for the where clause:

extension RawRepresentable where RawValue == String {
  static var postgreSQLDataType: PostgreSQLDataType {
    return Self.postgreSQLDataType
  }
  
  static var postgreSQLDataArrayType: PostgreSQLDataType {
    return Self.postgreSQLDataArrayType
  }
  
  static func convertFromPostgreSQLData(_ data: PostgreSQLData) throws -> Self {
    let aRawValue = try RawValue.convertFromPostgreSQLData(data)
    guard let enumValue = Self(rawValue: aRawValue) else {
      throw Abort(.badRequest, reason: "Invalid enum value")
    }
    return enumValue
  }
  
  func convertToPostgreSQLData() throws -> PostgreSQLData {
    return try self.rawValue.convertToPostgreSQLData()
  }
}

Now I can define String enums like this:

enum Status : String, Codable {
  case uninitialized, created, claimed, expired
}

extension Status : KeyStringDecodable, PostgreSQLDataCustomConvertible, PostgreSQLColumnStaticRepresentable {
  static var postgreSQLColumn: PostgreSQLColumn = RawValue.postgreSQLColumn
  static var keyStringTrue: Status = .created
  static var keyStringFalse: Status = .uninitialized
}

Interestingly, I have to define postgreSQLColumn in the enum extension. If I try to conform to it in a protocol extension, the compiler can't figure out which method to use, because some other protocol (maybe PostgreSQLDataCustomConvertible?) already defines postgreSQLColumn.

In any case, it feels pretty clunky. Especially the KeyStringDecodable conformance, which forces me to specify a "truthiness" for the enum…

from fluent-postgres-driver.

bensyverson avatar bensyverson commented on May 6, 2024 1

Okay, a bit more detail. With the following extension, I can more easily conform Int enums to PostgreSQLDataCustomConvertible:

extension RawRepresentable where RawValue == Int {
  static var postgreSQLDataType: PostgreSQLDataType {
    return Self.postgreSQLDataType
  }
  
  static var postgreSQLDataArrayType: PostgreSQLDataType {
    return Self.postgreSQLDataArrayType
  }
  
  static func convertFromPostgreSQLData(_ data: PostgreSQLData) throws -> Self {
    let intData = try RawValue.convertFromPostgreSQLData(data)
    guard let auth = Self(rawValue: intData) else {
      throw Abort(.badRequest, reason: "Invalid enum value")
    }
    return auth
  }
  
  func convertToPostgreSQLData() throws -> PostgreSQLData {
    return try self.rawValue.convertToPostgreSQLData()
  }
}

Now I can do the following:

enum Status : Int, Codable {
  case uninitialized, created, claimed, expired
}

extension Status : PostgreSQLDataCustomConvertible { }

…and the enum can be used within a Model as expected. Personally, I think it's acceptable for the user to manually extend the enum to PostgreSQLDataCustomConvertible by using the line:
extension Status : PostgreSQLDataCustomConvertible { }

This follows the pattern set by other extensions such as:
extension User: PostgreSQLModel etc.

from fluent-postgres-driver.

tanner0101 avatar tanner0101 commented on May 6, 2024 1

@bensyverson, yeah that would be great! The PostgreSQLDataCustomConvertible extension should go in vapor/postgresql.

from fluent-postgres-driver.

tanner0101 avatar tanner0101 commented on May 6, 2024

I would try:

extension RawRepresentable where RawValue: PostgreSQLDataCustomConvertible {
    ...
}

Then at least it would be a simple matter of just adding:

extension MyEnum: PostgreSQLDataCustomConvertible { }

If that works, we can definitely add the generic RawRepresentable extension to the vapor/postgresql package.

extension RawRepresentable where RawValue: PostgreSQLColumnStaticRepresentable {
    ...
}

This ^ as well, which we could add to the vapor/fluent-postgresql package to make column representation just another simple conformance.

Regarding the KeyStringDecodable, unfortunately there's no way around that as Swift doesn't offer us any methods for discovering the available cases on an enum at runtime (cc @gwynne). There are certainly ways to hack this using knowledge of the ABI, but we'd really like to avoid that.

IIRC, there are some swift evolution proposals up for solutions to this enum case discovery problem.

With the above extensions added by vapor/fluent-postgres and vapor/postgresql, we could at least simplify things to look like:

enum Status : String, Codable {
  case uninitialized, created, claimed, expired
}

extension Status: KeyStringDecodable, PostgreSQLType  {
  static var keyStringTrue: Status = .created
  static var keyStringFalse: Status = .uninitialized
}

(See https://github.com/vapor/fluent-postgresql/blob/beta/Sources/FluentPostgreSQL/PostgreSQLType.swift#L4 btw)

from fluent-postgres-driver.

bensyverson avatar bensyverson commented on May 6, 2024

@tanner0101 Oh, cool, that worked! Using the following extension:

extension RawRepresentable where RawValue: PostgreSQLDataCustomConvertible {
  static var postgreSQLDataType: PostgreSQLDataType {
    return Self.postgreSQLDataType
  }
  
  static var postgreSQLDataArrayType: PostgreSQLDataType {
    return Self.postgreSQLDataArrayType
  }
  
  static func convertFromPostgreSQLData(_ data: PostgreSQLData) throws -> Self {
    let aRawValue = try RawValue.convertFromPostgreSQLData(data)
    guard let enumValue = Self(rawValue: aRawValue) else {
      throw Abort(.badRequest, reason: "Invalid enum value")
    }
    return enumValue
  }
  
  func convertToPostgreSQLData() throws -> PostgreSQLData {
    return try self.rawValue.convertToPostgreSQLData()
  }
}

I can now do this:

enum Status : String, Codable {
  case uninitialized, created, claimed, expired
}

extension Status : KeyStringDecodable, PostgreSQLDataCustomConvertible, PostgreSQLColumnStaticRepresentable {
  static var postgreSQLColumn: PostgreSQLColumn = RawValue.postgreSQLColumn
  static var keyStringTrue: Status = .created
  static var keyStringFalse: Status = .uninitialized
}

Thanks! Should I wrap the extension up in a PR?

from fluent-postgres-driver.

tanner0101 avatar tanner0101 commented on May 6, 2024

Merged in vapor/postgresql, thanks!

from fluent-postgres-driver.

bensyverson avatar bensyverson commented on May 6, 2024

@tanner0101 What needs to happen in vapor/fluent-postgresql inside the closure you mentioned? Is it just the following?

extension RawRepresentable where RawValue: PostgreSQLColumnStaticRepresentable {
      static var postgreSQLColumn: PostgreSQLColumn = RawValue.postgreSQLColumn
}

If so, I can send a PR through on this repo so we can get that tighter syntax you outlined!

from fluent-postgres-driver.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.