11#include " nix/expr/primops.hh"
22#include " nix/expr/eval-inline.hh"
33
4- #include " expr-config-private.hh"
5-
64#include < sstream>
75
86#include < toml.hpp>
97
108namespace nix {
119
12- #if HAVE_TOML11_4
13-
14- /* *
15- * This is what toml11 < 4.0 did when choosing the subsecond precision.
16- * TOML 1.0.0 spec doesn't define how sub-millisecond ranges should be handled and calls it
17- * implementation defined behavior. For a lack of a better choice we stick with what older versions
18- * of toml11 did [1].
19- *
20- * [1]: https://github.com/ToruNiina/toml11/blob/dcfe39a783a94e8d52c885e5883a6fbb21529019/toml/datetime.hpp#L282
21- */
22- static size_t normalizeSubsecondPrecision (toml::local_time lt)
23- {
24- auto millis = lt.millisecond ;
25- auto micros = lt.microsecond ;
26- auto nanos = lt.nanosecond ;
27- if (millis != 0 || micros != 0 || nanos != 0 ) {
28- if (micros != 0 || nanos != 0 ) {
29- if (nanos != 0 )
30- return 9 ;
31- return 6 ;
32- }
33- return 3 ;
34- }
35- return 0 ;
36- }
37-
38- /* *
39- * Normalize date/time formats to serialize to the same strings as versions prior to toml11 4.0.
40- *
41- * Several things to consider:
42- *
43- * 1. Sub-millisecond range is represented the same way as in toml11 versions prior to 4.0. Precisioun is rounded
44- * towards the next multiple of 3 or capped at 9 digits.
45- * 2. Seconds must be specified. This may become optional in (yet unreleased) TOML 1.1.0, but 1.0.0 defined local time
46- * in terms of RFC3339 [1].
47- * 3. date-time separator (`t`, `T` or space ` `) is canonicalized to an upper T. This is compliant with RFC3339
48- * [1] 5.6:
49- * > Applications that generate this format SHOULD use upper case letters.
50- *
51- * [1]: https://datatracker.ietf.org/doc/html/rfc3339#section-5.6
52- */
53- static void normalizeDatetimeFormat (toml::value & t)
54- {
55- if (t.is_local_datetime ()) {
56- auto & ldt = t.as_local_datetime ();
57- t.as_local_datetime_fmt () = {
58- .delimiter = toml::datetime_delimiter_kind::upper_T,
59- // https://datatracker.ietf.org/doc/html/rfc3339#section-5.6
60- .has_seconds = true , // Mandated by TOML 1.0.0
61- .subsecond_precision = normalizeSubsecondPrecision (ldt.time ),
62- };
63- return ;
64- }
65-
66- if (t.is_offset_datetime ()) {
67- auto & odt = t.as_offset_datetime ();
68- t.as_offset_datetime_fmt () = {
69- .delimiter = toml::datetime_delimiter_kind::upper_T,
70- // https://datatracker.ietf.org/doc/html/rfc3339#section-5.6
71- .has_seconds = true , // Mandated by TOML 1.0.0
72- .subsecond_precision = normalizeSubsecondPrecision (odt.time ),
73- };
74- return ;
75- }
76-
77- if (t.is_local_time ()) {
78- auto & lt = t.as_local_time ();
79- t.as_local_time_fmt () = {
80- .has_seconds = true , // Mandated by TOML 1.0.0
81- .subsecond_precision = normalizeSubsecondPrecision (lt),
82- };
83- return ;
84- }
85- }
86-
87- #endif
88-
8910static void prim_fromTOML (EvalState & state, const PosIdx pos, Value ** args, Value & val)
9011{
9112 auto toml = state.forceStringNoCtx (*args[0 ], pos, " while evaluating the argument passed to builtins.fromTOML" );
9213
9314 std::istringstream tomlStream (std::string{toml});
9415
95- auto visit = [&](auto & self, Value & v, toml::value t) -> void {
16+ std::function<void (Value &, toml::value)> visit;
17+
18+ visit = [&](Value & v, toml::value t) {
9619 switch (t.type ()) {
9720 case toml::value_t ::table: {
9821 auto table = toml::get<toml::table>(t);
99- auto attrs = state.buildBindings (table.size ());
22+
23+ size_t size = 0 ;
24+ for (auto & i : table) {
25+ (void ) i;
26+ size++;
27+ }
28+
29+ auto attrs = state.buildBindings (size);
10030
10131 for (auto & elem : table) {
10232 forceNoNullByte (elem.first );
103- self (self, attrs.alloc (elem.first ), elem.second );
33+ visit ( attrs.alloc (elem.first ), elem.second );
10434 }
10535
10636 v.mkAttrs (attrs);
10737 } break ;
38+ ;
10839 case toml::value_t ::array: {
10940 auto array = toml::get<std::vector<toml::value>>(t);
11041
11142 auto list = state.buildList (array.size ());
11243 for (const auto & [n, v] : enumerate(list))
113- self (self, *(v = state.allocValue ()), array[n]);
44+ visit ( *(v = state.allocValue ()), array[n]);
11445 v.mkList (list);
11546 } break ;
47+ ;
11648 case toml::value_t ::boolean:
11749 v.mkBool (toml::get<bool >(t));
11850 break ;
51+ ;
11952 case toml::value_t ::integer:
12053 v.mkInt (toml::get<int64_t >(t));
12154 break ;
55+ ;
12256 case toml::value_t ::floating:
12357 v.mkFloat (toml::get<NixFloat>(t));
12458 break ;
59+ ;
12560 case toml::value_t ::string: {
12661 auto s = toml::get<std::string_view>(t);
12762 forceNoNullByte (s);
12863 v.mkString (s);
12964 } break ;
65+ ;
13066 case toml::value_t ::local_datetime:
13167 case toml::value_t ::offset_datetime:
13268 case toml::value_t ::local_date:
13369 case toml::value_t ::local_time: {
13470 if (experimentalFeatureSettings.isEnabled (Xp::ParseTomlTimestamps)) {
135- #if HAVE_TOML11_4
136- normalizeDatetimeFormat (t);
137- #endif
13871 auto attrs = state.buildBindings (2 );
13972 attrs.alloc (" _type" ).mkString (" timestamp" );
14073 std::ostringstream s;
@@ -147,24 +80,16 @@ static void prim_fromTOML(EvalState & state, const PosIdx pos, Value ** args, Va
14780 throw std::runtime_error (" Dates and times are not supported" );
14881 }
14982 } break ;
83+ ;
15084 case toml::value_t ::empty:
15185 v.mkNull ();
15286 break ;
87+ ;
15388 }
15489 };
15590
15691 try {
157- visit (
158- visit,
159- val,
160- toml::parse (
161- tomlStream,
162- " fromTOML" /* the "filename" */
163- #if HAVE_TOML11_4
164- ,
165- toml::spec::v (1 , 0 , 0 ) // Be explicit that we are parsing TOML 1.0.0 without extensions
166- #endif
167- ));
92+ visit (val, toml::parse (tomlStream, " fromTOML" /* the "filename" */ ));
16893 } catch (std::exception & e) { // TODO: toml::syntax_error
16994 state.error <EvalError>(" while parsing TOML: %s" , e.what ()).atPos (pos).debugThrow ();
17095 }
0 commit comments