1 /**
2 Copyright: Copyright (c) 2016, 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 Utility useful for plugins.
11 */
12 module dextool.plugin.utility;
13 
14 import logger = std.experimental.logger;
15 
16 import dextool.plugin.types : CliBasicOption, CliOptionParts;
17 
18 version (unittest) {
19     import unit_threaded : shouldEqual;
20 }
21 
22 /** Make a static c'tor that creates an instance with all class members initialized.
23  *
24  * Params:
25  *   T = type to construct an instance of
26  *   postInit = call the function with the initialized instance.
27  */
28 mixin template MakerInitializingClassMembers(T, alias postInit = function void(ref T) {
29 }) {
30     static T make() {
31         T inst;
32 
33         foreach (member; __traits(allMembers, T)) {
34             alias MemberT = typeof(__traits(getMember, inst, member));
35             static if (is(MemberT == class)) {
36                 __traits(getMember, inst, member) = new MemberT;
37             }
38         }
39 
40         postInit(inst);
41 
42         return inst;
43     }
44 }
45 
46 /** Convenient array with support for marking of elements for later removal.
47  */
48 struct MarkArray(T) {
49     import std.array : Appender;
50 
51     alias Range = T[];
52     private Appender!(size_t[]) remove_;
53     private Appender!(T*[]) arr;
54 
55     /// Store e in the cache.
56     void put(T e) {
57         auto item = new T;
58         *item = e;
59         arr.put(item);
60     }
61 
62     /// ditto
63     void put(T[] e) {
64         import std.algorithm : map;
65 
66         foreach (b; e.map!((a) { auto item = new T; *item = a; return item; })) {
67             arr.put(b);
68         }
69     }
70 
71     /// Retrieve a slice of the stored data.
72     auto data() {
73         import std.algorithm : map;
74 
75         return arr.data.map!(a => *a);
76     }
77 
78     /** Mark index `idx` for removal.
79      *
80      * Later as in calling $(D doRemoval).
81      */
82     void markForRemoval(size_t idx) @safe pure {
83         remove_.put(idx);
84     }
85 
86     /// Remove all items that has been marked.
87     void doRemoval() {
88         import std.algorithm : canFind, filter, map;
89         import std.range : enumerate;
90 
91         // naive implementation. Should use swapping instead.
92         typeof(arr) new_;
93         new_.put(arr.data.enumerate.filter!(a => !canFind(remove_.data,
94                 a.index)).map!(a => a.value));
95         arr.clear;
96         remove_.clear;
97 
98         arr = new_;
99     }
100 
101     /// Clear the $(D MarkArray).
102     void clear() {
103         arr.clear;
104         remove_.clear;
105     }
106 }
107 
108 @("Should store item")
109 unittest {
110     MarkArray!int arr;
111 
112     arr.put(10);
113 
114     arr.data.length.shouldEqual(1);
115     arr.data[0].shouldEqual(10);
116 }
117 
118 @("Should mark and remove items")
119 unittest {
120     MarkArray!int arr;
121     arr.put([10, 20, 30]);
122 
123     arr.markForRemoval(1);
124     arr.data.length.shouldEqual(3);
125 
126     arr.doRemoval;
127 
128     arr.data.length.shouldEqual(2);
129     arr.data[0].shouldEqual(10);
130     arr.data[1].shouldEqual(30);
131 }