1 /*
2 	 -------------------------------------------------------------------
3 
4 	 Copyright (C) 2014, Edwin van Leeuwen
5 
6 	 This file is part of todod todo list manager.
7 
8 	 Todod is free software; you can redistribute it and/or modify
9 	 it under the terms of the GNU General Public License as published by
10 	 the Free Software Foundation; either version 3 of the License, or
11 	 (at your option) any later version.
12 
13 	 Todod is distributed in the hope that it will be useful,
14 	 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 	 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 	 GNU General Public License for more details.
17 
18 	 You should have received a copy of the GNU General Public License
19 	 along with Todod. If not, see <http://www.gnu.org/licenses/>.
20 
21 	 -------------------------------------------------------------------
22 	 */
23 
24 module todod.commandline;
25 
26 import std.algorithm;
27 import std.string;
28 
29 import colorize;
30 
31 /// Manage command line arguments
32 struct Commands(COMMAND) {
33 	alias Completion = string[] delegate( string, string );
34 
35 	/// Create commands using the introduction when printing help
36 	this( string introduction ) {
37 		myintroduction = introduction;
38 	}
39 
40 	///
41 	unittest {
42 		auto cmds = Commands!( string delegate( string ) )( "Usage: myprogram [OPTION]" );
43 
44 		cmds.add( "world", delegate( string parameter ) { return parameter ~ " world"; }, 
45 				"Append world to the given parameter" );
46 		cmds.add( "help", delegate( string parameter ) { cmds.toString; return parameter; }, 
47 				"Print this help message." );
48 		assert( cmds["world"]( "Hello" ) == "Hello world" );
49 		assert( equal( ["world", "help"], cmds.commands() ) );
50 	}
51 
52 	/// Add a new command given de name, the action to perform and a description
53 	void add( string command, COMMAND action, string description ) {
54 		addition_order ~= command;
55 		mycommands[command] = action;
56 		myhelp[command] = description;
57 	}
58 
59 	/// Create help message
60 	string toString() {
61 		string description = myintroduction ~ "\n";
62 		foreach( comm; addition_order ) {
63 			description ~= 
64 				color( "    " ~ comm.leftJustify( 15 ) ~ " ", fg.red ) 
65 				~ myhelp[comm] ~ "\n\n";
66 		}
67 		return description;
68 	}
69 
70 	/// Return COMMAND associated with command. If command does not exist then call return COMMAND associated with help. If neither exist fail.
71 	COMMAND opIndex( const string command ) {
72 		if ( exists( command )) 
73 			return mycommands[command];
74 		else if ( exists( "help" ) )
75 			return mycommands[ "help" ];
76 		import std.stdio;
77 		writeln( toString );
78 		assert( 0, "Command does not exist: " ~ command );
79 	}
80 
81 	/// Return an array with all possible commands
82 	string[] commands() {
83 		return mycommands.keys;
84 	}
85 
86 	/// Is this command provided?
87 	bool exists( const string cmd ) {
88 		if (cmd in mycommands)
89 			return true;
90 		else
91 			return false;
92 	}
93 
94 	/// Add completion option for this specific command
95 	void addCompletion( string cmd, Completion completion ) {
96 		completions[cmd] = completion;
97 	}
98 
99 	/// Set default completion function
100 	void defaultCompletion( Completion completion ) {
101 		defaulCompletionInitialized = true;
102 		myDefaultCompletion = completion;
103 	}
104 
105 	/// Return completion options
106 	string[] completionOptions( string cmd, string parameter ) {
107 		if ( cmd in completions )
108 			return completions[cmd]( cmd, parameter );
109 		else if ( defaulCompletionInitialized )
110 			return myDefaultCompletion( cmd, parameter );
111 		string[] emptyResult;
112 		return emptyResult;
113 	}
114 	
115 	private:
116 		string[] addition_order;
117 
118 		string myintroduction;
119 		COMMAND[string] mycommands;
120 		string[string] myhelp;
121 		Completion[string] completions;
122 
123 		bool defaulCompletionInitialized = false;
124 		Completion myDefaultCompletion;
125 }
126 
127 unittest {
128 	auto cmd = Commands!int( "Intro" );
129 	cmd.add( "cmd1", 1, "help1" );
130 	cmd.add( "cmd2", 2, "help2" );
131 	assert( cmd["cmd1"] == 1 );
132 	assert( cmd["cmd2"] == 2 );
133 }
134 
135 unittest {
136 	auto cmd = Commands!int( "" );
137 	assert( cmd.completionOptions( "bla", "" ).length == 0 );
138 }