1 /*
2  *   Copyright (c) 2020 Orfeo Da Vià
3  *
4  *   Boost Software License - Version 1.0 - August 17th, 2003
5  *
6  *   Permission is hereby granted, free of charge, to any person or organization
7  *   obtaining a copy of the software and accompanying documentation covered by
8  *   this license (the "Software") to use, reproduce, display, distribute,
9  *   execute, and transmit the Software, and to prepare derivative works of the
10  *   Software, and to permit third-parties to whom the Software is furnished to
11  *   do so, all subject to the following:
12  *
13  *   The copyright notices in the Software and this entire statement, including
14  *   the above license grant, this restriction and the following disclaimer,
15  *   must be included in all copies of the Software, in whole or in part, and
16  *   all derivative works of the Software, unless such copies or derivative
17  *   works are solely in the form of machine-executable object code generated by
18  *   a source language processor.
19  *
20  *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21  *   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22  *   FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
23  *   SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
24  *   FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
25  *   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26  *   DEALINGS IN THE SOFTWARE.
27  */
28 
29 module app;
30 
31 import std.stdio : writefln, writeln;
32 import std.experimental.logger;
33 import std.json : JSONValue;
34 
35 enum MIRROR = "./.dirimere";
36 
37 void main(string[] args) {
38    import std.getopt;
39 
40    bool verbose;
41    bool clean;
42    string proxyFile = "dirimere.json";
43 
44    auto opt = getopt(args, "verbose|v", "Verbose", &verbose,
45          "clean|c", "Delete package directory", &clean,
46          "proxyFile|i", "The filename of the file to search packages in", &proxyFile,
47          );
48    if (verbose) {
49       globalLogLevel(LogLevel.trace);
50    } else {
51       globalLogLevel(LogLevel.info);
52    }
53    if (opt.helpWanted) {
54       defaultGetoptPrinter("dirimere", opt.options);
55       help;
56    } else {
57       JSONValue j = makeJson(proxyFile);
58       trace(j);
59       run(j, clean);
60    }
61 }
62 
63 JSONValue makeJson(string fn) {
64    import std.file : readText;
65    import std.json : parseJSON;
66    string depJ = readText(fn);
67    return parseJSON(depJ);
68 }
69 
70 void run(JSONValue dubConfig, bool clean) {
71    import std.array : join;
72    import std.file : exists, mkdirRecurse, rmdirRecurse;
73    import std.process : execute;
74 
75    foreach (dep; dubConfig.array) {
76       string v = dep["version"].get!string.getVersion;
77       string name = dep["name"].get!string;
78       string f = getFolderName(name, v);
79       if (clean) {
80          writefln("Remove %s", f);
81          f.rmdirRecurse;
82       }
83 
84       if (!exists(f)) {
85          f.mkdirRecurse;
86 
87          string[] cmd = getCloneCmd(dep["url"].get!string, v, f);
88          trace(cmd.join(" "));
89          writefln("Clone %s version %s", name, v);
90 
91          auto reply = execute(cmd);
92          if (reply.status != 0) {
93             writeln("Failed\n", reply.output);
94          } else {
95             writeln("Successful");
96          }
97       } else {
98          writeln("None to do");
99       }
100    }
101 }
102 
103 string getVersion(string v) {
104    import std..string : startsWith;
105    if (v.startsWith("v")) {
106       return v[1 .. $];
107    } else {
108       return v;
109    }
110 }
111 
112 unittest {
113    assert("v1.2.3".getVersion == "1.2.3");
114    assert("2.2.3".getVersion == "2.2.3");
115 }
116 
117 string getFolderName(string name, string v) {
118    import std.path : buildPath;
119 
120    return buildPath(MIRROR, name ~ "-" ~ v);
121 }
122 
123 unittest {
124    string f = getFolderName("cul", "0.1.0");
125    assert(f == "./.mirror/cul-0.1.0", f);
126 }
127 
128 string[] getCloneCmd(string url, string branch, string folder) {
129    string[] a = ["git", "clone", "--depth", "1"];
130    a ~= "--branch";
131    a ~= "v" ~ branch;
132    a ~= url;
133    a ~= folder;
134 
135    return a;
136 }
137 
138 unittest {
139    string[] x = getCloneCmd("git@o30", "0.13.0", "cul");
140    assert(x[0] == "git");
141    assert(x[5] == "v0.13.0");
142 }
143 
144 
145 bool isValidCsFile(in string fn) {
146    import std.algorithm.searching : canFind;
147    enum ASSEMBLY = "Assembly";
148    enum TEST = "test";
149    enum APP = "App";
150    return !(canFind(fn, ASSEMBLY) || canFind(fn, TEST) || canFind(fn, APP));
151 }
152 unittest {
153    assert(isValidCsFile(".mirror/Uns/src/Cul.cs"));
154    assert(!isValidCsFile(".mirror/Uns/src/App.cs"));
155    assert(!isValidCsFile(".mirror/Uns/src/AssemblyInfo.cs"));
156    assert(!isValidCsFile(".mirror/Uns/src/IAssemblyInfo.cs"));
157    assert(!isValidCsFile(".mirror/Uns/test/Cul.cs"));
158    assert(!isValidCsFile(".mirror/Uns/tests/Cul.cs"));
159 }
160 
161 void help() {
162    enum VERSION = "0.5.0";
163    writefln("Version %s", VERSION);
164 }