Skip to content

Commit

Permalink
Make semigroup lawful, handle errors externally
Browse files Browse the repository at this point in the history
  • Loading branch information
danbroooks committed Jan 29, 2022
1 parent bedd353 commit 1f539dd
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 22 deletions.
52 changes: 32 additions & 20 deletions persistent/Database/Persist/Quasi/Internal.hs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@ module Database.Persist.Quasi.Internal
import Prelude hiding (lines)

import Control.Applicative (Alternative((<|>)))
import Control.Monad (mplus)
import Data.Char (isLower, isSpace, isUpper, toLower)
import Data.List (find, foldl')
import Data.List.NonEmpty (NonEmpty(..))
Expand Down Expand Up @@ -693,10 +692,16 @@ mkUnboundEntityDef ps parsedEntDef =
foldMap (maybe mempty (takeConstraint ps entNameHS cols) . NEL.nonEmpty) textAttribs

idField =
entityConstraintDefsIdField entityConstraintDefs
case entityConstraintDefsIdField entityConstraintDefs of
SetMoreThanOnce -> error "expected only one Id declaration per entity"
SetOnce a -> Just a
NotSet -> Nothing

primaryComposite =
entityConstraintDefsPrimaryComposite entityConstraintDefs
case entityConstraintDefsPrimaryComposite entityConstraintDefs of
SetMoreThanOnce -> error "expected only one Primary declaration per entity"
SetOnce a -> Just a
NotSet -> Nothing

cols :: [UnboundFieldDef]
cols = reverse . fst . foldr (associateComments ps) ([], []) $ reverse attribs
Expand Down Expand Up @@ -917,35 +922,42 @@ getSqlNameOr def =
_ ->
Nothing

data SetOnceAtMost a
= NotSet
| SetOnce a
| SetMoreThanOnce

instance Semigroup (SetOnceAtMost a) where
a <> b =
case (a, b) of
(_, NotSet) -> a
(NotSet, _) -> b
(SetOnce _, SetOnce _) -> SetMoreThanOnce
_ -> a

instance Monoid (SetOnceAtMost a) where
mempty =
NotSet

data EntityConstraintDefs = EntityConstraintDefs
{ entityConstraintDefsIdField :: Maybe UnboundIdDef
, entityConstraintDefsPrimaryComposite :: Maybe UnboundCompositeDef
{ entityConstraintDefsIdField :: SetOnceAtMost UnboundIdDef
, entityConstraintDefsPrimaryComposite :: SetOnceAtMost UnboundCompositeDef
, entityConstraintDefsUniques :: Maybe (NonEmpty UniqueDef)
, entityConstraintDefsForeigns :: Maybe (NonEmpty UnboundForeignDef)
}

instance Semigroup EntityConstraintDefs where
a <> b =
EntityConstraintDefs
{ entityConstraintDefsIdField = justOneId (entityConstraintDefsIdField a) (entityConstraintDefsIdField b)
, entityConstraintDefsPrimaryComposite = justOneComposite (entityConstraintDefsPrimaryComposite a) (entityConstraintDefsPrimaryComposite b)
{ entityConstraintDefsIdField = entityConstraintDefsIdField a <> entityConstraintDefsIdField b
, entityConstraintDefsPrimaryComposite = entityConstraintDefsPrimaryComposite a <> entityConstraintDefsPrimaryComposite b
, entityConstraintDefsUniques = entityConstraintDefsUniques a <> entityConstraintDefsUniques b
, entityConstraintDefsForeigns = entityConstraintDefsForeigns a <> entityConstraintDefsForeigns b
}

justOneId :: Maybe UnboundIdDef -> Maybe UnboundIdDef -> Maybe UnboundIdDef
justOneId (Just x) (Just y) = error $ "expected only one of: "
`mappend` show x `mappend` " " `mappend` show y
justOneId x y = x `mplus` y

justOneComposite :: Maybe UnboundCompositeDef -> Maybe UnboundCompositeDef -> Maybe UnboundCompositeDef
justOneComposite (Just x) (Just y) = error $ "expected only one of: "
`mappend` show x `mappend` " " `mappend` show y
justOneComposite x y = x `mplus` y

instance Monoid EntityConstraintDefs where
mempty =
EntityConstraintDefs Nothing Nothing Nothing Nothing
EntityConstraintDefs mempty mempty Nothing Nothing

entityConstraintDefsUniquesList :: EntityConstraintDefs -> [UniqueDef]
entityConstraintDefsUniquesList = foldMap NEL.toList . entityConstraintDefsUniques
Expand Down Expand Up @@ -974,12 +986,12 @@ takeConstraint ps entityName defs (n :| rest) =
"Primary" ->
mempty
{ entityConstraintDefsPrimaryComposite =
Just (takeComposite (unboundFieldNameHS <$> defs) rest)
SetOnce (takeComposite (unboundFieldNameHS <$> defs) rest)
}
"Id" ->
mempty
{ entityConstraintDefsIdField =
Just (takeId ps entityName rest)
SetOnce (takeId ps entityName rest)
}
_ | isCapitalizedText n ->
mempty
Expand Down
4 changes: 2 additions & 2 deletions persistent/test/Database/Persist/QuasiSpec.hs
Original file line number Diff line number Diff line change
Expand Up @@ -369,7 +369,7 @@ User
age Int
|]
let [user] = parse lowerCaseSettings definitions
errMsg = [st|expected only one of: UnboundIdDef {unboundIdEntityName = EntityNameHS {unEntityNameHS = "User"}, unboundIdDBName = FieldNameDB {unFieldNameDB = "id"}, unboundIdAttrs = [FieldAttrOther "Text"], unboundIdCascade = FieldCascade {fcOnUpdate = Nothing, fcOnDelete = Nothing}, unboundIdType = Just (FTTypeCon Nothing "Text")} UnboundIdDef {unboundIdEntityName = EntityNameHS {unEntityNameHS = "User"}, unboundIdDBName = FieldNameDB {unFieldNameDB = "id"}, unboundIdAttrs = [FieldAttrOther "Text"], unboundIdCascade = FieldCascade {fcOnUpdate = Nothing, fcOnDelete = Nothing}, unboundIdType = Just (FTTypeCon Nothing "Text")}|]
errMsg = [st|expected only one Id declaration per entity|]
evaluate (unboundEntityDef user) `shouldThrow`
errorCall (T.unpack errMsg)

Expand Down Expand Up @@ -405,7 +405,7 @@ User
Primary name
|]
let [user] = parse lowerCaseSettings definitions
errMsg = [st|expected only one of: UnboundCompositeDef {unboundCompositeCols = [FieldNameHS {unFieldNameHS = "ref"}], unboundCompositeAttrs = []} UnboundCompositeDef {unboundCompositeCols = [FieldNameHS {unFieldNameHS = "name"}], unboundCompositeAttrs = []}|]
errMsg = [st|expected only one Primary declaration per entity|]
evaluate (unboundEntityDef user) `shouldThrow`
errorCall (T.unpack errMsg)

Expand Down

0 comments on commit 1f539dd

Please sign in to comment.