1 /++ Ponies main module. 2 + 3 + <img src="images/dependencies.svg" /> 4 + 5 + Authors: Christian Koestlin, Christian Köstlin 6 + Copyright: Copyright (c) 2018, Christian Koestlin 7 + License: MIT 8 +/ 9 module ponies; 10 11 public import ponies.packageversion; 12 13 import std.algorithm; 14 import std.conv; 15 import std.experimental.logger; 16 import std.file; 17 import std.functional; 18 import std.range; 19 import std.regex; 20 import std.stdio; 21 import std.string; 22 import std.traits; 23 import std.typecons; 24 25 enum Vote 26 { 27 up, 28 down, 29 dontCare 30 } 31 32 auto removePlusMinusPrefix(string s) 33 { 34 auto negate = false; 35 if (s[0] == '-') 36 { 37 negate = true; 38 s = s[1 .. $]; 39 } 40 else if (s[0] == '+') 41 { 42 s = s[1 .. $]; 43 } 44 return tuple!("negative", "text")(negate, s); 45 } 46 47 @("check remove plusminusprefix") unittest 48 { 49 import unit_threaded; 50 51 "-test".removePlusMinusPrefix.shouldEqual(tuple(true, "test")); 52 "test".removePlusMinusPrefix.shouldEqual(tuple(false, "test")); 53 "+test".removePlusMinusPrefix.shouldEqual(tuple(false, "test")); 54 } 55 56 auto voteByPlusMinusRegex(string pony, string plusMinusRegex) 57 { 58 if (plusMinusRegex.length == 0) 59 { 60 return Vote.dontCare; 61 } 62 63 auto pm = removePlusMinusPrefix(plusMinusRegex); 64 65 auto r = regex(pm.text); 66 if (pony.match(r)) 67 { 68 if (pm.negative) 69 { 70 return Vote.down; 71 } 72 else 73 { 74 return Vote.up; 75 } 76 } 77 else 78 { 79 return Vote.dontCare; 80 } 81 } 82 83 bool vote(P)(P pony, bool old, string pattern) 84 { 85 auto h = voteByPlusMinusRegex(pony.to!string, pattern); 86 switch (h) 87 { 88 case Vote.up: 89 return true; 90 case Vote.down: 91 return false; 92 default: 93 return old; 94 } 95 } 96 97 bool selected(P)(P pony, string what) 98 { 99 // dfmt off 100 return what 101 .split(",") 102 .fold!((result, pattern) => pony.vote(result, pattern)) 103 (false); 104 // dfmt on 105 } 106 107 @("select a pony") unittest 108 { 109 import unit_threaded; 110 111 "test".selected(".*").shouldBeTrue; 112 "test".selected("-.*").shouldBeFalse; 113 "test".selected("test1,+test").shouldBeTrue; 114 "test".selected("test1,-test").shouldBeFalse; 115 "test".selected(".*,-test").shouldBeFalse; 116 "test".selected("-.*,+test").shouldBeTrue; 117 "a.dlang.pony".selected(".*dlang.*").shouldBeTrue; 118 "a.dlang.pony".selected("-.*dlang.*").shouldBeFalse; 119 } 120 121 auto readyToRun(P)(P ponies) 122 { 123 return ponies.filter!(a => a.applicable).array; 124 } 125 126 auto poniesToRun(P)(P ponies, string what) 127 { 128 return ponies.readyToRun.filter!(a => a.selected(what)); 129 } 130 131 enum What 132 { 133 all, 134 readyToRun 135 } 136 137 auto select(T)(T ponies, What what) 138 { 139 switch (what) 140 { 141 case What.all: 142 return ponies; 143 case What.readyToRun: 144 return ponies.readyToRun; 145 default: 146 throw new Exception("nyi"); 147 } 148 } 149 150 auto possibleValues(E)() if (is(E == enum)) 151 { 152 return [EnumMembers!E].map!("a.to!string").join(", "); 153 } 154 155 T askFor(T)() if (is(T == enum)) 156 { 157 writeln("Please enter %s [%s]: ".format(T.stringof, possibleValues!T)); 158 auto line = readln.strip; 159 return line.to!T; 160 } 161 162 enum CheckStatus 163 { 164 todo, 165 done, 166 dont_know 167 } 168 169 @("bool to checkstatus") unittest 170 { 171 import unit_threaded; 172 173 true.to!CheckStatus.shouldEqual(CheckStatus.done); 174 false.to!CheckStatus.shouldEqual(CheckStatus.todo); 175 } 176 177 abstract class Pony 178 { 179 public abstract string name(); 180 public abstract bool applicable(); 181 public abstract CheckStatus check(); 182 public string[] doctor() 183 { 184 return []; 185 } 186 187 public abstract void run(); 188 } 189 190 alias UserAndProject = Tuple!(string, "user", string, "project"); 191 UserAndProject userAndProject; 192 auto getUserAndProject() 193 { 194 import std.process; 195 import std.string : replace; 196 197 auto res = ["git", "remote", "get-url", "origin"].execute; 198 auto pattern = "github.com:(?P<user>.*)/(?P<project>.*)"; 199 auto match = matchFirst(res.output, regex(pattern, "m")); 200 if (match) 201 { 202 return UserAndProject(match["user"], match["project"].replace(".git", "")); 203 } 204 else 205 { 206 return UserAndProject(cast(string) null, cast(string) null); 207 } 208 }