1 module dud.pkgdescription.joining;
2 
3 import std.array : array, empty, front;
4 import std.algorithm.searching : canFind, find;
5 import std.algorithm.sorting : sort;
6 import std.algorithm.iteration : uniq, filter, each;
7 import std.exception : enforce;
8 import std.format : format;
9 import std.traits : FieldNameTuple;
10 import std.typecons : nullable, Nullable, apply;
11 
12 import dud.pkgdescription;
13 import dud.pkgdescription.exception;
14 import dud.pkgdescription.compare;
15 import dud.pkgdescription.duplicate;
16 import dud.pkgdescription.duplicate : ddup = dup;
17 
18 @safe:
19 
20 PackageDescription expandConfiguration(ref const(PackageDescription) pkg,
21 		string confName)
22 {
23 	PackageDescription ret = pkg.ddup();
24 	() @trusted { ret.configurations.clear(); }();
25 
26 	const(PackageDescription) conf = findConfiguration(pkg, confName);
27 	joinPackageDescription(ret, pkg, conf);
28 	return ret;
29 }
30 
31 PackageDescription expandBuildType(ref const(PackageDescription) pkg,
32 		string buildTypeName)
33 {
34 	PackageDescription ret = pkg.ddup();
35 	const(BuildType) buildType = findBuildType(pkg, buildTypeName);
36 	joinPackageDescription(ret, pkg, buildType.pkg);
37 	return ret;
38 }
39 
40 void joinPackageDescription(ref PackageDescription ret,
41 		ref const(PackageDescription) orig, ref const(PackageDescription) conf)
42 {
43 	import dud.pkgdescription.helper : isMem;
44 	static foreach(mem; FieldNameTuple!PackageDescription) {
45 		// override with conf
46 		static if(canFind(
47 			[ isMem!"systemDependencies", isMem!"ddoxTool", isMem!"platforms"
48 			, isMem!"buildOptions"
49 			], mem))
50 		{
51 			__traits(getMember, ret, mem) =
52 				dud.pkgdescription.duplicate.dup(
53 					__traits(getMember, conf, mem));
54 		} else static if(canFind(
55 			[ isMem!"targetPath", isMem!"targetName", isMem!"mainSourceFile"
56 			, isMem!"workingDirectory"
57 			], mem))
58 		{
59 			__traits(getMember, ret, mem) = selectIfEmpty(
60 				__traits(getMember, ret, mem),
61 				__traits(getMember, conf, mem));
62 		} else static if(canFind(
63 			[ isMem!"dflags", isMem!"lflags", isMem!"versions"
64 			, isMem!"importPaths", isMem!"sourcePaths", isMem!"sourceFiles"
65 			, isMem!"stringImportPaths" , isMem!"excludedSourceFiles"
66 			, isMem!"copyFiles" , isMem!"preGenerateCommands"
67 			, isMem!"postGenerateCommands" , isMem!"preBuildCommands"
68 			, isMem!"postBuildCommands" , isMem!"preRunCommands"
69 			, isMem!"postRunCommands", isMem!"libs", isMem!"versionFilters"
70 			, isMem!"debugVersionFilters", isMem!"debugVersions"
71 			, isMem!"toolchainRequirements", isMem!"dependencies"
72 			, isMem!"subConfigurations", isMem!"buildTypes"
73 			, isMem!"targetType"
74 			], mem))
75 		{
76 			__traits(getMember, ret, mem) =
77 				join(__traits(getMember, orig, mem),
78 						__traits(getMember, conf, mem));
79 		} else static if(canFind(
80 			[ isMem!"name", isMem!"description", isMem!"homepage"
81 			, isMem!"license", isMem!"authors"//, isMem!"version_"
82 			, isMem!"copyright", isMem!"subPackages"
83 			, isMem!"ddoxFilterArgs", isMem!"configurations"
84 			, isMem!"buildRequirements"
85 			], mem))
86 		{
87 			// global options not allowed to change by configuration
88 		} else {
89 			pragma(msg, mem);
90 		}
91 	}
92 }
93 
94 String selectIfEmpty(const(String) a, const(String) b) {
95 	return b.platforms.empty
96 		? a.ddup()
97 		: b.ddup();
98 }
99 
100 UnprocessedPath selectIfEmpty(const(UnprocessedPath) a,
101 		const(UnprocessedPath) b)
102 {
103 	return b.path.empty
104 		? a.ddup()
105 		: b.ddup();
106 }
107 
108 Path selectIfEmpty(const(Path) a, const(Path) b) {
109 	return b.platforms.empty
110 		? a.ddup()
111 		: b.ddup();
112 }
113 
114 string selectIfEmpty(const(string) a, const(string) b) {
115 	return b.empty
116 		? a.ddup()
117 		: b.ddup();
118 }
119 
120 SubConfigs join(ref const(SubConfigs) a, ref const(SubConfigs) b) {
121 	SubConfigs ret = b.ddup();
122 	a.unspecifiedPlatform.byKeyValue()
123 		.filter!(kv => kv.key !in ret.unspecifiedPlatform)
124 		.each!(kv => ret.unspecifiedPlatform[kv.key] = kv.value);
125 
126 	foreach(key, value; a.configs) {
127 		if(key !in ret.configs) {
128 			ret.configs[key] = string[string].init;
129 		}
130 		foreach(key2, value2; value) {
131 			if(key2 !in ret.configs[key]) {
132 				ret.configs[key][key2] = value2.ddup();
133 			}
134 		}
135 	}
136 	return ret;
137 }
138 
139 BuildType[string] join(const(BuildType[string]) a,
140 		const(BuildType[string]) b)
141 {
142 	BuildType[string] ret = b.ddup();
143 	a.byKeyValue()
144 		.filter!(it => it.key !in ret)
145 		.each!(bt => ret[bt.key] = bt.value.ddup());
146 	return ret;
147 }
148 
149 Dependency[] join(const(Dependency[]) a, const(Dependency[]) b) {
150 	Dependency[] ret = b.ddup();
151 	a.filter!(dep =>
152 			!canFind!((g, h) => g.name == h.name
153 				&& g.platforms == h.platforms)(ret, dep))
154 		.each!(dep => ret ~= dep.ddup());
155 	return ret;
156 }
157 
158 ToolchainRequirement[Toolchain] join(const(ToolchainRequirement[Toolchain]) a,
159 		const(ToolchainRequirement[Toolchain]) b)
160 {
161 	ToolchainRequirement[Toolchain] ret = dud.pkgdescription.duplicate.dup(a);
162 	b.byKeyValue()
163 		.each!(it => ret[it.key] =
164 				dud.pkgdescription.duplicate.dup(it.value));
165 	return ret;
166 }
167 
168 Paths join(const(Paths) a, const(Paths) b) {
169 	Paths ret = dud.pkgdescription.duplicate.dup(a);
170 	b.platforms
171 		.filter!(it =>
172 				!canFind!((g, h) => areEqual(g, h))(a.platforms, it))
173 		.each!(it => ret.platforms ~= dud.pkgdescription.duplicate.dup(it));
174 	return ret;
175 }
176 
177 TargetType join(const(TargetType) a, const(TargetType) b) {
178 	return b != TargetType.autodetect
179 		? b
180 		: a;
181 }
182 
183 Strings join(const(Strings) a, const(Strings) b) {
184 	Strings ret = dud.pkgdescription.duplicate.dup(a);
185 	b.platforms
186 		.filter!(it =>
187 				!canFind!((g, h) => areEqual(g, h))(a.platforms, it))
188 		.each!(it => ret.platforms ~= dud.pkgdescription.duplicate.dup(it));
189 	return ret;
190 }
191 
192 string[] join(const(string[]) a, const(string[]) b) {
193 	string[] ret = (dud.pkgdescription.duplicate.dup(a)
194 			~ dud.pkgdescription.duplicate.dup(b)).sort.uniq.array;
195 	return ret;
196 }
197 
198 //
199 // Helper
200 //
201 
202 const(BuildType) findBuildType(const PackageDescription pkg,
203 	string buildTypeName)
204 {
205 	const(BuildType)* ret = buildTypeName in pkg.buildTypes;
206 	enforce!UnknownBuildType(ret !is null,
207 		format("'%s' is a unknown buildType of package '%s'", pkg.name));
208 	return *ret;
209 }
210 
211 const(PackageDescription) findConfiguration(const PackageDescription pkg,
212 	string confName)
213 {
214 	const(PackageDescription)* ret = confName in pkg.configurations;
215 	enforce!UnknownConfiguration(ret !is null,
216 		format("'%s' is a unknown configuration of package '%s'", pkg.name));
217 	return *ret;
218 }