1 /** 2 * This module contains some utils usefull for cron parser 3 * 4 * Copyright: 5 * Copyright (c) 2018, Maxim Tyapkin. 6 * Authors: 7 * Maxim Tyapkin 8 * License: 9 * This software is licensed under the terms of the BSD 3-clause license. 10 * The full terms of the license can be found in the LICENSE.md file. 11 */ 12 13 module cronexp.utils; 14 15 private 16 { 17 import std.algorithm : each, map; 18 import std.array : array, split; 19 import std.conv : to; 20 import std.datetime; 21 import std.range : iota; 22 import std.traits : isSomeString; 23 import std.typecons : tuple; 24 } 25 26 27 /** 28 * Parse list of indexes to var (eg. "1,2,3,5,8") 29 */ 30 void parseList(T, R)(ref T val, R expr) 31 if (isSomeString!R) 32 { 33 val = 0; 34 expr.split(",") 35 .map!(to!ubyte) 36 .array 37 .each!(n => bitSet!T(val, n)); 38 } 39 40 41 unittest 42 { 43 ushort x; 44 parseList(x, "1,2,4,5,8"); 45 assert(x == cast(ushort)0b100110110); 46 } 47 48 49 /** 50 * Parse range of indexes to var (eg. "2-4" or "5-1") 51 * 52 * Note: 53 * If the range "a-b" is reversed (a > b), it's mean 54 * two ranges: "a-until", "from-b" 55 */ 56 void parseRange(T, R)(ref T val, R expr, ubyte from, ubyte until) 57 if (isSomeString!R) 58 { 59 val = 0; 60 auto rngs = expr.split("-") 61 .map!(to!ubyte) 62 .array; 63 if (rngs.length < 2) 64 return; 65 66 if (rngs[0] < rngs[1]) 67 { 68 iota(rngs[0], rngs[1] + 1) 69 .each!(n => bitSet!T(val, cast(ubyte)n)); 70 } 71 else 72 { 73 iota(rngs[0], until + 1) 74 .each!(n => bitSet!T(val, cast(ubyte)n)); 75 iota(from, rngs[1] + 1) 76 .each!(n => bitSet!T(val, cast(ubyte)n)); 77 } 78 } 79 80 81 unittest 82 { 83 ushort x; 84 parseRange(x, "4-7", 1, 10); 85 assert(x == cast(ushort)0b_0000_0000_1111_0000); 86 parseRange(x, "7-4", 1, 10); 87 assert(x == cast(ushort)0b_0000_0111_1001_1110); 88 } 89 90 91 /** 92 * Parse sequence of indexes to var (eg. "a/x" == "a, a+x, a+2x, ...") 93 * 94 * Note: "*" is synonym for "from" 95 */ 96 void parseSequence(T, R)(ref T val, R expr, ubyte from, ubyte until) 97 if (isSomeString!R) 98 { 99 val = 0; 100 auto rngs = expr.split("/") 101 .map!(n => n == "*" ? from : n.to!ubyte) 102 .array; 103 if(rngs.length < 2) 104 return; 105 106 iota(rngs[0], until + 1, rngs[1]) 107 .each!(n => bitSet!T(val, cast(ubyte)n)); 108 } 109 110 111 unittest 112 { 113 ushort x; 114 parseSequence(x, "1/3", 1, 10); 115 assert(x == cast(ushort)0b_0000_0100_1001_0010); 116 parseSequence(x, "*/2", 0, 15); 117 assert(x == cast(ushort)0b_0101_0101_0101_0101); 118 parseSequence(x, "2/10", 0, 7); 119 assert(x == cast(ushort)0b_0000_0000_0000_0100); 120 } 121 122 123 /** 124 * Fill all allowed indexes to var 125 */ 126 void parseAny(T)(ref T val, ubyte from, ubyte until) 127 { 128 val = 0; 129 iota(from, until + 1) 130 .each!(n => bitSet!T(val, cast(ubyte)n)); 131 } 132 133 134 unittest 135 { 136 ushort x; 137 parseAny(x, 1, 10); 138 assert(x == cast(ushort)0b_0000_0111_1111_1110); 139 } 140 141 142 /** 143 * Get idx-th bit in var 144 */ 145 bool bitTest(T)(T num, ubyte idx) 146 { 147 return (num & (cast(T)1 << idx)) != 0; 148 } 149 150 151 /** 152 * Set idx-th bit in var to 1 153 */ 154 void bitSet(T)(ref T num, ubyte idx) 155 { 156 num = cast(T)(num | (cast(T)1 << idx)); 157 } 158 159 160 /** 161 * Set idx-th bit in var to 0 162 */ 163 void bitClear(T)(ref T num, ubyte idx) 164 { 165 num = cast(T)(num & (~(cast(T)1 << idx))); 166 } 167 168 169 unittest 170 { 171 ulong num = 0x1_00_00; 172 assert(bitTest(num, 16)); 173 bitClear(num, 16); 174 assert(!bitTest(num, 16)); 175 bitSet(num, 63); 176 assert(num); 177 } 178 179 180 /** 181 * Convert std.datetime.DayOfWeek enum to dow index 182 */ 183 ubyte dow(DateTime dt) 184 { 185 final switch (dt.dayOfWeek) with (DayOfWeek) 186 { 187 case mon: return 1; 188 case tue: return 2; 189 case wed: return 3; 190 case thu: return 4; 191 case fri: return 5; 192 case sat: return 6; 193 case sun: return 7; 194 } 195 } 196 197 198 unittest 199 { 200 //3 JAN 2000 - monday 201 assert(DateTime(2000, 1, 3).dow == 1); 202 assert(DateTime(2000, 1, 4).dow == 2); 203 assert(DateTime(2000, 1, 5).dow == 3); 204 assert(DateTime(2000, 1, 6).dow == 4); 205 assert(DateTime(2000, 1, 7).dow == 5); 206 assert(DateTime(2000, 1, 8).dow == 6); 207 assert(DateTime(2000, 1, 9).dow == 7); 208 }