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_from :: Maybe String,
24 opt_to :: Maybe String }
27 -- |Constructs an instance of Options, with each of its members set to
29 default_options :: Options
30 default_options = Options { opt_heartbeat = Just 600,
36 -- |The options list that we construct associates a function with each
37 -- option. This function is responsible for updating an Options record
38 -- with the appropriate value.
40 -- For more information and an example of this idiom, see,
42 -- <http://www.haskell.org/haskellwiki/High-level_option_handling_with_GetOpt>
44 options :: [OptDescr (Options -> IO Options)]
46 [ Option ['h'][] (NoArg set_help) "Prints this help message.",
47 Option ['n'][] (ReqArg set_heartbeat "heartbeat") "How many seconds to wait between polling.",
48 Option ['t'][] (ReqArg set_to "email_address") "Send tweets TO email_address.",
49 Option ['f'][] (ReqArg set_from "email_address") "Send tweets FROM email_address."
53 -- | Attempt to parse an 'Int' from a 'String'. This is just a 'Maybe'
54 -- wrapper around 'reads'.
55 parse_int :: String -> Maybe Int
61 set_heartbeat :: String -> Options -> IO Options
62 set_heartbeat arg opts = do
63 let new_heartbeat = parse_int arg
64 return opts { opt_heartbeat = new_heartbeat }
66 set_help :: Options -> IO Options
68 return opts { opt_help = True }
70 set_to :: String -> Options -> IO Options
72 return opts { opt_to = Just arg }
74 set_from :: String -> Options -> IO Options
75 set_from arg opts = do
76 return opts { opt_from = Just arg }
81 usage = "Usage: twat [-n heartbeat] [-t to_address] [-f from_address] <username1> [username2, [username3]...]"
84 -- The usage header, and all available flags (as generated by GetOpt)
86 help_text = usageInfo usage options
89 -- Return a list of options.
90 parse_options :: IO Options
93 let (actions, _, _) = getOpt Permute options argv
95 -- This will execute each of the functions contained in our options
96 -- list, one after another, on a default_options record. The end
97 -- result should be an Options instance with all of its members set
99 opts <- foldl (>>=) (return default_options) actions
104 -- | A list of parse errors relating to the heartbeat.
105 heartbeat_errors :: IO [String]
106 heartbeat_errors = do
109 then return ["\"heartbeat\" does not appear to be an integer."]
112 -- |Parse errors relating to the list of usernames.
113 username_errors :: IO [String]
116 let (_, usernames, _) = getOpt Permute options argv
119 then return ["no usernames provided."]
123 -- |Parse errors relating to the "To" address.
124 to_errors :: IO [String]
126 toaddr <- to_email_address
127 fromaddr <- from_email_address
128 if (isNothing toaddr) && (isJust fromaddr)
129 then return ["\"from\" address specified without \"to\" address."]
133 -- |Parse errors relating to the "From" address.
134 from_errors :: IO [String]
136 toaddr <- to_email_address
137 fromaddr <- from_email_address
138 if (isJust toaddr) && (isNothing fromaddr)
139 then return ["\"to\" address specified without \"from\" address."]
143 -- |Format an error message for printing.
144 format_error :: String -> String
145 format_error err = "ERROR: " ++ err ++ "\n"
148 -- |Return a list of all parse errors.
149 parse_errors :: IO [String]
152 let (_, _, errors) = getOpt Permute options argv
153 errs_heartbeat <- heartbeat_errors
154 errs_username <- username_errors
156 errs_from <- from_errors
157 return $ map format_error (errors ++
163 -- |Was the "help" option passed on the command line?
166 opts <- parse_options
167 return (opt_help opts)
169 -- |What's the heartbeat?
170 heartbeat :: IO (Maybe Int)
172 opts <- parse_options
173 return (opt_heartbeat opts)
175 -- |What "To" address was given on the command line?
176 to_email_address :: IO (Maybe String)
177 to_email_address = do
178 opts <- parse_options
181 -- |What "From" address was given on the command line?
182 from_email_address :: IO (Maybe String)
183 from_email_address = do
184 opts <- parse_options
185 return (opt_from opts)
188 -- |What usernames were passed on the command line?
189 parse_usernames :: IO [String]
192 let (_, usernames, _) = getOpt Permute options argv