1 /** 2 Copyright: Copyright (c) 2020, Joakim Brännström. All rights reserved. 3 License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) 4 Author: Joakim Brännström (joakim.brannstrom@gmx.com) 5 */ 6 module my.filter; 7 8 import logger = std.experimental.logger; 9 10 @safe: 11 12 /** Filter strings by first cutting out a region (include) and then selectively 13 * remove (exclude) from that region. 14 * 15 * I often use this in my programs to allow a user to specify what files to 16 * process and the have some control over what to exclude. 17 * 18 * `--re-include` and `--re-exclude` is a suggestion for parameters to use with 19 * `getopt`. 20 */ 21 struct ReFilter { 22 import std.regex : Regex, regex, matchFirst; 23 24 Regex!char includeRe; 25 Regex!char[] excludeRe; 26 27 /** 28 * The regular expressions are set to ignoring the case. 29 * 30 * Params: 31 * include = regular expression. 32 * exclude = regular expression. 33 */ 34 this(string include, string[] exclude) { 35 includeRe = regex(include, "i"); 36 foreach (r; exclude) 37 excludeRe ~= regex(r, "i"); 38 } 39 40 /** 41 * Returns: true if `s` matches `ìncludeRe` and NOT matches any of `excludeRe`. 42 */ 43 bool match(string s) { 44 if (matchFirst(s, includeRe).empty) 45 return false; 46 47 foreach (ref re; excludeRe) { 48 if (!matchFirst(s, re).empty) 49 return false; 50 } 51 52 return true; 53 } 54 } 55 56 /// Example: 57 unittest { 58 import std.algorithm : filter; 59 import std.array : array; 60 61 auto r = ReFilter("foo.*", [".*bar.*", ".*batman"]); 62 63 assert(["foo", "foobar", "foo smurf batman", "batman", "fo", 64 "foo mother"].filter!(a => r.match(a)).array == [ 65 "foo", "foo mother" 66 ]); 67 } 68 69 /** Filter strings by first cutting out a region (include) and then selectively 70 * remove (exclude) from that region. 71 * 72 * I often use this in my programs to allow a user to specify what files to 73 * process and the have some control over what to exclude. 74 */ 75 struct GlobFilter { 76 string[] include; 77 string[] exclude; 78 79 /** 80 * The regular expressions are set to ignoring the case. 81 * 82 * Params: 83 * include = glob string patter 84 * exclude = glob string patterh 85 */ 86 this(string[] include, string[] exclude) { 87 this.include = include; 88 this.exclude = exclude; 89 } 90 91 /** 92 * Returns: true if `s` matches `ìncludeRe` and NOT matches any of `excludeRe`. 93 */ 94 bool match(string s) { 95 import std.algorithm : canFind; 96 import std.path : globMatch; 97 98 if (!canFind!((a, b) => globMatch(b, a))(include, s)) { 99 debug logger.tracef("%s did not match any of %s", s, include); 100 return false; 101 } 102 103 if (canFind!((a, b) => globMatch(b, a))(exclude, s)) { 104 debug logger.tracef("%s did match one of %s", s, exclude); 105 return false; 106 } 107 108 return true; 109 } 110 } 111 112 /// Example: 113 unittest { 114 import std.algorithm : filter; 115 import std.array : array; 116 117 auto r = GlobFilter(["foo*"], ["*bar*", "*batman"]); 118 119 assert(["foo", "foobar", "foo smurf batman", "batman", "fo", 120 "foo mother"].filter!(a => r.match(a)).array == [ 121 "foo", "foo mother" 122 ]); 123 }