Skip to content

Instantly share code, notes, and snippets.

@Lysxia
Last active October 1, 2020 12:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Lysxia/21594fe5918ad77c5605edd71fc0dd1d to your computer and use it in GitHub Desktop.
Save Lysxia/21594fe5918ad77c5605edd71fc0dd1d to your computer and use it in GitHub Desktop.
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE DerivingStrategies #-}
module FlexibleId where
import Data.Char
import Data.List.Split
import Data.List
import Control.Applicative (Alternative((<|>)))
import GHC.Generics
newtype Name = Name String deriving newtype (Eq, Ord, Show)
newtype PK = PK Integer deriving newtype (Eq, Ord, Show)
newtype XXX = XXX String deriving newtype (Eq, Ord, Show)
class FlexibleId a where
typeSuffix :: String
readId :: String -> Maybe a
instance FlexibleId Name where
typeSuffix = "name"
readId s | all isAccepted s = Just $ Name s where isAccepted c = isAlphaNum c || c == '_'
readId _ = Nothing
instance FlexibleId PK where
typeSuffix = "pk"
readId s | all isDigit s = Just $ PK $ read s
readId _ = Nothing
instance FlexibleId XXX where
typeSuffix = "xxx"
readId s | all isDigit s = Just $ XXX s
readId _ = Nothing
-- order matters!!!
data FlexibleUserId = UserPK PK | UserName Name | UserXXX XXX deriving (Show, Generic)
instance FlexibleId FlexibleUserId where
typeSuffix = "User-key"
readId = greadId "User"
--
greadId :: (Generic a, GFlexibleId (Rep a)) => String -> String -> Maybe a
greadId prefix s =
case splitOn ":" s of
[s1, suffix] -> to <$> greadId_ prefix s1 (Just suffix)
[s1] -> to <$> greadId_ prefix s1 Nothing
_ -> Nothing
class GFlexibleId f where
greadId_ ::
String {- type prefix to strip ("User") -} ->
String {- input string (before ":") -} ->
Maybe String {- optional input suffix (after ":") -} ->
Maybe (f p)
instance GFlexibleId f => GFlexibleId (D1 c f) where
greadId_ pre s suf = M1 <$> greadId_ pre s suf
instance (GFlexibleId f, GFlexibleId g) => GFlexibleId (f :+: g) where
greadId_ pre s suf = (L1 <$> greadId_ pre s suf) <|> (R1 <$> greadId_ pre s suf)
instance (GFlexibleId f, Constructor c) => GFlexibleId (C1 c f) where
greadId_ pre s suf | isMatch = M1 <$> greadId_ pre s suf
| otherwise = Nothing
where
isMatch = case (suf, stripPrefix pre cname) of
(Just z, Just z') -> z == map toLower z'
(Nothing, _) -> True
(_, _) -> False
cname = conName @c undefined -- conName doesn't use its argument
instance (GFlexibleId f) => GFlexibleId (S1 c f) where
greadId_ pre s suf = M1 <$> greadId_ pre s suf
instance FlexibleId a => GFlexibleId (K1 i a) where
greadId_ _ s _ = K1 <$> readId s
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment