@@ -7,6 +7,9 @@ module Data.Id
7 ( Id, mkId, unId, unsafeIdTagConvert, coerceId, _Id, nilId
7 ( Id, mkId, unId, unsafeIdTagConvert, coerceId, _Id, nilId
8 , IntId, mkIntId, unIntId, unsafeIntIdTagConvert, coerceIntId, _IntId
8 , IntId, mkIntId, unIntId, unsafeIntIdTagConvert, coerceIntId, _IntId
9 , Name, mkName, unName, unsafeNameTagConvert, coerceName, _Name
9 , Name, mkName, unName, unsafeNameTagConvert, coerceName, _Name
@@ -18,32 +21,52 @@ import Data.Binary (Binary)
18import Data.Coerce (coerce)
21import Data.Coerce (coerce)
19import Data.Data
22import Data.Data
20import Data.Hashable (Hashable)
23import Data.Hashable (Hashable)
22import Data.String
24import Data.String
23import Data.Text as T
25import Data.Text as T
24−import Data.UUID (UUID)
26+import Data.UUID.Types (UUID)
25−import qualified Data.UUID as UUID
27+import qualified Data.UUID.Types as UUID
27−import Database.PostgreSQL.Simple.FromField as PG (FromField)
28−import Database.PostgreSQL.Simple.ToField as PG (ToField)
29−import Data.Csv as Csv hiding(Name)
32import GHC.TypeLits
28import GHC.TypeLits
33−import Test.QuickCheck
29+import Prelude as P
34import Text.ParserCombinators.ReadP
30import Text.ParserCombinators.ReadP
35import Web.HttpApiData
31import Web.HttpApiData
36import Web.PathPieces (PathPiece(..))
32import Web.PathPieces (PathPiece(..))
35+import qualified Data.UUID.V4 as UUID.V4
39+import Test.QuickCheck
41+import Data.Csv as Csv hiding(Name)
44+import Database.PostgreSQL.Simple.FromField as PG (FromField)
45+import Database.PostgreSQL.Simple.ToField as PG (ToField)
53+import qualified Data.Store
38newtype Id t = Id { unId :: UUID }
56newtype Id t = Id { unId :: UUID }
39 deriving stock Data
57 deriving stock Data
40 deriving newtype
58 deriving newtype
41 ( Eq, Ord, Binary
59 ( Eq, Ord, Binary
42#ifndef ghcjs_HOST_OS
60#ifndef ghcjs_HOST_OS
43 , PG.ToField, PG.FromField
62 , PG.ToField, PG.FromField
45− , FromJSON, ToJSON, NFData, Hashable, FromJSONKey, ToJSONKey, ToSchema
65+ , FromJSON, ToJSON, NFData, Hashable, FromJSONKey, ToJSONKey
67+ , ToSchema, ToParamSchema
46− , ToParamSchema, FromHttpApiData, ToHttpApiData )
69+ , FromHttpApiData, ToHttpApiData )
48type role Id nominal
71type role Id nominal
@@ -53,25 +76,42 @@ mkId :: forall s. UUID -> Id s
53mkId = coerce
76mkId = coerce
54{-# INLINE mkId #-}
77{-# INLINE mkId #-}
80+randomId :: forall s. IO (Id s)
81+randomId = coerce <$> UUID.V4.nextRandom
82+{-# INLINE randomId #-}
56instance KnownSymbol s => Show (Id s) where
85instance KnownSymbol s => Show (Id s) where
57− show (Id v) = "Id-" <> symbolVal (Proxy @s) <> "-" <> show v
86+ show (Id v) = "Id-" <> symbolVal (Proxy @s) <> "-" <> P.show v
59instance KnownSymbol s => Read (Id s) where
88instance KnownSymbol s => Read (Id s) where
60 readsPrec _ = readP_to_S $ do
89 readsPrec _ = readP_to_S $ do
61 _ <- string ("Id-" <> symbolVal (Proxy @s) <> "-")
90 _ <- string ("Id-" <> symbolVal (Proxy @s) <> "-")
62 mkId <$> readS_to_P reads
91 mkId <$> readS_to_P reads
64instance Flat (Id s) where
94instance Flat (Id s) where
65 encode = F.encode . UUID.toWords . coerce
95 encode = F.encode . UUID.toWords . coerce
66 decode = coerce . (\(a,b,c,d) -> UUID.fromWords a b c d) <$> F.decode
96 decode = coerce . (\(a,b,c,d) -> UUID.fromWords a b c d) <$> F.decode
67 size = F.size . UUID.toWords . coerce
97 size = F.size . UUID.toWords . coerce
101+instance Data.Store.Store (Id s) where
102+ poke = Data.Store.poke . UUID.toWords . coerce
103+ peek = coerce . (\(a,b,c,d) -> UUID.fromWords a b c d) <$> Data.Store.peek
104+ size = Data.Store.ConstSize 16
69instance Arbitrary (Id t) where
108instance Arbitrary (Id t) where
70 arbitrary = fmap Id $ UUID.fromWords
109 arbitrary = fmap Id $ UUID.fromWords
71 <$> arbitrary
110 <$> arbitrary
72 <*> arbitrary
111 <*> arbitrary
73 <*> arbitrary
112 <*> arbitrary
74 <*> arbitrary
113 <*> arbitrary
76instance PathPiece (Id t) where
116instance PathPiece (Id t) where
77 fromPathPiece = coerce . UUID.fromText
117 fromPathPiece = coerce . UUID.fromText
@@ -79,12 +119,14 @@ instance PathPiece (Id t) where
80#ifndef ghcjs_HOST_OS
120#ifndef ghcjs_HOST_OS
82instance Csv.FromField (Id tag) where
123instance Csv.FromField (Id tag) where
83 parseField = parseField
124 parseField = parseField
84 >=> fmap mkId . maybe (fail "invalid UUID in CSV") pure . UUID.fromText
125 >=> fmap mkId . maybe (fail "invalid UUID in CSV") pure . UUID.fromText
86instance Csv.ToField (Id tag) where
127instance Csv.ToField (Id tag) where
87 toField = Csv.toField . UUID.toText . coerce
128 toField = Csv.toField . UUID.toText . coerce
@@ -111,11 +153,24 @@ newtype IntId t = IntId { unIntId :: Integer }
111 deriving newtype
153 deriving newtype
112 ( Eq, Ord, Binary
154 ( Eq, Ord, Binary
113#ifndef ghcjs_HOST_OS
155#ifndef ghcjs_HOST_OS
157+ , PG.ToField, PG.FromField
114− , PG.ToField, PG.FromField, Csv.ToField, Csv.FromField
160+ , Csv.ToField, Csv.FromField
170+ , ToSchema, ToParamSchema, Arbitrary
116− , FromJSON, ToJSON, NFData, Hashable, FromJSONKey, ToJSONKey, ToSchema
172+ , FromJSON, ToJSON, NFData, Hashable, FromJSONKey, ToJSONKey, FromHttpApiData
117− , ToParamSchema, FromHttpApiData, ToHttpApiData, PathPiece, Flat, Arbitrary
118− , Num, Integral, Real, Enum )
173+ , ToHttpApiData, PathPiece, Num, Integral, Real, Enum )
120type role IntId nominal
175type role IntId nominal
@@ -126,7 +181,7 @@ mkIntId = coerce
126{-# INLINE mkIntId #-}
181{-# INLINE mkIntId #-}
128instance KnownSymbol s => Show (IntId s) where
183instance KnownSymbol s => Show (IntId s) where
129− show (IntId v) = "IntId-" <> symbolVal (Proxy @s) <> "-" <> show v
184+ show (IntId v) = "IntId-" <> symbolVal (Proxy @s) <> "-" <> P.show v
131instance KnownSymbol s => Read (IntId s) where
186instance KnownSymbol s => Read (IntId s) where
132 readsPrec _ = readP_to_S $ do
187 readsPrec _ = readP_to_S $ do
@@ -149,11 +204,24 @@ newtype Name t = Name { unName :: Text }
149 deriving newtype
204 deriving newtype
150 ( Eq, Ord, Binary
205 ( Eq, Ord, Binary
151#ifndef ghcjs_HOST_OS
206#ifndef ghcjs_HOST_OS
208+ , PG.ToField, PG.FromField
152− , PG.ToField, PG.FromField, Csv.ToField, Csv.FromField
211+ , Csv.ToField, Csv.FromField
221+ , ToSchema, ToParamSchema
154− , FromJSON, ToJSON, NFData, Hashable, FromJSONKey, ToJSONKey, ToSchema
223+ , FromJSON, ToJSON, NFData, Hashable, FromJSONKey, ToJSONKey, FromHttpApiData
155− , ToParamSchema, FromHttpApiData, ToHttpApiData, PathPiece, Flat
156− , IsString, Semigroup, Monoid )
224+ , ToHttpApiData, PathPiece, IsString, Semigroup, Monoid )
158type role Name nominal
226type role Name nominal
@@ -164,15 +232,17 @@ mkName = coerce
164{-# INLINE mkName #-}
232{-# INLINE mkName #-}
166instance KnownSymbol s => Show (Name s) where
234instance KnownSymbol s => Show (Name s) where
167− show (Name v) = "Name-" <> symbolVal (Proxy @s) <> "-" <> show v
235+ show (Name v) = "Name-" <> symbolVal (Proxy @s) <> "-" <> P.show v
169instance KnownSymbol s => Read (Name s) where
237instance KnownSymbol s => Read (Name s) where
170 readsPrec _ = readP_to_S $ do
238 readsPrec _ = readP_to_S $ do
171 _ <- string ("Name-" <> symbolVal (Proxy @s) <> "-")
239 _ <- string ("Name-" <> symbolVal (Proxy @s) <> "-")
172 mkName <$> readS_to_P reads
240 mkName <$> readS_to_P reads
174instance Arbitrary (Name s) where
243instance Arbitrary (Name s) where
175 arbitrary = coerce . T.pack . getPrintableString <$> arbitrary
244 arbitrary = coerce . T.pack . getPrintableString <$> arbitrary
177-- | This is a more \"explicit\" 'coerce' specifically for 'Name'.
247-- | This is a more \"explicit\" 'coerce' specifically for 'Name'.
178-- You are forced to explicitly specify the phantom types you are converting
248-- You are forced to explicitly specify the phantom types you are converting