1 module deps; 2 3 import std.range; 4 import std.regex; 5 import std.stdio; 6 import std.typecons; 7 version (unittest) import dshould; 8 9 alias Dependency = Tuple!(string, "client", string, "supplier"); 10 11 auto moduleDependencies(alias predicate)(File file) 12 { 13 import std.algorithm : filter, map; 14 15 return reader(file.byLine) 16 .filter!predicate 17 .map!(dependency => Dependency(dependency.client.name, dependency.supplier.name)); 18 } 19 20 auto reader(R)(R input) 21 { 22 return Reader!R(input); 23 } 24 25 struct Reader(R) 26 if (isInputRange!R) 27 { 28 alias Module = Tuple!(string, "name", string, "path"); 29 alias Dependency = Tuple!(Module, "client", Module, "supplier"); 30 31 private R input; 32 33 public bool empty = false; 34 35 public Dependency front; 36 37 private this(R input) 38 { 39 this.input = input; 40 popFront; 41 } 42 43 public void popFront() 44 { 45 import std.conv : to; 46 import std.regex : matchFirst, regex; 47 48 enum pattern = regex(`^(depsImport\s)?` 49 ~ `(?P<clientName>[\w.]+)\s\((?P<clientPath>.*)\)` 50 ~ `\s:[^:]*:\s` 51 ~ `(?P<supplierName>[\w.]+)\s\((?P<supplierPath>.*)\)`); 52 53 while (!this.input.empty) 54 { 55 auto captures = this.input.front.matchFirst(pattern); 56 57 scope (exit) 58 this.input.popFront; 59 60 if (captures) 61 { 62 with (this.front.client) 63 { 64 name = captures["clientName"].to!string; 65 path = captures["clientPath"].to!string; 66 } 67 with (this.front.supplier) 68 { 69 name = captures["supplierName"].to!string; 70 path = captures["supplierPath"].to!string; 71 } 72 return; 73 } 74 } 75 this.empty = true; 76 } 77 } 78 79 @("read module dependencies") 80 unittest 81 { 82 const line = "depend (src/depend.d) : private : object (/usr/include/dmd/druntime/import/object.di)"; 83 const client = tuple("depend", "src/depend.d"); 84 const supplier = tuple("object", "/usr/include/dmd/druntime/import/object.di"); 85 86 reader(only(line)).should.equal(only(tuple(client, supplier))); 87 }