1 /*
2  * hunt-console eases the creation of beautiful and testable command line interfaces.
3  *
4  * Copyright (C) 2018-2019, HuntLabs
5  *
6  * Website: https://www.huntlabs.net
7  *
8  * Licensed under the Apache-2.0 License.
9  *
10  */
11  
12 module hunt.console.input.StringInput;
13 
14 import hunt.console.error.InvalidArgumentException;
15 import hunt.console.util.StringUtils;
16 
17 import hunt.collection.ArrayList;
18 import hunt.collection.List;
19 import hunt.console.input.ArgvInput;
20 import hunt.text.Common;
21 import std.string;
22 import std.regex;
23 
24 class StringInput : ArgvInput
25 {
26     private static string REGEX_STRING = "([^\\s]+?)(?:\\s|(?<!\\\\\\\\)\"|(?<!\\\\\\\\)\\'|$)";
27 
28     private static string REGEX_QUOTED_STRING = "(?:\"([^\"\\\\\\\\]*(?:\\\\\\\\.[^\"\\\\\\\\]*)*)\"|\\'([^\\'\\\\\\\\]*(?:\\\\\\\\.[^\\'\\\\\\\\]*)*)\\')";
29 
30     public this(string input)
31     {
32         super(null);
33 
34         setTokens(tokenize(input));
35     }
36 
37     private List!(string) tokenize(string input)
38     {
39         List!(string) tokens = new ArrayList!(string)();
40         int length = cast(int)(input.length);
41         string inputPart;
42         string token;
43         int matchLength;
44         int cursor = 0;
45 
46         string whiteSpace = /* Pattern.compile */("^\\s+");
47         // Matcher whiteSpaceMatcher;
48         string quotedOption = /* Pattern.compile */("^([^=\"\\'\\s]+?)(=?)(" ~ REGEX_QUOTED_STRING ~ "+)");
49         // Matcher quotedOptionMatcher;
50         string quotedstring = /* Pattern.compile */("^" ~ REGEX_QUOTED_STRING);
51         // Matcher quotedStringMatcher;
52         string str = /* Pattern.compile */("^" ~ REGEX_STRING);
53         // Matcher stringMatcher;
54 
55         while (cursor < length) {
56 
57             inputPart = input.substring(cursor);
58 
59             auto whiteSpaceMatcher =matchAll(inputPart, whiteSpace);
60             auto quotedOptionMatcher = matchAll(inputPart,quotedOption);
61             auto quotedStringMatcher = matchAll(inputPart,quotedstring);
62             auto stringMatcher = matchAll(inputPart,str);
63 
64             if (!whiteSpaceMatcher.empty()) {
65                 matchLength = cast(int)(whiteSpaceMatcher.front().captures[0].length);
66             } else if (!quotedOptionMatcher.empty()) {
67                 token = quotedOptionMatcher.front().captures[1] ~ quotedOptionMatcher.front().captures[2] ~ StringUtils.unquote(quotedOptionMatcher.front().captures[3].substring(1, quotedOptionMatcher.front().captures[3].length - 1).replace("(\"')|('\")|('')|(\"\")", ""));
68                 tokens.add(token);
69                 matchLength = cast(int)(quotedOptionMatcher.front().captures[0].length);
70             } else if (!quotedStringMatcher.empty()) {
71                 token = quotedStringMatcher.front().captures[0];
72                 tokens.add(StringUtils.unquote(token.substring(1, token.length - 1)));
73                 matchLength = cast(int)(token.length);
74             } else if (!stringMatcher.empty()) {
75                 token = stringMatcher.front().captures[1];
76                 tokens.add(StringUtils.unquote(token));
77                 matchLength = cast(int)(stringMatcher.front().captures[0].length);
78             } else {
79                 // should never happen
80                 throw new InvalidArgumentException(format("Unable to parse input near '... %s ...", input.substring(cursor, cursor + 10)));
81             }
82 
83             cursor += matchLength;
84         }
85 
86         return tokens;
87     }
88 }