1 module rest.apiv1;
2 
3 import vibe.d;
4 import rest.iapiv1;
5 import exec.iexecprovider;
6 import contentprovider;
7 
8 class ApiV1: IApiV1
9 {
10 	private IExecProvider execProvider_;
11 	private ContentProvider contentProvider_;
12 
13 	this(IExecProvider execProvider, ContentProvider contentProvider)
14 	{
15 		this.execProvider_ = execProvider;
16 		this.contentProvider_ = contentProvider;
17 	}
18 
19 	/++
20 		Parses the message contained in $(D output)
21 		and fills the appropriate errors and warnings
22 		arrays.
23 		Note: parsing s just done when $(D output.success)
24 		is false.
25 	+/
26 	private static void parseErrorsAndWarnings(ref RunOutput output)
27 	{
28 		import std.regex;
29 		import std.algorithm: splitter;
30 		import std.conv: to;
31 
32 		static ctr = ctRegex!
33 			`^[^(]+\(([0-9]+)(,[0-9]+)?\): ([a-zA-Z]+): (.*)$`;
34 
35 		foreach(line; splitter(output.output, '\n')) {
36 			auto m = line.matchFirst(ctr);
37 			if (m.empty)
38 				continue;
39 			auto lineNumber = to!int(m[1]);
40 			string type = m[3];
41 			string message = m[4];
42 
43 			switch (type) {
44 				case "Warning":
45 				case "Deprecation":
46 					output.warnings ~= RunOutput.Message(lineNumber,
47 						message);
48 					break;
49 				default:
50 					output.errors ~= RunOutput.Message(lineNumber,
51 						message);
52 			}
53 		}
54 	}
55 
56 	RunOutput run(string source)
57 	{
58 		if (source.length > 4 * 1024) {
59 			return RunOutput("ERROR: source code size is above limit.", false);
60 		}
61 
62 		auto result = execProvider_.compileAndExecute(source);
63 		auto output = RunOutput(result.output, result.success);
64 		parseErrorsAndWarnings(output);
65 		return output;
66 	}
67 
68 	SourceOutput getSource(string _language, string _chapter, string _section)
69 	{
70 		auto tourData = contentProvider_.getContent(_language, _chapter, _section);
71 		if (tourData.content == null) {
72 			throw new HTTPStatusException(404,
73 				"Couldn't find tour data for chapter '%s', section %d".format(_language, _chapter, _section));
74 		}
75 
76 		return SourceOutput(tourData.content.sourceCode);
77 	}
78 }
79 
80 unittest {
81 	string run1 = `onlineapp.d(6): Error: found '}' when expecting ';' following statement
82 onlineapp.d(6): Error: found 'EOF' when expecting '}' following compound statement
83 Failed: ["dmd", "-v", "-o-", "onlineapp.d", "-I."]`;
84 
85 	auto test2 = ApiV1.RunOutput(run1, false);
86 	ApiV1.parseErrorsAndWarnings(test2);
87 	assert(test2.errors.length == 2);
88 	assert(test2.errors[0].line == 6);
89 	assert(test2.errors[0].message[0 .. 9] == "found '}'");
90 	assert(test2.errors[1].line == 6);
91 	assert(test2.errors[1].message[0 .. 11] == "found 'EOF'");
92 
93 	string run2 = `dlang-tour ~master: building configuration "executable"...
94 ../.dub/packages/dyaml-0.5.2/source/dyaml/dumper.d(15,8): Deprecation: module std.stream is deprecated - It will be removed from Phobos in October 2016. If you still need it, go to https://github.com/DigitalMars/undeaD
95 ../.dub/packages/dyaml-0.5.2/source/dyaml/emitter.d(21,8): Deprecation: module std.stream is deprecated - It will be removed from Phobos in October 2016. If you still need it, go to https://github.com/DigitalMars/undeaD
96 ../.dub/packages/dyaml-0.5.2/source/dyaml/representer.d(679,8): Deprecation: module std.stream is deprecated - It will be removed from Phobos in October 2016. If you still need it, go to https://github.com/DigitalMars/undeaD
97 ../.dub/packages/dyaml-0.5.2/source/dyaml/loader.d(171,16): Deprecation: module std.stream is deprecated - It will be removed from Phobos in October 2016. If you still need it, go to https://github.com/DigitalMars/undeaD`;
98 	auto test3 = ApiV1.RunOutput(run2, false);
99 	ApiV1.parseErrorsAndWarnings(test3);
100 	assert(test3.warnings.length == 4);
101 	import std.algorithm: all;
102 	assert(test3.warnings.all!(x => x.message[0 .. 31] == "module std.stream is deprecated"));
103 	assert(test3.warnings[0].line == 15);
104 	assert(test3.warnings[1].line == 21);
105 	assert(test3.warnings[2].line == 679);
106 	assert(test3.warnings[3].line == 171);
107 }