1 module clid.validate;
2 
3 import std.stdio : stderr;
4 import std.traits : FunctionTypeOf, Parameters;
5 
6 /**
7  * Validates a command line argument.
8  */
9 bool Validate(alias f)(string arg, int n) @property // @suppress(dscanner.style.phobos_naming_convention)
10 if (is(FunctionTypeOf!(f) == function) && is(Parameters!(f)[1] == int))
11 {
12 	return f(arg, n);
13 }
14 
15 /**
16  * Validates a command line argument.
17  */
18 bool Validate(alias f)(string arg, string str) @property // @suppress(dscanner.style.phobos_naming_convention)
19 if (is(FunctionTypeOf!(f) == function) && is(Parameters!(f)[1] == string))
20 {
21 	return f(arg, str);
22 }
23 
24 /**
25  * Validates that the string is not empty.
26  */
27 bool isNotEmpty(string arg, string str)
28 {
29 	if (str.length == 0)
30 	{
31 		stderr.writeln("Argument " ~ arg ~ " must not be empty string");
32 		return false;
33 	}
34 	return true;
35 }
36 
37 unittest
38 {
39 	assert(isNotEmpty("--arg", "") == false);
40 	assert(isNotEmpty("--arg", null) == false);
41 	assert(isNotEmpty("--arg", "wow") == true);
42 	assert(isNotEmpty("--arg", " ") == true);
43 }
44 
45 /**
46  * Validates that argument refers to a valid file.
47  */
48 bool isFile(string arg, string str)
49 {
50 	import std.file : exists, fileIsFile = isFile;
51 
52 	if (!str.exists || !str.fileIsFile)
53 	{
54 		stderr.writeln("Argument " ~ arg ~ " must refer to a file that exists");
55 		return false;
56 	}
57 	return true;
58 }
59 
60 /**
61  * Validates that argument refers to a valid directory.
62  */
63 bool isDir(string arg, string str)
64 {
65 	import std.file : exists, dirIsDir = isDir;
66 
67 	if (!str.exists || !str.dirIsDir)
68 	{
69 		stderr.writeln("Argument " ~ arg ~ " must refer to a directory that exists");
70 		return false;
71 	}
72 	return true;
73 }
74 
75 /**
76  * Validates that the argument does not refer to anything on the filesystem.
77  */
78 bool doesNotExist(string arg, string str)
79 {
80 	import std.file : exists;
81 
82 	if (str.exists)
83 	{
84 		stderr.writeln("Argument " ~ arg ~ " must not refer to a file that already exists");
85 		return false;
86 	}
87 	return true;
88 }
89 
90 /**
91  * Makes sure a given argument is a positive number (n >= 0).
92  */
93 bool isPositive(string arg, int value)
94 {
95 	if (value < 0)
96 	{
97 		stderr.writeln("Argument " ~ arg ~ " must be positive");
98 		return false;
99 	}
100 	return true;
101 }
102 
103 unittest
104 {
105 	assert(isPositive("--arg", 1) == true);
106 	assert(isPositive("--arg", 0) == true);
107 	assert(isPositive("--arg", -1) == false);
108 }
109 
110 /**
111  * Makes sure a given argument is a negative number (n <= 0).
112  */
113 bool isNegative(string arg, int value)
114 {
115 	if (value > 0)
116 	{
117 		stderr.writeln("Argument " ~ arg ~ " must be negative");
118 		return false;
119 	}
120 	return true;
121 }
122 
123 unittest
124 {
125 	assert(isNegative("--arg", 1) == false);
126 	assert(isNegative("--arg", 0) == true);
127 	assert(isNegative("--arg", -1) == true);
128 }
129 
130 /**
131  * Checks that a given argument is a valid port number (n > 0 && n < 65536).
132  */
133 bool isPortNumber(string arg, int value)
134 {
135 	if (value > 0 && value <= 0xFFFF)
136 		return true;
137 	stderr.writeln("Argument " ~ arg ~ " must be a port number within range of [1, 65535]");
138 	return false;
139 }
140 
141 unittest
142 {
143 	assert(isPortNumber("--arg", 1) == true);
144 	assert(isPortNumber("--arg", 0xFFFF) == true);
145 	assert(isPortNumber("--arg", 0) == false);
146 	assert(isPortNumber("--arg", 0x10000) == false);
147 }