1 module workspaced.com.snippets.dependencies;
2 
3 import workspaced.api;
4 import workspaced.com.dub;
5 import workspaced.com.snippets;
6 
7 import std.algorithm;
8 
9 ///
10 alias SnippetList = PlainSnippet[];
11 
12 /// A list of dependencies usable in an associative array
13 struct DependencySet
14 {
15 	string[] sorted;
16 
17 	void set(string[] deps)
18 	{
19 		deps.sort!"a<b";
20 		sorted = deps;
21 	}
22 
23 	bool hasAll(string[] deps) const
24 	{
25 		deps.sort!"a<b";
26 		int a, b;
27 		while (a < sorted.length && b < deps.length)
28 		{
29 			const as = sorted[a];
30 			const bs = deps[b];
31 			const c = cmp(as, bs);
32 
33 			if (c == 0)
34 			{
35 				a++;
36 				b++;
37 			}
38 			else if (c < 0)
39 				return false;
40 			else
41 				b++;
42 		}
43 		return a == sorted.length;
44 	}
45 
46 	bool opEquals(const ref DependencySet other) const
47 	{
48 		return sorted == other.sorted;
49 	}
50 
51 	size_t toHash() const @safe nothrow
52 	{
53 		size_t ret;
54 		foreach (v; sorted)
55 			ret ^= typeid(v).getHash((() @trusted => &v)());
56 		return ret;
57 	}
58 }
59 
60 class DependencyBasedSnippetProvider : SnippetProvider
61 {
62 	SnippetList[DependencySet] snippets;
63 
64 	void addSnippet(string[] requiredDependencies, PlainSnippet snippet)
65 	{
66 		DependencySet set;
67 		set.set(requiredDependencies);
68 
69 		if (auto v = set in snippets)
70 			*v ~= snippet;
71 		else
72 			snippets[set] = [snippet];
73 	}
74 
75 	Future!(Snippet[]) provideSnippets(scope const WorkspaceD.Instance instance,
76 			scope const(char)[] file, scope const(char)[] code, int position, const SnippetInfo info)
77 	{
78 		if (!instance.has!DubComponent)
79 			return typeof(return).fromResult(null);
80 		else
81 		{
82 			string id = typeid(this).name;
83 			auto dub = instance.get!DubComponent;
84 			return typeof(return).async(delegate() {
85 				string[] deps;
86 				foreach (dep; dub.dependencies)
87 				{
88 					deps ~= dep.name;
89 					deps ~= dep.dependencies.keys;
90 				}
91 				Snippet[] ret;
92 				foreach (k, v; snippets)
93 				{
94 					if (k.hasAll(deps))
95 					{
96 						foreach (snip; v)
97 							if (snip.levels.canFind(info.level))
98 								ret ~= snip.buildSnippet(id);
99 					}
100 				}
101 				return ret;
102 			});
103 		}
104 	}
105 
106 	Future!Snippet resolveSnippet(scope const WorkspaceD.Instance instance,
107 			scope const(char)[] file, scope const(char)[] code, int position,
108 			const SnippetInfo info, Snippet snippet)
109 	{
110 		snippet.resolved = true;
111 		return typeof(return).fromResult(snippet);
112 	}
113 }
114 
115 unittest
116 {
117 	DependencySet set;
118 	set.set(["vibe-d", "mir", "serve-d"]);
119 	assert(set.hasAll(["vibe-d", "serve-d", "mir"]));
120 	assert(set.hasAll(["vibe-d", "serve-d", "serve-d", "serve-d", "mir", "mir"]));
121 	assert(set.hasAll(["vibe-d", "serve-d", "mir", "workspace-d"]));
122 	assert(set.hasAll(["diet-ng", "vibe-d", "serve-d", "mir", "workspace-d"]));
123 	assert(!set.hasAll(["diet-ng", "serve-d", "mir", "workspace-d"]));
124 	assert(!set.hasAll(["diet-ng", "serve-d", "vibe-d", "workspace-d"]));
125 	assert(!set.hasAll(["diet-ng", "mir", "mir", "vibe-d", "workspace-d"]));
126 	assert(!set.hasAll(["diet-ng", "mir", "vibe-d", "workspace-d"]));
127 
128 	set.set(["vibe-d:http"]);
129 	assert(set.hasAll([
130 				"botan", "botan", "botan-math", "botan-math", "diet-ng", "diet-ng",
131 				"eventcore", "eventcore", "libasync", "libasync", "memutils",
132 				"memutils", "memutils", "mir-linux-kernel", "mir-linux-kernel",
133 				"openssl", "openssl", "openssl", "stdx-allocator", "stdx-allocator",
134 				"stdx-allocator", "taggedalgebraic", "taggedalgebraic", "vibe-core",
135 				"vibe-core", "vibe-d", "vibe-d:core", "vibe-d:core", "vibe-d:core",
136 				"vibe-d:core", "vibe-d:core", "vibe-d:core", "vibe-d:crypto",
137 				"vibe-d:crypto", "vibe-d:crypto", "vibe-d:data", "vibe-d:data",
138 				"vibe-d:data", "vibe-d:http", "vibe-d:http", "vibe-d:http", "vibe-d:http",
139 				"vibe-d:http", "vibe-d:inet", "vibe-d:inet", "vibe-d:inet", "vibe-d:inet",
140 				"vibe-d:mail", "vibe-d:mail", "vibe-d:mongodb", "vibe-d:mongodb",
141 				"vibe-d:redis", "vibe-d:redis", "vibe-d:stream", "vibe-d:stream",
142 				"vibe-d:stream", "vibe-d:stream", "vibe-d:textfilter", "vibe-d:textfilter",
143 				"vibe-d:textfilter", "vibe-d:textfilter", "vibe-d:tls", "vibe-d:tls",
144 				"vibe-d:tls", "vibe-d:tls", "vibe-d:utils", "vibe-d:utils", "vibe-d:utils",
145 				"vibe-d:utils", "vibe-d:utils", "vibe-d:utils", "vibe-d:web", "vibe-d:web"
146 			]));
147 
148 	set.set(null);
149 	assert(set.hasAll([]));
150 	assert(set.hasAll(["foo"]));
151 }