create-schematic-gen-site/source/csgs/extract.d

124 lines
4.2 KiB
D

module csgs.extract;
import handy_httpd;
import slf4d;
import std.typecons;
import std.file;
import std.path;
const EXTRACTS_DIR = "extracts";
const EXTRACT_FILENAME = "__EXTRACT__.json";
const EXTRACT_COMMAND = ["java", "-jar", "materials-extractor-v1.0.0.jar"];
bool validateExtractRequest(ref MultipartFormData data, ref HttpResponse response) {
if (data.elements.length < 2) {
response.setStatus(HttpStatus.BAD_REQUEST);
response.writeBodyString("Requires at least 2 form data elements (schematic file and count).");
return false;
}
uint nextElementIdx = 0;
while (nextElementIdx < data.elements.length) {
MultipartElement element = data.elements[nextElementIdx++];
if (element.name == "schematics") {
if (element.filename.isNull || element.filename.get().length < 5 || element.content.length < 10) {
response.setStatus(HttpStatus.BAD_REQUEST);
response.writeBodyString("Invalid or missing schematic file.");
return false;
}
const string filename = element.filename.get();
if (nextElementIdx == data.elements.length) {
response.setStatus(HttpStatus.BAD_REQUEST);
response.writeBodyString("Missing count element for schematic: " ~ filename);
return false;
}
MultipartElement countElement = data.elements[nextElementIdx++];
import std.conv;
try {
immutable uint count = countElement.content.to!uint;
if (count < 1 || count > 1000) throw new Exception("out of range: count should be between 1 and 1000, inclusive.");
} catch (Exception e) {
response.setStatus(HttpStatus.BAD_REQUEST);
response.writeBodyString("Invalid count element: " ~ e.msg);
return false;
}
} else if (element.name == "processing-terminal") {
// TODO: Check processing-terminal format.
} else {
response.setStatus(HttpStatus.BAD_REQUEST);
response.writeBodyString("Unknown element: " ~ element.name);
return false;
}
}
return true;
}
string doExtract(ref MultipartFormData data) {
import std.json;
import std.uuid;
import std.process;
immutable UUID extractId = randomUUID();
immutable string extractIdStr = extractId.toString();
immutable string extractDir = buildPath(EXTRACTS_DIR, extractIdStr);
if (!exists(extractDir)) {
mkdirRecurse(extractDir);
}
string[] filenames;
uint[] counts;
foreach (MultipartElement element; data.elements) {
if (element.name == "schematics") {
const filePath = buildPath(extractDir, element.filename.get());
std.file.write(filePath, element.content);
filenames ~= filePath;
} else if (element.name == "counts") {
import std.conv;
immutable uint count = element.content.to!uint;
counts ~= count;
}
}
infoF!"Running extract process on files: %s"(filenames);
Nullable!string extractorPath = getExtractorProgramPath();
if (extractorPath.isNull) {
throw new HttpStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "Could not find extractor program.");
}
auto extractionResult = execute(["java", "-jar", extractorPath.get()] ~ filenames);
immutable int exitCode = extractionResult.status;
infoF!"Exit code: %d"(exitCode);
rmdirRecurse(extractDir);
if (exitCode != 0) {
throw new HttpStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "Extraction program failed.");
} else {
immutable string extractJsonPath = extractDir ~ ".json";
// It was successful, so add __COUNT__ to each object in the extract data.
JSONValue extractData = parseJSON(extractionResult.output);
for (uint i = 0; i < extractData.array.length; i++) {
extractData.array[i].object["__COUNT__"] = JSONValue(counts[i]);
}
std.file.write(extractJsonPath, extractData.toPrettyString());
return extractIdStr;
}
}
Nullable!string getExtractorProgramPath() {
import std.string;
foreach (DirEntry entry; dirEntries(getcwd(), SpanMode.shallow, false)) {
if (entry.isFile() && endsWith(entry.name, ".jar")) {
return nullable(entry.name);
}
}
return Nullable!string.init;
}
Nullable!string getCurrentExtractorVersion() {
import std.regex;
Nullable!string extractorPath = getExtractorProgramPath();
if (extractorPath.isNull) return Nullable!string.init;
auto r = regex(`v\d+\.\d+\.\d+`);
auto c = matchFirst(extractorPath.get(), r);
if (c.empty) return Nullable!string.init;
return nullable(c.front());
}