1 /**
2 Copyright: Copyright (c) 2017, Joakim Brännström. All rights reserved.
3 License: MPL-2
4 Author: Joakim Brännström (joakim.brannstrom@gmx.com)
5 
6 This Source Code Form is subject to the terms of the Mozilla Public License,
7 v.2.0. If a copy of the MPL was not distributed with this file, You can obtain
8 one at http://mozilla.org/MPL/2.0/.
9 
10 Helper functions for xml reading and writing.
11 */
12 module dextool.xml;
13 
14 import std.typecons : Nullable;
15 
16 import logger = std.experimental.logger;
17 
18 import dextool.type : FileName;
19 
20 /// Generate the xml prelude.
21 void makePrelude(AppT)(ref AppT app) {
22     import std.format : formattedWrite;
23 
24     formattedWrite(app, `<?xml version="1.0" encoding="UTF-8"?>` ~ "\n");
25 }
26 
27 /// Parse an xml file.
28 Nullable!T readRawConfig(T, alias parseFunc)(FileName fname) @trusted nothrow {
29     static import std.file;
30     import std.utf : validate;
31     import std.xml;
32 
33     string msg;
34     Nullable!T rval;
35 
36     try {
37         string fin = cast(string) std.file.read(fname);
38         validate(fin);
39         check(fin);
40         auto xml = new DocumentParser(fin);
41 
42         rval = parseFunc(xml);
43         return rval;
44     }
45     catch (CheckException ex) {
46         try {
47             msg = ex.toString;
48         }
49         catch (Exception ex) {
50             msg = ex.msg;
51         }
52     }
53     catch (Exception ex) {
54         msg = ex.msg;
55     }
56 
57     try {
58         logger.errorf("Invalid xml file '%s'", cast(string) fname);
59         logger.error(msg);
60     }
61     catch (Exception ex) {
62     }
63 
64     return rval;
65 }
66 
67 /** Store the input in a configuration file to make it easy to regenerate the
68  * test double.
69  */
70 ref AppT makeXmlLog(AppT)(ref AppT app, string[] raw_cli_flags,) {
71     import std.algorithm : joiner, copy, splitter;
72     import std.array : array;
73     import std.file : thisExePath;
74     import std.format : format;
75     import std.path : baseName;
76     import std.range : dropOne, drop, takeOne;
77     import std.utf : byChar;
78     import std.xml;
79     import dextool.utility : dextoolVersion;
80     import dextool.xml : makePrelude;
81 
82     auto exe_r = thisExePath.baseName.splitter('-');
83 
84     auto exe_name = "dextool";
85     foreach (a; exe_r.save.takeOne) {
86         exe_name = a;
87     }
88 
89     auto plugin_name = "unknown_plugin";
90     foreach (a; exe_r.save.dropOne) {
91         plugin_name = a;
92     }
93 
94     auto cleaned_cli = raw_cli_flags.drop(2);
95 
96     auto doc = new Document(new Tag("dextool"));
97     doc.tag.attr["version"] = dextoolVersion;
98     {
99         auto command = new Element("command");
100         command ~= new CData(format("%s %s %s", exe_name, plugin_name,
101                 cleaned_cli.joiner(" ").byChar.array().idup));
102         doc ~= new Comment("command line when dextool was executed");
103         doc ~= command;
104     }
105 
106     makePrelude(app);
107     doc.pretty(4).joiner("\n").copy(app);
108 
109     return app;
110 }