1 module commandr.utils; 2 3 import commandr; 4 import std.array : array; 5 import std.algorithm : find, map, levenshteinDistance; 6 import std.typecons : Tuple, Nullable; 7 import std.range : isInputRange, ElementType; 8 9 10 // helpers 11 12 private Nullable!T wrapIntoNullable(T)(T[] data) pure nothrow @safe @nogc { 13 Nullable!T result; 14 if (data.length > 0) { 15 result = data[0]; 16 } 17 return result; 18 } 19 20 unittest { 21 assert(wrapIntoNullable(cast(string[])[]).isNull); 22 23 auto wrapped = wrapIntoNullable(["test"]); 24 assert(!wrapped.isNull); 25 assert(wrapped.get() == "test"); 26 27 wrapped = wrapIntoNullable(["test", "bar"]); 28 assert(!wrapped.isNull); 29 assert(wrapped.get() == "test"); 30 } 31 32 Nullable!Option getOptionByFull(T)(T aggregate, string name) nothrow pure @safe @nogc { 33 return aggregate.options.find!(o => o.full == name).wrapIntoNullable; 34 } 35 36 Nullable!Flag getFlagByFull(T)(T aggregate, string name) nothrow pure @safe @nogc { 37 return aggregate.flags.find!(o => o.full == name).wrapIntoNullable; 38 } 39 40 Nullable!Option getOptionByShort(T)(T aggregate, string name) nothrow pure @safe @nogc { 41 return aggregate.options.find!(o => o.abbrev == name).wrapIntoNullable; 42 } 43 44 Nullable!Flag getFlagByShort(T)(T aggregate, string name) nothrow pure @safe @nogc { 45 return aggregate.flags.find!(o => o.abbrev == name).wrapIntoNullable; 46 } 47 48 string getEntryKindName(IEntry entry) nothrow pure @safe { 49 if (cast(Option)entry) { 50 return "option"; 51 } 52 53 else if (cast(Flag)entry) { 54 return "flag"; 55 } 56 57 else if (cast(Argument)entry) { 58 return "argument"; 59 } 60 else { 61 return null; 62 } 63 } 64 65 string matchingCandidate(string[] values, string current) @safe { 66 auto distances = values.map!(v => levenshteinDistance(v, current)); 67 68 immutable long index = distances.minIndex; 69 if (index < 0) { 70 return null; 71 } 72 73 return values[index]; 74 } 75 76 unittest { 77 assert (matchingCandidate(["test", "bar"], "tst") == "test"); 78 assert (matchingCandidate(["test", "bar"], "barr") == "bar"); 79 assert (matchingCandidate([], "barr") == null); 80 } 81 82 83 // minIndex is not in GDC (ugh) 84 ptrdiff_t minIndex(T)(T range) if(isInputRange!T) { 85 ptrdiff_t index, minIndex; 86 ElementType!T min = ElementType!T.max; 87 88 foreach(el; range) { 89 if (el < min) { 90 min = el; 91 minIndex = index; 92 } 93 index += 1; 94 } 95 96 if (min == ElementType!T.max) { 97 return -1; 98 } 99 100 return minIndex; 101 } 102 103 unittest { 104 assert([1, 0].minIndex == 1); 105 assert([0, 1, 2].minIndex == 0); 106 assert([2, 1, 2].minIndex == 1); 107 assert([2, 1, 0].minIndex == 2); 108 assert([1, 1, 0].minIndex == 2); 109 assert([0, 1, 0].minIndex == 0); 110 assert((cast(int[])[]).minIndex == -1); 111 }