1 /++
2 This module only contains a template mixin used to generate
3 SI units, prefixes and utility functions usable at run time.
4 
5 Copyright: Copyright 2013-2018, Nicolas Sicard
6 Authors: Nicolas Sicard
7 License: $(LINK www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
8 Source: $(LINK https://github.com/biozic/quantities)
9 +/
10 module quantities.runtime.si.definitions;
11 
12 /++
13 Generates SI units, prefixes and several utility functions
14 (parsing and formatting) usable at run time.
15 +/
16 mixin template RuntimeSI(N)
17 {
18     import std.traits : isSomeString;
19 
20     /// A list of common SI symbols and prefixes
21     static __gshared SymbolList!N siSymbols;
22     shared static this()
23     {
24         siSymbols = siSymbolList;
25     }
26 
27     import quantities.runtime.qvariant;
28     import std.traits : isSomeString;
29 
30     /++
31     Parses a string for a quantity of type Q at run time.
32 
33     Throws a DimensionException.
34 
35     Params:
36         str = the string to parse.
37     +/
38     QVariant!N parseSI(S)(S str)
39             if (isSomeString!S)
40     {
41         import quantities.runtime.parsing : Parser;
42         import std.conv : parse;
43 
44         static Parser!(N, (ref S s) => parse!N(s)) siParser;
45         static bool initialized = false;
46         if (!initialized)
47             siParser.symbolList = siSymbols;
48         return siParser.parse(str);
49     }
50     ///
51     unittest
52     {
53         auto t = parseSI("90 min");
54         assert(t == 90 * minute);
55         t = parseSI("h");
56         assert(t == 1 * hour);
57 
58         auto v = parseSI("2");
59         assert(v == (2 * meter) / meter);
60     }
61 
62     /++
63     Format a SI quantity according to a format string known at run time.
64 
65     Params:
66         format = The format string. Must start with a format specification
67               for the value of the quantity (a numeric type), that must be 
68               followed by the symbol of a SI unit.
69         quantity = The quantity that must be formatted.
70     +/
71     SIFormatter siFormat(string format, QVariant!N quantity)
72     {
73         return SIFormatter(format, quantity);
74     }
75     ///
76     unittest
77     {
78         import std.conv : text;
79 
80         QVariant!double speed = 12.5 * kilo(meter) / hour;
81         assert(siFormat("%.2f m/s", speed).text == "3.47 m/s");
82     }
83 
84     /// Ditto
85     struct SIFormatter
86     {
87         private
88         {
89             string fmt;
90             QVariant!N unit;
91             QVariant!N quantity;
92         }
93 
94         /++
95         Create a formatter struct.
96 
97         Params:
98             format = The format string. Must start with a format specification
99                 for the value of the quantity (a numeric type), that must be 
100                 followed by the symbol of a SI unit.
101             quantity = The quantity that must be formatted.
102         +/
103         this(string format, QVariant!N quantity)
104         {
105             import std.format : FormatSpec;
106             import std.array : Appender;
107 
108             fmt = format;
109             auto spec = FormatSpec!char(format);
110             auto app = Appender!string();
111             spec.writeUpToNextSpec(app);
112             unit = parseSI(spec.trailing);
113             this.quantity = quantity;
114         }
115 
116         ///
117         void toString(scope void delegate(const(char)[]) sink) const
118         {
119             import std.format : formattedWrite;
120 
121             sink.formattedWrite(fmt, quantity.value(unit));
122         }
123     }
124 }