(* Parse the command line. *) open CalendarLib open Todo_types open Todo_utils open Printf let usage = "\ todo -- A simple to-do list todo [--global-options] [subcommand] [--subcommand-options] Subcommands: todo list [--all] [--retired] - List all tasks todo today \"description\" [tag[,tag...]] - Add a simple task for today, with an optional list of tags todo idea \"description\" [tag[,tag...]] - Add an long-term idea todo [todo|task] deadline [--estimate nr_days] \"description\" [tag[,tag...]] - Add a to-do task with a deadline and optional time estimate todo retire ID [ID ...] - Retire one or more tasks using the task #ID todo move ID [today | [todo|task] deadline | ideas | retired | unsorted] - Move a task to another list todo tag ID tag [--del tag] - Add or remove tags from a task todo tag-list - List all tags and their usage todo tag-add tag colour - Create a new tag, with a colour todo tag-del tag - Delete a tag (must be unused) todo tag-colour tag colour - Change the colour of a tag Tag colours may be: black blue green cyan red purple yellow Examples: todo today \"Eat breakfast\" personal,food todo task 2019-31-12 \"Prepare for next decade\" todo idea \"Write a to-do manager\" work todo list Options:" (* Parse the command line. *) let parse_command_line () = let print_version_and_exit () = printf "%s %s\n" Todo_config.package_name Todo_config.package_version; exit 0 in let list_all = ref false in let list_retired = ref false in let tag_del = ref [] in let add_tag_del tag = tag_del := tag :: !tag_del in let todo_estimate = ref None in let set_todo_estimate days = todo_estimate := Some (Calendar.Period.day days) in (* Per-subcommand specs. *) let idea_spec = Arg.align [ ] in let list_spec = Arg.align [ "--all", Arg.Set list_all, " List all tasks including retired"; "--retired", Arg.Set list_retired, " List only retired tasks"; ] in let move_spec = Arg.align [ ] in let retire_spec = Arg.align [ ] in let tag_spec = Arg.align [ "--del", Arg.String add_tag_del, "tag Remove tag"; "--delete", Arg.String add_tag_del, "tag Remove tag"; "--remove", Arg.String add_tag_del, "tag Remove tag"; "--rm", Arg.String add_tag_del, "tag Remove tag"; ] in let tag_add_spec = Arg.align [ ] in let tag_colour_spec = Arg.align [ ] in let tag_del_spec = Arg.align [ ] in let tag_list_spec = Arg.align [ ] in let today_spec = Arg.align [ ] in let todo_spec = Arg.align [ "--estimate", Arg.Int set_todo_estimate, "days Estimated time taken"; ] in (* This parses the global parameters before the subcommand. *) let global_spec = Arg.align [ "--version", Arg.Unit print_version_and_exit, " Display version and exit"; ] in let spec = ref global_spec in let subcommand = ref None in let anon_params = ref [] in let anon s = if !subcommand = None then ( (* It's the subcommand, so switch the spec. *) let sc = try subcommand_of_string s with Invalid_argument _ -> raise (Arg.Bad "unknown subcommand") in subcommand := Some sc; match sc with | Idea -> spec := idea_spec | List -> spec := list_spec | Move -> spec := move_spec | Retire -> spec := retire_spec | Tag -> spec := tag_spec | Tag_add -> spec := tag_add_spec | Tag_colour -> spec := tag_colour_spec | Tag_del -> spec := tag_del_spec | Tag_list -> spec := tag_list_spec | Today -> spec := today_spec | Todo -> spec := todo_spec ) else (* We've seen the subcommand already, just add it to anon_params. *) anon_params := s :: !anon_params in Arg.parse_dynamic spec anon usage; (* Return the parsed command line. *) let subcommand = match !subcommand with | None -> error "no subcommand given" | Some c -> c in let anon_params = List.rev !anon_params in let list_retired = !list_retired in let list_all = !list_all in let tag_del = List.rev !tag_del in let todo_estimate = !todo_estimate in subcommand, anon_params, list_retired, list_all, tag_del, todo_estimate