never executed always true always false
1 module Conjure.UserError
2 ( MonadUserError(..), userErr1
3 , UserErrorT(..), runUserError
4 , failToUserError, failToBug
5 , liftUserErrorT
6 ) where
7
8 import Conjure.Prelude
9 -- import qualified Conjure.Prelude as Prelude ( MonadFail(..) )
10 import Conjure.Bug
11 import Conjure.Language.Pretty
12
13 -- base
14 import System.Exit ( exitWith, ExitCode(..) )
15 import System.IO as X ( stderr, hPutStrLn )
16
17
18 -- pipes
19 import qualified Pipes
20
21
22 userErr1 :: MonadUserError m => Doc -> m a
23 userErr1 = userErr . return
24
25 class MonadFailDoc m => MonadUserError m where
26 userErr :: [Doc] -> m a
27
28 instance MonadUserError (Either Doc) where
29 userErr msgs = do
30 let msgsOut = case msgs of
31 [] -> bug "userErr []"
32 [msg] -> [ "Error:" <++> msg ]
33 _ -> [ "Error" <+> pretty (i :: Int) <> ":" <++> msg
34 | (i, msg) <- zip [1..] msgs
35 ]
36 Left (vcat msgsOut)
37
38 -- user errors exit with exit code 2 now.
39 -- in the future we intend to exit with different exit code for different kind of user errors,
40 -- but they will always use values >1
41 -- exit code 0 is for success
42 -- exit code 1 is for bugs
43 -- exit code >1 for user errors
44 instance MonadUserError IO where
45 userErr msgs =
46 case userErr msgs of
47 Left doc -> hPutStrLn stderr (renderNormal (doc :: Doc)) >> exitWith (ExitFailure 2)
48 Right x -> return x
49
50 instance MonadUserError m => MonadUserError (IdentityT m) where
51 userErr = lift . userErr
52
53 instance MonadUserError m => MonadUserError (MaybeT m) where
54 userErr = lift . userErr
55
56 instance MonadUserError m => MonadUserError (ExceptT m) where
57 userErr = lift . userErr
58
59 instance MonadUserError m => MonadUserError (StateT st m) where
60 userErr = lift . userErr
61
62 instance (MonadUserError m, Monoid w) => MonadUserError (WriterT w m) where
63 userErr = lift . userErr
64
65 instance MonadUserError m => MonadUserError (ReaderT r m) where
66 userErr = lift . userErr
67
68 instance MonadUserError m => MonadUserError (Pipes.Proxy a b c d m) where
69 userErr = lift . userErr
70
71
72 -- | This is to run a MonadUserError. Everything else should lift.
73 newtype UserErrorT m a = UserErrorT { runUserErrorT :: m (Either [Doc] a) }
74
75 runUserError :: UserErrorT Identity a -> Either [Doc] a
76 runUserError ma = runIdentity (runUserErrorT ma)
77
78 instance (Functor m) => Functor (UserErrorT m) where
79 fmap f = UserErrorT . fmap (fmap f) . runUserErrorT
80
81 instance (MonadFailDoc m) => Applicative (UserErrorT m) where
82 pure = UserErrorT . return .Right
83 (<*>) = ap
84
85 instance (MonadFailDoc m) => Monad (UserErrorT m) where
86 return = pure
87 m >>= k = UserErrorT $ do
88 a <- runUserErrorT m
89 case a of
90 Left e -> return (Left e)
91 Right x -> runUserErrorT (k x)
92
93 instance (MonadIO m, MonadFailDoc m) => MonadIO (UserErrorT m) where
94 liftIO comp = UserErrorT $ do
95 res <- liftIO comp
96 return (Right res)
97
98 -- instance MonadTrans UserErrorT where
99
100 liftUserErrorT :: Monad m => m a -> UserErrorT m a
101 liftUserErrorT comp = UserErrorT $ Right <$> comp
102
103 instance MonadFailDoc m => MonadFailDoc (UserErrorT m) where
104 failDoc = liftUserErrorT . failDoc
105
106 instance MonadFailDoc m => MonadFail (UserErrorT m) where
107 fail = liftUserErrorT . fail
108
109 instance MonadFailDoc m => MonadUserError (UserErrorT m) where
110 userErr msgs = UserErrorT $ return $ Left msgs
111
112
113 failToUserError :: MonadUserError m => ExceptT m a -> m a
114 failToUserError comp = do
115 res <- runExceptT comp
116 case res of
117 Left err -> userErr1 err
118 Right x -> return x
119
120 failToBug :: Monad m => ExceptT m a -> m a
121 failToBug comp = do
122 res <- runExceptT comp
123 case res of
124 Left err -> bug err
125 Right x -> return x