1 -- |The CommandLine module handles parsing of the command-line options.
2 -- It should more or less be a black box, providing Main with only the
3 -- information it requires.
14 import Data.Maybe (isJust, isNothing)
15 import System.Console.GetOpt
16 import System.Environment (getArgs)
20 -- |A record containing values for all available options.
21 data Options = Options { opt_heartbeat :: Maybe Int,
23 opt_ignore_retweets :: Bool,
24 opt_ignore_replies :: Bool,
25 opt_from :: Maybe String,
26 opt_to :: Maybe String }
29 -- |Constructs an instance of Options, with each of its members set to
31 default_options :: Options
32 default_options = Options { opt_heartbeat = Just 600,
34 opt_ignore_retweets = False,
35 opt_ignore_replies = False,
40 -- |The options list that we construct associates a function with each
41 -- option. This function is responsible for updating an Options record
42 -- with the appropriate value.
44 -- For more information and an example of this idiom, see,
46 -- <http://www.haskell.org/haskellwiki/High-level_option_handling_with_GetOpt>
48 options :: [OptDescr (Options -> IO Options)]
53 "Prints this help message.",
57 (ReqArg set_heartbeat "heartbeat")
58 "How many seconds to wait between polling.",
62 (ReqArg set_to "email_address")
63 "Send tweets TO email_address.",
67 (ReqArg set_from "email_address")
68 "Send tweets FROM email_address.",
71 ['i']["ignore-retweets"]
72 (NoArg set_ignore_retweets)
76 ['I']["ignore-replies"]
77 (NoArg set_ignore_replies)
82 -- | Attempt to parse an 'Int' from a 'String'. This is just a 'Maybe'
83 -- wrapper around 'reads'.
84 parse_int :: String -> Maybe Int
90 set_heartbeat :: String -> Options -> IO Options
91 set_heartbeat arg opts = do
92 let new_heartbeat = parse_int arg
93 return opts { opt_heartbeat = new_heartbeat }
95 set_help :: Options -> IO Options
97 return opts { opt_help = True }
99 set_ignore_retweets :: Options -> IO Options
100 set_ignore_retweets opts =
101 return opts { opt_ignore_retweets = True }
103 set_ignore_replies :: Options -> IO Options
104 set_ignore_replies opts =
105 return opts { opt_ignore_replies = True }
107 set_to :: String -> Options -> IO Options
109 return opts { opt_to = Just arg }
111 set_from :: String -> Options -> IO Options
112 set_from arg opts = do
113 return opts { opt_from = Just arg }
118 usage = "Usage: twat [-n heartbeat] [-t to_address] [-f from_address] <username1> [username2, [username3]...]"
121 -- The usage header, and all available flags (as generated by GetOpt)
123 help_text = usageInfo usage options
126 -- Return a list of options.
127 parse_options :: IO Options
130 let (actions, _, _) = getOpt Permute options argv
132 -- This will execute each of the functions contained in our options
133 -- list, one after another, on a default_options record. The end
134 -- result should be an Options instance with all of its members set
136 opts <- foldl (>>=) (return default_options) actions
141 -- | A list of parse errors relating to the heartbeat.
142 heartbeat_errors :: IO [String]
143 heartbeat_errors = do
146 then return ["\"heartbeat\" does not appear to be an integer."]
149 -- |Parse errors relating to the list of usernames.
150 username_errors :: IO [String]
153 let (_, usernames, _) = getOpt Permute options argv
156 then return ["no usernames provided."]
160 -- |Parse errors relating to the "To" address.
161 to_errors :: IO [String]
163 toaddr <- to_email_address
164 fromaddr <- from_email_address
165 if (isNothing toaddr) && (isJust fromaddr)
166 then return ["\"from\" address specified without \"to\" address."]
170 -- |Parse errors relating to the "From" address.
171 from_errors :: IO [String]
173 toaddr <- to_email_address
174 fromaddr <- from_email_address
175 if (isJust toaddr) && (isNothing fromaddr)
176 then return ["\"to\" address specified without \"from\" address."]
180 -- |Format an error message for printing.
181 format_error :: String -> String
182 format_error err = "ERROR: " ++ err ++ "\n"
185 -- |Return a list of all parse errors.
186 parse_errors :: IO [String]
189 let (_, _, errors) = getOpt Permute options argv
190 errs_heartbeat <- heartbeat_errors
191 errs_username <- username_errors
193 errs_from <- from_errors
194 return $ map format_error (errors ++
200 -- |Was the "help" option passed on the command line?
203 opts <- parse_options
204 return (opt_help opts)
206 -- |What's the heartbeat?
207 heartbeat :: IO (Maybe Int)
209 opts <- parse_options
210 return (opt_heartbeat opts)
212 -- |What "To" address was given on the command line?
213 to_email_address :: IO (Maybe String)
214 to_email_address = do
215 opts <- parse_options
218 -- |What "From" address was given on the command line?
219 from_email_address :: IO (Maybe String)
220 from_email_address = do
221 opts <- parse_options
222 return (opt_from opts)
225 -- |What usernames were passed on the command line?
226 parse_usernames :: IO [String]
229 let (_, usernames, _) = getOpt Permute options argv