hasbolt-extras 0.0.1.9 → 0.0.2.0
raw patch · 4 files changed
+150/−57 lines, 4 filesPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
API changes (from Hackage documentation)
- Database.Bolt.Extras.DSL.Typed: type family HasField' (types :: k) (field :: Symbol) :: Constraint;
- Database.Bolt.Extras.Generic: BoltEnum :: a -> BoltEnum a
- Database.Bolt.Extras.Generic: class GFromValue rep
- Database.Bolt.Extras.Generic: class GToValue rep
- Database.Bolt.Extras.Generic: gFromValue :: GFromValue rep => String -> Maybe (rep a)
- Database.Bolt.Extras.Generic: gToValue :: GToValue rep => rep a -> String
- Database.Bolt.Extras.Generic: instance (Data.Typeable.Internal.Typeable a, GHC.Generics.Generic a, Database.Bolt.Extras.Generic.GFromValue (GHC.Generics.Rep a)) => Database.Bolt.Extras.Internal.Types.FromValue (Database.Bolt.Extras.Generic.BoltEnum a)
- Database.Bolt.Extras.Generic: instance (Database.Bolt.Extras.Generic.GFromValue l, Database.Bolt.Extras.Generic.GFromValue r) => Database.Bolt.Extras.Generic.GFromValue (l GHC.Generics.:+: r)
- Database.Bolt.Extras.Generic: instance (Database.Bolt.Extras.Generic.GToValue l, Database.Bolt.Extras.Generic.GToValue r) => Database.Bolt.Extras.Generic.GToValue (l GHC.Generics.:+: r)
- Database.Bolt.Extras.Generic: instance (GHC.Generics.Generic a, Database.Bolt.Extras.Generic.GToValue (GHC.Generics.Rep a)) => Database.Bolt.Extras.Internal.Types.ToValue (Database.Bolt.Extras.Generic.BoltEnum a)
- Database.Bolt.Extras.Generic: instance Database.Bolt.Extras.Generic.GFromValue cs => Database.Bolt.Extras.Generic.GFromValue (GHC.Generics.D1 meta cs)
- Database.Bolt.Extras.Generic: instance Database.Bolt.Extras.Generic.GToValue cs => Database.Bolt.Extras.Generic.GToValue (GHC.Generics.D1 meta cs)
- Database.Bolt.Extras.Generic: instance GHC.Classes.Eq a => GHC.Classes.Eq (Database.Bolt.Extras.Generic.BoltEnum a)
- Database.Bolt.Extras.Generic: instance GHC.Generics.Generic (Database.Bolt.Extras.Generic.BoltEnum a)
- Database.Bolt.Extras.Generic: instance GHC.Show.Show a => GHC.Show.Show (Database.Bolt.Extras.Generic.BoltEnum a)
- Database.Bolt.Extras.Generic: instance GHC.TypeLits.KnownSymbol name => Database.Bolt.Extras.Generic.GFromValue (GHC.Generics.C1 ('GHC.Generics.MetaCons name fixity rec) GHC.Generics.U1)
- Database.Bolt.Extras.Generic: instance GHC.TypeLits.KnownSymbol name => Database.Bolt.Extras.Generic.GToValue (GHC.Generics.C1 ('GHC.Generics.MetaCons name fixity rec) GHC.Generics.U1)
- Database.Bolt.Extras.Generic: newtype BoltEnum a
- Database.Bolt.Extras.Graph: type family RelRes a :: *;
+ Database.Bolt.Extras.DSL.Typed: type AddType (types :: k) (typ :: Type) = (result :: k) | result -> types typ;
+ Database.Bolt.Extras.DSL.Typed: type CanAddType (types :: k) :: Constraint;
+ Database.Bolt.Extras.DSL.Typed: type HasField (types :: k) (field :: Symbol) (typ :: Type) :: Constraint;
+ Database.Bolt.Extras.DSL.Typed: type HasField' (types :: k) (field :: Symbol) :: Constraint;
+ Database.Bolt.Extras.Generic: BoltGeneric :: a -> BoltGeneric a
+ Database.Bolt.Extras.Generic: class GIsValue rep
+ Database.Bolt.Extras.Generic: class GRecordValue rep
+ Database.Bolt.Extras.Generic: gExactEither :: GRecordValue rep => (String -> String) -> Value -> Either UnpackError (rep a)
+ Database.Bolt.Extras.Generic: gIsValue :: GIsValue rep => Options -> rep a -> Either String Value
+ Database.Bolt.Extras.Generic: instance (Data.Typeable.Internal.Typeable a, GHC.Generics.Generic a, Database.Bolt.Extras.Generic.GRecordValue (GHC.Generics.Rep a)) => Database.Bolt.Record.RecordValue (Database.Bolt.Extras.Generic.BoltGeneric a)
+ Database.Bolt.Extras.Generic: instance (Database.Bolt.Extras.Generic.GIsValue l, Database.Bolt.Extras.Generic.GIsValue r) => Database.Bolt.Extras.Generic.GIsValue (l GHC.Generics.:*: r)
+ Database.Bolt.Extras.Generic: instance (Database.Bolt.Extras.Generic.GIsValue l, Database.Bolt.Extras.Generic.GIsValue r) => Database.Bolt.Extras.Generic.GIsValue (l GHC.Generics.:+: r)
+ Database.Bolt.Extras.Generic: instance (Database.Bolt.Extras.Generic.GRecordValue l, Database.Bolt.Extras.Generic.GRecordValue r) => Database.Bolt.Extras.Generic.GRecordValue (l GHC.Generics.:*: r)
+ Database.Bolt.Extras.Generic: instance (Database.Bolt.Extras.Generic.GRecordValue l, Database.Bolt.Extras.Generic.GRecordValue r) => Database.Bolt.Extras.Generic.GRecordValue (l GHC.Generics.:+: r)
+ Database.Bolt.Extras.Generic: instance (GHC.Generics.Generic a, Database.Bolt.Extras.Generic.GIsValue (GHC.Generics.Rep a)) => Database.Bolt.Value.Type.IsValue (Database.Bolt.Extras.Generic.BoltGeneric a)
+ Database.Bolt.Extras.Generic: instance (GHC.Generics.Selector s, Database.Bolt.Value.Type.IsValue a) => Database.Bolt.Extras.Generic.GIsValue (GHC.Generics.S1 s (GHC.Generics.Rec0 a))
+ Database.Bolt.Extras.Generic: instance (GHC.TypeLits.KnownSymbol name, Database.Bolt.Extras.Generic.GRecordValue a) => Database.Bolt.Extras.Generic.GRecordValue (GHC.Generics.S1 ('GHC.Generics.MetaSel ('GHC.Maybe.Just name) s1 s2 s3) a)
+ Database.Bolt.Extras.Generic: instance (TypeError ...) => Database.Bolt.Extras.Generic.GIsValue (GHC.Generics.C1 ('GHC.Generics.MetaCons s1 s2 'GHC.Types.False) cs)
+ Database.Bolt.Extras.Generic: instance (TypeError ...) => Database.Bolt.Extras.Generic.GRecordValue (GHC.Generics.C1 ('GHC.Generics.MetaCons s1 s2 'GHC.Types.False) cs)
+ Database.Bolt.Extras.Generic: instance Database.Bolt.Extras.Generic.GIsValue cs => Database.Bolt.Extras.Generic.GIsValue (GHC.Generics.C1 ('GHC.Generics.MetaCons s1 s2 'GHC.Types.True) cs)
+ Database.Bolt.Extras.Generic: instance Database.Bolt.Extras.Generic.GIsValue cs => Database.Bolt.Extras.Generic.GIsValue (GHC.Generics.D1 meta cs)
+ Database.Bolt.Extras.Generic: instance Database.Bolt.Extras.Generic.GRecordValue cs => Database.Bolt.Extras.Generic.GRecordValue (GHC.Generics.C1 ('GHC.Generics.MetaCons s1 s2 'GHC.Types.True) cs)
+ Database.Bolt.Extras.Generic: instance Database.Bolt.Extras.Generic.GRecordValue cs => Database.Bolt.Extras.Generic.GRecordValue (GHC.Generics.D1 meta cs)
+ Database.Bolt.Extras.Generic: instance Database.Bolt.Record.RecordValue a => Database.Bolt.Extras.Generic.GRecordValue (GHC.Generics.K1 i a)
+ Database.Bolt.Extras.Generic: instance GHC.Classes.Eq a => GHC.Classes.Eq (Database.Bolt.Extras.Generic.BoltGeneric a)
+ Database.Bolt.Extras.Generic: instance GHC.Generics.Generic (Database.Bolt.Extras.Generic.BoltGeneric a)
+ Database.Bolt.Extras.Generic: instance GHC.Show.Show a => GHC.Show.Show (Database.Bolt.Extras.Generic.BoltGeneric a)
+ Database.Bolt.Extras.Generic: instance GHC.TypeLits.KnownSymbol name => Database.Bolt.Extras.Generic.GIsValue (GHC.Generics.C1 ('GHC.Generics.MetaCons name s2 'GHC.Types.False) GHC.Generics.U1)
+ Database.Bolt.Extras.Generic: instance GHC.TypeLits.KnownSymbol name => Database.Bolt.Extras.Generic.GRecordValue (GHC.Generics.C1 ('GHC.Generics.MetaCons name s2 'GHC.Types.False) GHC.Generics.U1)
+ Database.Bolt.Extras.Generic: newtype BoltGeneric a
+ Database.Bolt.Extras.Graph: type NodeReq a :: *;
+ Database.Bolt.Extras.Graph: type NodeRes a :: *;
+ Database.Bolt.Extras.Graph: type RelReq a :: *;
+ Database.Bolt.Extras.Graph: type RelRes a :: *;
- Database.Bolt.Extras.Graph: relations :: forall n_aaFg a_aaFh b_aaFi b_aevx. Lens (Graph n_aaFg a_aaFh b_aaFi) (Graph n_aaFg a_aaFh b_aevx) (Map (n_aaFg, n_aaFg) b_aaFi) (Map (n_aaFg, n_aaFg) b_aevx)
+ Database.Bolt.Extras.Graph: relations :: forall n_agaF a_agaG b_agaH b_agBG. Lens (Graph n_agaF a_agaG b_agaH) (Graph n_agaF a_agaG b_agBG) (Map (n_agaF, n_agaF) b_agaH) (Map (n_agaF, n_agaF) b_agBG)
- Database.Bolt.Extras.Graph: vertices :: forall n_aaFg a_aaFh b_aaFi a_aevy. Lens (Graph n_aaFg a_aaFh b_aaFi) (Graph n_aaFg a_aevy b_aaFi) (Map n_aaFg a_aaFh) (Map n_aaFg a_aevy)
+ Database.Bolt.Extras.Graph: vertices :: forall n_agaF a_agaG b_agaH a_agBH. Lens (Graph n_agaF a_agaG b_agaH) (Graph n_agaF a_agBH b_agaH) (Map n_agaF a_agaG) (Map n_agaF a_agBH)
Files
- CHANGELOG.md +9/−0
- hasbolt-extras.cabal +3/−2
- src/Database/Bolt/Extras/Generic.hs +128/−49
- src/Database/Bolt/Extras/Template/Internal/Converters.hs +10/−6
CHANGELOG.md view
@@ -6,6 +6,15 @@ ## [Unreleased] +## [0.0.2.0] - 2023-01-24+### Added+- `BoltGeneric` wrapper for `DerivingVia` to derive `IsValue` and `RecordValue` for Haskell types.+### Changed+- `NodeLike` and `RelationLike` classes now use `IsValue` / `RecordValue` instead of `ToValue` /+ `FromValue`.+### Removed+- `BoltEnum` newtype, superseded by `BoltGeneric`.+ ## [0.0.1.9] - 2022-03-14 ### Added - `IsValue` instances for `ToValue` & `NodeLike` via newtypes;
hasbolt-extras.cabal view
@@ -1,5 +1,5 @@ name: hasbolt-extras-version: 0.0.1.9+version: 0.0.2.0 synopsis: Extras for hasbolt library description: Extras for hasbolt library homepage: https://github.com/biocad/hasbolt-extras#readme@@ -22,7 +22,8 @@ || ==8.8.4 || ==8.10.7 || ==9.0.2- || ==9.2.2+ || ==9.2.5+ || ==9.4.4 source-repository head type: git
src/Database/Bolt/Extras/Generic.hs view
@@ -1,91 +1,170 @@ {-# LANGUAGE AllowAmbiguousTypes #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DerivingVia #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE KindSignatures #-}+{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeOperators #-}-{-# LANGUAGE TypeSynonymInstances #-} {-# LANGUAGE UndecidableInstances #-} module Database.Bolt.Extras.Generic where -import Data.Proxy (Proxy (..))-import Data.Text (pack, unpack)-import Database.Bolt (Value (..))-import GHC.Generics (C1, D1, Generic (..), M1 (..), Meta (..), U1 (..), type (:+:) (..))-import GHC.TypeLits (KnownSymbol, symbolVal)+import Data.Map.Strict (lookup, singleton)+import Data.Proxy (Proxy (..))+import Data.Text (pack)+import Database.Bolt (IsValue (..), RecordValue (..),+ UnpackError (Not), Value (..))+import GHC.Generics (C1, D1, Generic (..), K1 (..), M1 (..),+ Meta (..), Rec0, S1, Selector (selName),+ U1 (..), type (:*:) (..), type (:+:) (..))+import GHC.TypeLits as GHC (ErrorMessage (Text), KnownSymbol,+ TypeError, symbolVal) -import Control.Applicative ((<|>))-import Database.Bolt.Extras.Internal.Types (FromValue (..), ToValue (..))-import Type.Reflection (Typeable, typeRep)+import Data.Aeson (Options, constructorTagModifier,+ defaultOptions, fieldLabelModifier)+import Data.Either (isRight)+import Prelude hiding (lookup)+import Type.Reflection (Typeable) --- | Wrapper to encode enum-like types as strings in the DB.--- -- Intended usage is with @DerivingVia@: -- -- >>> :{ -- data Color = Red | Green | Blue--- deriving (Show, Generic)--- deriving (ToValue, FromValue) via BoltEnum Color+-- deriving (Eq, Show, Generic)+-- deriving (IsValue, RecordValue) via BoltGeneric Color+-- data MyRec = MyRec+-- { field1 :: Int+-- , field2 :: [Text]+-- , field3 :: Double+-- , field4 :: Color+-- }+-- deriving (Eq, Show, Generic)+-- deriving (IsValue, RecordValue) via BoltGeneric MyRec+-- data MyHardRec = MyHard+-- { field1h :: Int+-- , field2h :: [Text]+-- , field3h :: MyRec+-- }+-- deriving (Eq, Show, Generic)+-- deriving (IsValue, RecordValue) via BoltGeneric MyHardRec+-- data FailTest = FailTest Int Int+-- deriving (Eq, Show, Generic)+-- deriving (IsValue, RecordValue) via BoltGeneric FailTest -- :} ----- >>> toValue Red+-- >>> Bolt.toValue Red -- T "Red"--- >>> fromValue (T "Blue") :: Color--- Blue--- >>> fromValue (T "Brown") :: Color--- *** Exception: Could not unpack unknown value Brown of Color+-- >>> Bolt.toValue Blue+-- T "Blue"+-- >>> let myRec = MyRec 1 [pack "hello"] 3.14 Red+-- >>> Bolt.toValue myRec+-- M (fromList [("field1",I 1),("field2",L [T "hello"]),("field3",F 3.14),("field4",T "Red")])+-- >>> let myHardRec = MyHard 2 [pack "Hello!"] myRec+-- >>> Bolt.toValue myHardRec+-- M (fromList [("field1h",I 2),("field2h",L [T "Hello!"]),("field3h",M (fromList [("field1",I 1),("field2",L [T "hello"]),("field3",F 3.14),("field4",T "Red")]))])+-- >>> (exactEither . Bolt.toValue) myHardRec == Right myHardRec+-- True+-- >>> Bolt.toValue $ FailTest 1 2 -- ...+-- ... Can't make IsValue for non-record, non-unit constructor -- ...-newtype BoltEnum a- = BoltEnum a+--++newtype BoltGeneric a+ = BoltGeneric a deriving (Eq, Show, Generic) -instance (Generic a, GToValue (Rep a)) => ToValue (BoltEnum a) where- toValue (BoltEnum a) = T $ pack $ gToValue $ from a+instance (Generic a, GIsValue (Rep a)) => IsValue (BoltGeneric a) where+ toValue (BoltGeneric a) =+ case gIsValue defaultOptions (from a) of+ Left err -> error err+ Right res -> res -instance (Typeable a, Generic a, GFromValue (Rep a)) => FromValue (BoltEnum a) where- fromValue (T str) =- case gFromValue $ unpack str of- Nothing -> error $ "Could not unpack unknown value " <> unpack str <> " of " <> show (typeRep @a)- Just rep -> BoltEnum $ to rep- fromValue v = error $ "Could not unpack " <> show v <> " as " <> show (typeRep @a)+instance (Typeable a, Generic a, GRecordValue (Rep a)) => RecordValue (BoltGeneric a) where+ exactEither v = BoltGeneric . to <$> gExactEither id v -class GToValue rep where- gToValue :: rep a -> String+class GIsValue rep where+ gIsValue :: Options -> rep a -> Either String Value -instance GToValue cs => GToValue (D1 meta cs) where- gToValue (M1 cs) = gToValue cs+instance GIsValue cs => GIsValue (D1 meta cs) where+ gIsValue op (M1 cs) = gIsValue op cs -instance KnownSymbol name => GToValue (C1 ('MetaCons name fixity rec) U1) where- gToValue _ = symbolVal @name Proxy+instance GIsValue cs => (GIsValue (C1 ('MetaCons s1 s2 'True) cs)) where+ gIsValue op (M1 cs) = gIsValue op cs -instance (GToValue l, GToValue r) => GToValue (l :+: r) where- gToValue (L1 l) = gToValue l- gToValue (R1 r) = gToValue r+instance {-# OVERLAPPING #-} (KnownSymbol name) => GIsValue (C1 ('MetaCons name s2 'False) U1) where+ gIsValue op _ = Right $ T $ pack $ constructorTagModifier op $ symbolVal @name Proxy -class GFromValue rep where- gFromValue :: String -> Maybe (rep a)+instance TypeError ('GHC.Text "Can't make IsValue for non-record, non-unit constructor ") => GIsValue (C1 ('MetaCons s1 s2 'False) cs) where+ gIsValue _ _ = error "not reachable" -instance GFromValue cs => GFromValue (D1 meta cs) where- gFromValue = fmap M1 . gFromValue @cs+instance (Selector s, IsValue a) => GIsValue (S1 s (Rec0 a)) where+ gIsValue op m@(M1 (K1 v)) = Right $ M $ singleton (pack name) (toValue v)+ where+ name = fieldLabelModifier op (selName m) -instance KnownSymbol name => GFromValue (C1 ('MetaCons name fixity rec) U1) where- gFromValue str =- if str == symbolVal @name Proxy- then Just $ M1 U1- else Nothing+instance (GIsValue l, GIsValue r) => GIsValue (l :+: r) where+ gIsValue op (L1 l) = gIsValue op l+ gIsValue op (R1 r) = gIsValue op r -instance (GFromValue l, GFromValue r) => GFromValue (l :+: r) where- gFromValue str = L1 <$> gFromValue @l str <|> R1 <$> gFromValue @r str+instance (GIsValue l, GIsValue r) => GIsValue (l :*: r) where+ gIsValue op (l :*: r) = do+ lRes <- gIsValue op l+ rRes <- gIsValue op r+ case (lRes, rRes) of+ (M ml, M mr) -> Right $ M $ ml <> mr+ _ -> Left "not record product type" +class GRecordValue rep where+ gExactEither :: (String -> String) -> Value -> Either UnpackError (rep a)++instance GRecordValue cs => GRecordValue (D1 meta cs) where+ gExactEither modifier v = M1 <$> gExactEither modifier v++instance GRecordValue cs => GRecordValue (C1 ('MetaCons s1 s2 'True) cs) where+ gExactEither modifier v = M1 <$> gExactEither modifier v++instance {-# OVERLAPPING #-} (KnownSymbol name) => GRecordValue (C1 ('MetaCons name s2 'False) U1) where+ gExactEither _ (T str) = + if str == name+ then Right $ M1 U1+ else Left $ Not $ "expected constructor name: " <> name <> " , but got: " <> str+ where+ name = pack $ symbolVal @name Proxy+ gExactEither _ _ = Left $ Not "bad value"+ +instance TypeError ('GHC.Text "Can't make GRecordValue for non-record, non-unit constructor ") => GRecordValue (C1 ('MetaCons s1 s2 'False) cs) where+ gExactEither _ = error "not reachable"++instance (KnownSymbol name, GRecordValue a) => GRecordValue (S1 ('MetaSel ('Just name) s1 s2 s3) a) where+ gExactEither modifier (M m) =+ case lookup (pack $ modifier name) m of+ Just v -> M1 <$> gExactEither modifier v+ Nothing -> Left $ Not $ "selector with name:" <> pack name <> " not in record"+ where+ name = symbolVal @name Proxy+ gExactEither _ _ = Left $ Not "bad structure in selector case"++instance (GRecordValue l, GRecordValue r) => GRecordValue (l :*: r) where+ gExactEither modifier v = (:*:) <$> gExactEither modifier v <*> gExactEither modifier v++instance (GRecordValue l, GRecordValue r) => GRecordValue (l :+: r) where+ gExactEither modifier v =+ let res = L1 <$> gExactEither @l modifier v in+ if isRight res then res else R1 <$> gExactEither @r modifier v++instance (RecordValue a) => GRecordValue (K1 i a) where+ gExactEither _ v = K1 <$> exactEither v+ {- $setup >>> :set -XDerivingStrategies -XDerivingVia >>> :load Database.Bolt.Extras Database.Bolt.Extras.Generic >>> import GHC.Generics >>> import Database.Bolt.Extras.Generic->>> import Database.Bolt (Value (..))+>>> import Data.Text (Text, pack)+>>> import Database.Bolt as Bolt (Value (..), IsValue(toValue), RecordValue(exactEither)) -}
src/Database/Bolt/Extras/Template/Internal/Converters.hs view
@@ -12,10 +12,10 @@ import Data.Map.Strict (fromList, member, notMember, (!)) import Data.Text (Text, pack, unpack)-import Database.Bolt (Node (..), URelationship (..), Value (..))-import Database.Bolt.Extras (FromValue (..), Labels (..),+import Database.Bolt (Node (..), URelationship (..), Value (..), IsValue(..), RecordValue(..))+import Database.Bolt.Extras (Labels (..), NodeLike (..),- Properties (..), ToValue (..),+ Properties (..), URelationLike (..)) import Database.Bolt.Extras.Utils (currentLoc, dummyId) import Instances.TH.Lift ()@@ -316,9 +316,13 @@ checkLabels :: Labels t => t -> [Text] -> Bool checkLabels container = all (`elem` getLabels container) -getProp :: (Properties t, FromValue a) => t -> (Text, Bool) -> a-getProp container (fieldName, fieldMaybe) | fieldMaybe && fieldName `notMember` getProps container = fromValue $ N ()- | otherwise = fromValue (getProps container ! fieldName)+getProp :: (Properties t, RecordValue a) => t -> (Text, Bool) -> a+getProp container (fieldName, fieldMaybe) | fieldMaybe && fieldName `notMember` getProps container = exactE $ N ()+ | otherwise = exactE (getProps container ! fieldName)+ where+ exactE v = case exactEither v of+ Right res -> res+ Left err -> error $ show err unpackError :: Show c => c -> String -> a unpackError container label = error $ $currentLoc ++ " could not unpack " ++ label ++ " from " ++ show container