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 This is a handy range to interate over either all files from the user OR all
11 files in a compilation database.
12 */
13 module dextool.compilation_db.user_filerange;
14 
15 import logger = std.experimental.logger;
16 
17 import dextool.compilation_db : CompileCommandFilter, CompileCommandDB,
18     parseFlag, SearchResult, DbCompiler = Compiler;
19 import dextool.type : FileName, AbsolutePath;
20 
21 @safe:
22 
23 struct UserFileRange {
24     import std.typecons : Nullable;
25     import dextool.compilation_db : SearchResult, ParseFlags;
26 
27     enum RangeOver {
28         inFiles,
29         database
30     }
31 
32     this(CompileCommandDB db, string[] in_files, string[] cflags,
33             const CompileCommandFilter ccFilter, const DbCompiler userCompiler = DbCompiler.init) {
34         this.db = db;
35         this.cflags = cflags;
36         this.ccFilter = ccFilter;
37         this.inFiles = in_files;
38         this.userCompiler = userCompiler;
39 
40         if (in_files.length == 0) {
41             kind = RangeOver.database;
42         } else {
43             kind = RangeOver.inFiles;
44         }
45     }
46 
47     private {
48         const RangeOver kind;
49         CompileCommandDB db;
50         string[] inFiles;
51         string[] cflags;
52         const CompileCommandFilter ccFilter;
53         const DbCompiler userCompiler;
54     }
55 
56     Nullable!SearchResult front() {
57         assert(!empty, "Can't get front of an empty range");
58 
59         typeof(return) curr;
60 
61         final switch (kind) {
62         case RangeOver.inFiles:
63             if (db.length > 0) {
64                 curr = db.findFlags(FileName(inFiles[0]), cflags, ccFilter, userCompiler);
65             } else {
66                 curr = SearchResult(cflags.dup, AbsolutePath(FileName(inFiles[0])));
67             }
68             break;
69         case RangeOver.database:
70             curr = SearchResult(db.payload[0].parseFlag(ccFilter,
71                     userCompiler), db.payload[0].absoluteFile);
72             curr.get.flags.cflags = cflags ~ curr.get.flags.cflags;
73             break;
74         }
75 
76         return curr;
77     }
78 
79     void popFront() {
80         assert(!empty, "Can't pop front of an empty range");
81 
82         final switch (kind) {
83         case RangeOver.inFiles:
84             inFiles = inFiles[1 .. $];
85             break;
86         case RangeOver.database:
87             db.payload = db.payload[1 .. $];
88             break;
89         }
90     }
91 
92     bool empty() @safe pure nothrow const @nogc {
93         final switch (kind) {
94         case RangeOver.inFiles:
95             return inFiles.length == 0;
96         case RangeOver.database:
97             return db.length == 0;
98         }
99     }
100 
101     size_t length() @safe pure nothrow const @nogc {
102         final switch (kind) {
103         case RangeOver.inFiles:
104             return inFiles.length;
105         case RangeOver.database:
106             return db.length;
107         }
108     }
109 }
110 
111 private:
112 
113 import std.typecons : Nullable;
114 
115 /// Find flags for fname by searching in the compilation DB.
116 Nullable!SearchResult findFlags(ref CompileCommandDB compdb, FileName fname, const string[] flags,
117         ref const CompileCommandFilter flag_filter, DbCompiler userCompiler = DbCompiler.init) {
118     import dextool.compilation_db : appendOrError;
119 
120     auto rval = compdb.appendOrError(flags, fname, flag_filter);
121     logger.error(rval.isNull, "Unable to find any compiler flags for: ", fname);
122     return rval;
123 }