1 module check;
2 
3 import model;
4 import std.typecons;
5 version (unittest) import unit_threaded;
6 
7 struct Checker
8 {
9     private Dependency[] explicitDependencies;
10 
11     private Dependency[]  implicitDependencies;
12 
13     this(Dependency[] targetDependencies, bool simplify)
14     {
15         import std.algorithm : partition;
16 
17         bool implict(Dependency dependency)
18         {
19             import std.algorithm : any, filter;
20 
21             return targetDependencies
22                 .filter!(targetDependency => targetDependency != dependency)
23                 .any!(targetDependency => targetDependency.implies(dependency));
24         }
25 
26         auto dependencies = targetDependencies.dup;
27 
28         if (simplify)
29         {
30             this.explicitDependencies = dependencies.partition!implict;
31             this.implicitDependencies = dependencies[0 .. $ - this.explicitDependencies.length];
32             return;
33         }
34         this.implicitDependencies = dependencies;
35 }
36 
37     bool allows(Dependency actualDependency)
38     {
39         import std.algorithm : any;
40 
41         return this.explicitDependencies.any!(dependency => actualDependency.implies(dependency))
42             || this.implicitDependencies.any!(dependency => actualDependency == dependency);
43     }
44 }
45 
46 @("check for allowed dependencies")
47 unittest
48 {
49     auto dependencies = [
50         Dependency("a", "b"),
51         Dependency("a.x", "b.y"),
52         Dependency("b", "c"),
53         ];
54 
55     with (Checker(dependencies, Yes.simplify))
56     {
57         allows(Dependency("a", "b")).shouldBeTrue;
58         allows(Dependency("a.x", "b.y")).shouldBeTrue;
59         allows(Dependency("b", "c")).shouldBeTrue;
60         allows(Dependency("b.x", "c")).shouldBeTrue;  // implies explicit dependency
61 
62         allows(Dependency("a.x", "b")).shouldBeFalse;  // implies implicit dependency
63     }
64 }
65 
66 bool implies(Dependency lhs, Dependency rhs)
67 {
68     import std.algorithm : startsWith;
69 
70     return lhs.client.names.startsWith(rhs.client.names)
71         && lhs.supplier.names.startsWith(rhs.supplier.names);
72 }
73 
74 @("check for implied dependencies")
75 unittest
76 {
77     Dependency("a", "b").implies(Dependency("a", "b")).shouldBeTrue;
78     Dependency("a.x", "b.y").implies(Dependency("a", "b")).shouldBeTrue;
79 
80     Dependency("a.x", "b").implies(Dependency("a", "b.y")).shouldBeFalse;
81     Dependency("aa", "bb").implies(Dependency("a", "b")).shouldBeFalse;
82 }