1 /*
2  *  Copyright (c) 2016, Facebook, Inc.
3  *  All rights reserved.
4  *
5  *  This source code is licensed under the BSD-style license found in the
6  *  LICENSE file in the root directory of this source tree. An additional grant
7  *  of patent rights can be found in the PATENTS file in the same directory.
8  */
9 
10 #ifndef FATAL_INCLUDE_fatal_time_time_h
11 #define FATAL_INCLUDE_fatal_time_time_h
12 
13 #include <fatal/type/apply.h>
14 #include <fatal/type/array.h>
15 #include <fatal/type/get.h>
16 #include <fatal/type/list.h>
17 #include <fatal/type/sequence.h>
18 #include <fatal/type/sort.h>
19 
20 #include <chrono>
21 #include <ratio>
22 #include <type_traits>
23 #include <utility>
24 
25 namespace fatal {
26 namespace time {
27 
28 /**
29  * TODO: DOCUMENT
30  *
31  * @author: Marcelo Juchem <marcelo@fb.com>
32  */
33 
34 namespace detail {
35 
36 template <typename Ratio, char... Suffix>
37 using t_s = pair<Ratio, char_sequence<Suffix...>>;
38 
39 } // namespace detail {
40 
41 // TODO: MOVE TO MATH
42 using suffixes = list<
43   //detail::t_s<std::yocto, 'y', 's'>,
44   //detail::t_s<std::zepto, 'z', 's'>,
45   detail::t_s<std::atto, 'a', 's'>,
46   detail::t_s<std::femto, 'f', 's'>,
47   detail::t_s<std::pico, 'p', 's'>,
48   detail::t_s<std::nano, 'n', 's'>,
49   detail::t_s<std::micro, 'u', 's'>,
50   detail::t_s<std::milli, 'm', 's'>,
51   detail::t_s<std::centi, 'c', 's'>,
52   detail::t_s<std::deci, 'd', 's'>,
53   detail::t_s<std::chrono::seconds::period, 's'>,
54   detail::t_s<std::deca, 'd', 'a', 's'>,
55   detail::t_s<std::chrono::minutes::period, 'm', 'i', 'n'>,
56   detail::t_s<std::hecto, 'h', 's'>,
57   detail::t_s<std::kilo, 'k', 's'>,
58   detail::t_s<std::chrono::hours::period, 'h'>,
59   detail::t_s<
60     std::ratio_multiply<std::chrono::hours::period, std::ratio<24, 1>>, 'd'
61   >,
62   detail::t_s<
63     std::ratio_multiply<std::chrono::hours::period, std::ratio<24 * 7, 1>>,
64     'w', 'k'
65   >,
66   detail::t_s<std::mega, 'M', 's'>,
67   detail::t_s<std::giga, 'G', 's'>,
68   detail::t_s<std::tera, 'T', 's'>,
69   detail::t_s<std::peta, 'P', 's'>,
70   detail::t_s<std::exa, 'E', 's'>
71   //detail::t_s<std::zetta, 'Z', 's'>,
72   //detail::t_s<std::yotta, 'Y', 's'>
73 >;
74 
75 // TODO: DOCUMENT AND TEST
76 template <typename TPeriod>
77 using suffix_t = second<get<suffixes, TPeriod>>;
78 
79 // TODO: DOCUMENT AND TEST
80 template <typename T>
suffix()81 inline char const *suffix() {
82   return z_data<suffix_t<typename T::period>>();
83 }
84 
85 // TODO: DOCUMENT AND TEST
86 template <typename T>
suffix(T const &)87 inline char const *suffix(T const &) {
88   return suffix<T>();
89 }
90 
91 namespace detail {
92 
93 template <typename...> struct pretty;
94 
95 template <typename T, typename... Args>
96 struct pretty<T, Args...> {
97   template <typename Out, typename P, typename R>
98   static void print(Out &out, std::chrono::duration<R, P> time) {
99     auto const local = std::chrono::duration_cast<
100       std::chrono::duration<R, T>
101     >(time);
102 
103     auto const remaining = time - local;
104 
105     if (local.count()) {
106       out << local.count() << z_data<suffix_t<T>>();
107 
108       if (sizeof...(Args) && remaining.count()) {
109         out << ' ';
110       }
111     }
112 
113     pretty<Args...>::print(out, remaining);
114   }
115 };
116 
117 template <>
118 struct pretty<> {
119   template <typename Out, typename D>
120   static void print(Out &, D) {}
121 };
122 
123 using pretty_print_ratios = list<
124   std::ratio_multiply<std::chrono::hours::period, std::ratio<24 * 7, 1>>,
125   std::ratio_multiply<std::chrono::hours::period, std::ratio<24, 1>>,
126   std::chrono::hours::period,
127   std::chrono::minutes::period,
128   std::chrono::seconds::period,
129   std::chrono::milliseconds::period,
130   std::chrono::microseconds::period,
131   std::chrono::nanoseconds::period
132 >;
133 
134 } // namespace detail {
135 
136 template <typename Out, typename R, typename P>
137 Out &&pretty_print(Out &&out, std::chrono::duration<R, P> time) {
138   using ratios = reject<
139     typename detail::pretty_print_ratios, curry<applier<std::ratio_greater>, P>
140   >;
141   static_assert(!empty<ratios>::value, "unsupported duration");
142 
143   using impl = apply_to<ratios, detail::pretty>;
144   impl::print(out, time);
145 
146   return std::forward<Out>(out);
147 }
148 
149 } // namespace time {
150 } // namespace fatal {
151 
152 #endif // FATAL_INCLUDE_fatal_time_time_h
153