]> gitweb.michael.orlitzky.com - dead/halcyon.git/blob - src/CommandLine.hs
Take the heartbeat (in seconds) from the command line. Default to ten minutes.
[dead/halcyon.git] / src / CommandLine.hs
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.
4 module CommandLine
5 ( heartbeat,
6 help_set,
7 help_text,
8 from_email_address,
9 to_email_address,
10 parse_errors,
11 parse_usernames
12 ) where
13
14 import Data.Maybe (isJust, isNothing)
15 import System.Console.GetOpt
16 import System.Environment (getArgs)
17
18
19
20 -- |A record containing values for all available options.
21 data Options = Options { opt_heartbeat :: Maybe Int,
22 opt_help :: Bool,
23 opt_from :: Maybe String,
24 opt_to :: Maybe String }
25
26
27 -- |Constructs an instance of Options, with each of its members set to
28 -- default values.
29 default_options :: Options
30 default_options = Options { opt_heartbeat = Just 600,
31 opt_help = False,
32 opt_from = Nothing,
33 opt_to = Nothing }
34
35
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.
39 --
40 -- For more information and an example of this idiom, see,
41 --
42 -- <http://www.haskell.org/haskellwiki/High-level_option_handling_with_GetOpt>
43 --
44 options :: [OptDescr (Options -> IO Options)]
45 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."
50 ]
51
52
53 -- | Attempt to parse an 'Int' from a 'String'. This is just a 'Maybe'
54 -- wrapper around 'reads'.
55 parse_int :: String -> Maybe Int
56 parse_int s =
57 case (reads s) of
58 [(n,_)] -> Just n
59 _ -> Nothing
60
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 }
65
66 set_help :: Options -> IO Options
67 set_help opts = do
68 return opts { opt_help = True }
69
70 set_to :: String -> Options -> IO Options
71 set_to arg opts = do
72 return opts { opt_to = Just arg }
73
74 set_from :: String -> Options -> IO Options
75 set_from arg opts = do
76 return opts { opt_from = Just arg }
77
78
79 -- The usage header.
80 usage :: String
81 usage = "Usage: twat [-n heartbeat] [-t to_address] [-f from_address] <username1> [username2, [username3]...]"
82
83
84 -- The usage header, and all available flags (as generated by GetOpt)
85 help_text :: String
86 help_text = usageInfo usage options
87
88
89 -- Return a list of options.
90 parse_options :: IO Options
91 parse_options = do
92 argv <- getArgs
93 let (actions, _, _) = getOpt Permute options argv
94
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
98 -- correctly.
99 opts <- foldl (>>=) (return default_options) actions
100
101 return opts
102
103
104 -- | A list of parse errors relating to the heartbeat.
105 heartbeat_errors :: IO [String]
106 heartbeat_errors = do
107 hb <- heartbeat
108 if (isNothing hb)
109 then return ["\"heartbeat\" does not appear to be an integer."]
110 else return []
111
112 -- |Parse errors relating to the list of usernames.
113 username_errors :: IO [String]
114 username_errors = do
115 argv <- getArgs
116 let (_, usernames, _) = getOpt Permute options argv
117
118 if (null usernames)
119 then return ["no usernames provided."]
120 else return []
121
122
123 -- |Parse errors relating to the "To" address.
124 to_errors :: IO [String]
125 to_errors = do
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."]
130 else return []
131
132
133 -- |Parse errors relating to the "From" address.
134 from_errors :: IO [String]
135 from_errors = do
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."]
140 else return []
141
142
143 -- |Format an error message for printing.
144 format_error :: String -> String
145 format_error err = "ERROR: " ++ err ++ "\n"
146
147
148 -- |Return a list of all parse errors.
149 parse_errors :: IO [String]
150 parse_errors = do
151 argv <- getArgs
152 let (_, _, errors) = getOpt Permute options argv
153 errs_heartbeat <- heartbeat_errors
154 errs_username <- username_errors
155 errs_to <- to_errors
156 errs_from <- from_errors
157 return $ map format_error (errors ++
158 errs_heartbeat ++
159 errs_username ++
160 errs_to ++
161 errs_from)
162
163 -- |Was the "help" option passed on the command line?
164 help_set :: IO Bool
165 help_set = do
166 opts <- parse_options
167 return (opt_help opts)
168
169 -- |What's the heartbeat?
170 heartbeat :: IO (Maybe Int)
171 heartbeat = do
172 opts <- parse_options
173 return (opt_heartbeat opts)
174
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
179 return (opt_to opts)
180
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)
186
187
188 -- |What usernames were passed on the command line?
189 parse_usernames :: IO [String]
190 parse_usernames = do
191 argv <- getArgs
192 let (_, usernames, _) = getOpt Permute options argv
193 return usernames