1 module dsemver.git;
2 
3 import std.array : array;
4 import std.process : executeShell;
5 import std.datetime;
6 import std.algorithm.iteration : filter, map, splitter;
7 import std.algorithm.searching : any, canFind, startsWith;
8 import std.algorithm.sorting : sort;
9 import std..string : stripLeft;
10 import std.typecons : nullable, Nullable, tuple;
11 import std.exception : enforce;
12 import std.file : getcwd, chdir;
13 import std.format : format;
14 
15 import dsemver.semver;
16 
17 struct SheelRslt {
18 	const int rslt;
19 	string output;
20 }
21 
22 private SheelRslt execute(string projectPath, string command) {
23 	const cwd = getcwd();
24 	chdir(projectPath);
25 	scope(exit) {
26 		chdir(cwd);
27 	}
28 
29 	const r = executeShell(command);
30 	enforce(r.status == 0, format("'%s' failed in '%s'"
31 			~ "\nWith return code '%s' and msg %s"
32 			, command, projectPath, r.status, r.output));
33 	return SheelRslt(r.status, r.output);
34 }
35 
36 bool isClean(string projectPath) {
37 	enum cmd = "git status --porcelain";
38 
39 	const r = execute(projectPath, cmd);
40 
41 	const m = r.output.splitter("\n").map!(l => l.stripLeft)
42 		.any!(l => l.startsWith("M "));
43 
44 	return !m;
45 }
46 
47 private Nullable!SemVer toSemVer(string sv) {
48 	try {
49 		SemVer ret = parseSemVer(sv);
50 		return nullable(ret);
51 	} catch(Exception e) {
52 		return Nullable!(SemVer).init;
53 	}
54 }
55 
56 struct RefSemVer {
57 	string gitRef;
58 	SemVer semver;
59 }
60 
61 RefSemVer[] getTags(string projectPath) {
62 	enum cmd = "git for-each-ref --sort=creatordate --format '%(refname)' refs/tags";
63 	const r = execute(projectPath, cmd);
64 
65 	enum prefix = "refs/tags/";
66 
67 	SemVer dummy;
68 
69 	return r.output.splitter("\n").filter!(l => l.startsWith(prefix))
70 		.map!(v => tuple(v, toSemVer(v[prefix.length .. $])))
71 		.filter!(v => !v[1].isNull())
72 		.map!(v => RefSemVer(v[0], v[1].get()))
73 		.array
74 		.sort!((a,b) => a.semver > b.semver)
75 		.array;
76 }
77 
78 void checkoutRef(string projectPath, string tag) {
79 	const cmd = format("git checkout %s", tag);
80 	const r = execute(projectPath, cmd);
81 }