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.parse;
7 
8 import core.time : Duration, dur;
9 
10 class TimeParseException : Exception {
11     this(string msg) @safe {
12         super(msg);
13     }
14 }
15 
16 /** Parse a string as a duration.
17  *
18  * Example:
19  * ---
20  * auto d = parseDuration("1 hours 2 minutes");
21  * ---
22  *
23  * Params:
24  *  timeSpec = string to parse
25  *
26  * Returns: a `Duration`
27  *
28  * Throws: `TimeParseException` if unable to parse the string.
29  */
30 Duration parseDuration(string timeSpec) @safe {
31     import std.conv : to;
32     import std.format : format;
33     import std.range : chunks;
34     import std.string : split;
35 
36     Duration d;
37     const parts = timeSpec.split;
38 
39     if (parts.length % 2 != 0) {
40         throw new TimeParseException("Invalid time specification. The format is: value unit");
41     }
42 
43     foreach (const p; parts.chunks(2)) {
44         const nr = p[0].to!long;
45         bool validUnit;
46         immutable Units = [
47             "msecs", "seconds", "minutes", "hours", "days", "weeks"
48         ];
49         static foreach (Unit; Units) {
50             if (p[1] == Unit) {
51                 d += nr.dur!Unit;
52                 validUnit = true;
53             }
54         }
55         if (!validUnit) {
56             throw new Exception(format!"Invalid unit '%s'. Valid are %-(%s, %)."(p[1], Units));
57         }
58     }
59 
60     return d;
61 }
62 
63 @("shall parse a string to a duration")
64 unittest {
65     const expected = 1.dur!"weeks" + 1.dur!"days" + 3.dur!"hours"
66         + 2.dur!"minutes" + 5.dur!"seconds" + 9.dur!"msecs";
67     const d = parseDuration("1 weeks 1 days 3 hours 2 minutes 5 seconds 9 msecs");
68     assert(d == expected);
69 }