1 /++
2 This module only contains a template mixin used to generate
3 SI units, prefixes and utility functions usable at compile 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.compiletime.si.definitions;
11 
12 /++
13 Generates SI units, prefixes and several utility functions
14 (parsing and formatting) usable at compile time.
15 +/
16 mixin template CompiletimeSI(N)
17 {
18     enum siSymbols = siSymbolList;
19 
20     /// Predefined quantity type templates for SI quantities
21     alias Dimensionless = typeof(one);
22     alias Length = typeof(meter);
23     alias Mass = typeof(kilogram); /// ditto
24     alias Time = typeof(second); /// ditto
25     alias ElectricCurrent = typeof(ampere); /// ditto
26     alias Temperature = typeof(kelvin); /// ditto
27     alias AmountOfSubstance = typeof(mole); /// ditto
28     alias LuminousIntensity = typeof(candela); /// ditto
29 
30     alias Area = typeof(square(meter)); /// ditto
31     alias Surface = Area;
32     alias Volume = typeof(cubic(meter)); /// ditto
33     alias Speed = typeof(meter/second); /// ditto
34     alias Acceleration = typeof(meter/square(second)); /// ditto
35     alias MassDensity = typeof(kilogram/cubic(meter)); /// ditto
36     alias CurrentDensity = typeof(ampere/square(meter)); /// ditto
37     alias MagneticFieldStrength = typeof(ampere/meter); /// ditto
38     alias Concentration = typeof(mole/cubic(meter)); /// ditto
39     alias MolarConcentration = Concentration; /// ditto
40     alias MassicConcentration = typeof(kilogram/cubic(meter)); /// ditto
41     alias Luminance = typeof(candela/square(meter)); /// ditto
42     alias RefractiveIndex = typeof(kilogram); /// ditto
43 
44     alias Angle = typeof(radian); /// ditto
45     alias SolidAngle = typeof(steradian); /// ditto
46     alias Frequency = typeof(hertz); /// ditto
47     alias Force = typeof(newton); /// ditto
48     alias Pressure = typeof(pascal); /// ditto
49     alias Energy = typeof(joule); /// ditto
50     alias Work = Energy; /// ditto
51     alias Heat = Energy; /// ditto
52     alias Power = typeof(watt); /// ditto
53     alias ElectricCharge = typeof(coulomb); /// ditto
54     alias ElectricPotential = typeof(volt); /// ditto
55     alias Capacitance = typeof(farad); /// ditto
56     alias ElectricResistance = typeof(ohm); /// ditto
57     alias ElectricConductance = typeof(siemens); /// ditto
58     alias MagneticFlux = typeof(weber); /// ditto
59     alias MagneticFluxDensity = typeof(tesla); /// ditto
60     alias Inductance = typeof(henry); /// ditto
61     alias LuminousFlux = typeof(lumen); /// ditto
62     alias Illuminance = typeof(lux); /// ditto
63     alias CelsiusTemperature = typeof(celsius); /// ditto
64     alias Radioactivity = typeof(becquerel); /// ditto
65     alias AbsorbedDose = typeof(gray); /// ditto
66     alias DoseEquivalent = typeof(sievert); /// ditto
67     alias CatalyticActivity = typeof(katal); /// ditto
68 
69     import std.traits : isSomeString;
70 
71     /++
72     Parses a string for a quantity of type Q at run time.
73 
74     Params:
75         Q = the type of the returned quantity.
76         str = the string to parse.
77     +/
78     Q parseSI(Q, S)(S str)
79             if (isSomeString!S)
80     {
81         import quantities.runtime.parsing : Parser;
82         import std.conv : parse;
83 
84         enum siParser = Parser!(N, (ref S s) => parse!N(s))(siSymbols);
85         return Q(siParser.parse(str));
86     }
87     ///
88     unittest
89     {
90         alias Time = typeof(second);
91         Time t = parseSI!Time("90 min");
92         assert(t == 90 * minute);
93         t = parseSI!Time("h");
94         assert(t == 1 * hour);
95     }
96 
97     /++
98     Creates a Quantity from a string at compile-time.
99     +/
100     template si(string str)
101     {
102         import quantities.runtime.qvariant;
103         import quantities.runtime.parsing : Parser;
104         import std.conv : parse;
105 
106         enum siParser = Parser!(N, (ref string s) => parse!N(s))(siSymbols);
107         enum qty = siParser.parse(str);
108         enum spec = QVariant!N(1, qty.dimensions);
109         enum si = Quantity!(N, spec)(qty);
110     }
111     ///
112     unittest
113     {
114         alias Time = typeof(second);
115         enum t = si!"90 min";
116         static assert(is(typeof(t) == Time));
117         static assert(si!"h" == 60 * 60 * second);
118     }
119 
120     /++
121     Formats a SI quantity according to a format string known at compile time.
122     Params:
123         fmt = The format string. Must start with a format specification
124                 for the value of the quantity (a numeric type), that must be 
125                 followed by the symbol of a SI unit.
126         quantity = The quantity that must be formatted.
127     +/
128     auto siFormat(string fmt, Q)(Q quantity)
129     {
130         return SIFormatter!(fmt, Q)(quantity);
131     }
132     ///
133     unittest
134     {
135         import std.conv : text;
136 
137         enum speed = 12.5 * kilo(meter) / hour;
138         assert(siFormat!"%.2f m/s"(speed).text == "3.47 m/s");
139     }
140 
141     /// Ditto
142     struct SIFormatter(string fmt, Q)
143     {
144         private Q quantity;
145 
146         enum unit = {
147             import std.format : FormatSpec;
148             import std.array : Appender;
149 
150             auto spec = FormatSpec!char(fmt);
151             auto app = Appender!string();
152             spec.writeUpToNextSpec(app);
153             return parseSI!Q(spec.trailing);
154         }();
155 
156         /++
157         Create a formatter struct.
158 
159         Params:
160             format = The format string. Must start with a format specification
161                 for the value of the quantity (a numeric type), that must be 
162                 followed by the symbol of a SI unit.
163             quantity = The quantity that must be formatted.
164         +/
165         this(Q quantity)
166         {
167             this.quantity = quantity;
168         }
169 
170         ///
171         void toString(scope void delegate(const(char)[]) sink) const
172         {
173             import std.format : formattedWrite;
174 
175             sink.formattedWrite!fmt(quantity.value(unit));
176         }
177     }
178 }