1 /**
2 License:
3     Boost Software License - Version 1.0 - August 17th, 2003
4 
5     Permission is hereby granted, free of charge, to any person or organization
6     obtaining a copy of the software and accompanying documentation covered by
7     this license (the "Software") to use, reproduce, display, distribute,
8     execute, and transmit the Software, and to prepare derivative works of the
9     Software, and to permit third-parties to whom the Software is furnished to
10     do so, all subject to the following:
11 
12     The copyright notices in the Software and this entire statement, including
13     the above license grant, this restriction and the following disclaimer,
14     must be included in all copies of the Software, in whole or in part, and
15     all derivative works of the Software, unless such copies or derivative
16     works are solely in the form of machine-executable object code generated by
17     a source language processor.
18 
19     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20     IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21     FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
22     SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
23     FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
24     ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25     DEALINGS IN THE SOFTWARE.
26 
27 Authors:
28     Alexandru Ermicioi
29 **/
30 module aermicioi.aedi_property_reader.arg.accessor;
31 
32 import aermicioi.aedi.exception.not_found_exception;
33 import aermicioi.aedi_property_reader.core.accessor;
34 import std.algorithm;
35 import std.array;
36 import std.string;
37 import std.conv;
38 import std.range;
39 
40 /**
41 Accessor filtering a list of strings out of strings that are not containing a command line property
42 **/
43 class ArgumentAccessor : PropertyAccessor!(const(string)[]) {
44 
45     private static struct Filter {
46         string property;
47 
48         const(string)[] component;
49         const(string)[] buff;
50 
51         nothrow {
52             this(const(string)[] component, string property) {
53                 this.component = component;
54                 this.property = property;
55                 take(1);
56             }
57 
58             Filter save() {
59                 return this;
60             }
61 
62             string front() {
63 
64                 return buff[0];
65             }
66 
67             void popFront() {
68                 if (buff.length > 1) {
69 
70                     buff = buff.drop(1);
71                     return;
72                 }
73 
74                 if (buff.length > 0) {
75 
76                     buff = buff.drop(1);
77                     this.advance;
78                 }
79             }
80 
81             bool empty() {
82                 return buff.empty && component.empty;
83             }
84 
85             private void take(size_t amount = 1) {
86                 buff = (component.length >= amount) ? component.take(amount) : component.take(component.length);
87                 component = (component.length >= amount) ? component.drop(amount) : component.drop(component.length);
88             }
89 
90             private void advance() {
91                 while (!component.empty) {
92                     try {
93 
94                         if (component.front.commonPrefix("--").equal("--")) {
95                             auto splitted = component.front.splitter("=");
96 
97                             if ((splitted.front.strip('-') == property) && !splitted.take(1).empty) {
98                                 take(1);
99                                 return;
100                             }
101 
102                             if ((component.length > 1) && (splitted.front.strip('-') == property) && splitted.drop(1).front.commonPrefix("--").empty) {
103                                 take(2);
104                                 return;
105                             }
106                         }
107 
108                         if (component.front.commonPrefix("--").equal("-")) {
109                             import std.stdio;
110 
111                             if ((component.front.strip('-').equal(property)) || ((property.length == 1) && component.front.strip('-').canFind(property))) {
112                                 take(1);
113                                 return;
114                             }
115                         }
116 
117                         if (!component.front.splitter("=").drop(1).empty && component.front.splitter("=").front.equal(property)) {
118                             take(1);
119                             return;
120                         }
121 
122                         if (property.isNumeric) {
123                             auto up = property.to!size_t;
124                             size_t current;
125 
126                             auto count = component.countUntil!(c => c.commonPrefix("--").empty && c.splitter("=").drop(1).empty && (current++ == up));
127                             component = component.drop(count);
128                             take(1);
129                             component = null;
130                             return;
131                         }
132 
133                         component = component.empty ? component : component.drop(1);
134                     } catch (Exception e) {
135                         assert(false, text("Could not filter out command line arguments for ", property));
136                     }
137                 }
138             }
139         }
140     }
141 
142     /**
143      Get a property out of component
144 
145      Params:
146          component = a component which has some properties identified by property.
147      Throws:
148          NotFoundException in case when no requested property is available.
149          InvalidArgumentException in case when passed arguments are somehow invalid for use.
150      Returns:
151          FieldType accessed property.
152      **/
153     const(string)[] access(const(string)[] component, string property) const {
154         if (property.empty) {
155             return component;
156         }
157 
158         return Filter(component, property).array;
159     }
160 
161     /**
162      Check if requested property is present in component.
163 
164      Check if requested property is present in component.
165      The method could have allocation side effects due to the fact that
166      it is not restricted in calling access method of the accessor.
167 
168      Params:
169          component = component which is supposed to have property
170          property = speculated property that is to be tested if it is present in component
171      Returns:
172          true if property is in component
173      **/
174     bool has(in const(string)[] component, string property) const nothrow {
175         import std.experimental.logger;
176         import aermicioi.aedi_property_reader.core.traits;
177         // debug(trace) trace(property, " ----------------------- ", component, Filter(component, property)).n;
178         if (component.length > 1) {
179 
180             return !Filter(component, property).drop(1).empty;
181         }
182 
183         return false;
184     }
185 
186     /**
187      Identify the type of supported component.
188 
189      Identify the type of supported component. It returns type info of component
190      if it is supported by accessor, otherwise it will return typeid(void) denoting that
191      the type isn't supported by accessor. The accessor is not limited to returning the type
192      info of passed component, it can actually return type info of super type or any type
193      given the returned type is implicitly convertible or castable to ComponentType.
194 
195      Params:
196          component = the component for which accessor should identify the underlying type
197 
198      Returns:
199          TypeInfo type information about passed component, or typeid(void) if component is not supported.
200      **/
201     TypeInfo componentType(const(string)[] component) const nothrow {
202         return typeid(const(string)[]);
203     }
204 }