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