mirror of
https://github.com/NixOS/nix.git
synced 2025-11-11 21:16:02 +01:00
Merge pull request #13883 from xokdvium/toml-timestamps-reapply
Reapply "Merge pull request #13741 from xokdvium/toml-timestamps"
This commit is contained in:
commit
fea4a29c0a
9 changed files with 186 additions and 25 deletions
|
|
@ -71,6 +71,12 @@ toml11 = dependency(
|
|||
method : 'cmake',
|
||||
include_type : 'system',
|
||||
)
|
||||
|
||||
configdata_priv.set(
|
||||
'HAVE_TOML11_4',
|
||||
toml11.version().version_compare('>= 4.0.0').to_int(),
|
||||
)
|
||||
|
||||
deps_other += toml11
|
||||
|
||||
config_priv_h = configure_file(
|
||||
|
|
|
|||
|
|
@ -1,73 +1,140 @@
|
|||
#include "nix/expr/primops.hh"
|
||||
#include "nix/expr/eval-inline.hh"
|
||||
|
||||
#include "expr-config-private.hh"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include <toml.hpp>
|
||||
|
||||
namespace nix {
|
||||
|
||||
#if HAVE_TOML11_4
|
||||
|
||||
/**
|
||||
* This is what toml11 < 4.0 did when choosing the subsecond precision.
|
||||
* TOML 1.0.0 spec doesn't define how sub-millisecond ranges should be handled and calls it
|
||||
* implementation defined behavior. For a lack of a better choice we stick with what older versions
|
||||
* of toml11 did [1].
|
||||
*
|
||||
* [1]: https://github.com/ToruNiina/toml11/blob/dcfe39a783a94e8d52c885e5883a6fbb21529019/toml/datetime.hpp#L282
|
||||
*/
|
||||
static size_t normalizeSubsecondPrecision(toml::local_time lt)
|
||||
{
|
||||
auto millis = lt.millisecond;
|
||||
auto micros = lt.microsecond;
|
||||
auto nanos = lt.nanosecond;
|
||||
if (millis != 0 || micros != 0 || nanos != 0) {
|
||||
if (micros != 0 || nanos != 0) {
|
||||
if (nanos != 0)
|
||||
return 9;
|
||||
return 6;
|
||||
}
|
||||
return 3;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize date/time formats to serialize to the same strings as versions prior to toml11 4.0.
|
||||
*
|
||||
* Several things to consider:
|
||||
*
|
||||
* 1. Sub-millisecond range is represented the same way as in toml11 versions prior to 4.0. Precisioun is rounded
|
||||
* towards the next multiple of 3 or capped at 9 digits.
|
||||
* 2. Seconds must be specified. This may become optional in (yet unreleased) TOML 1.1.0, but 1.0.0 defined local time
|
||||
* in terms of RFC3339 [1].
|
||||
* 3. date-time separator (`t`, `T` or space ` `) is canonicalized to an upper T. This is compliant with RFC3339
|
||||
* [1] 5.6:
|
||||
* > Applications that generate this format SHOULD use upper case letters.
|
||||
*
|
||||
* [1]: https://datatracker.ietf.org/doc/html/rfc3339#section-5.6
|
||||
*/
|
||||
static void normalizeDatetimeFormat(toml::value & t)
|
||||
{
|
||||
if (t.is_local_datetime()) {
|
||||
auto & ldt = t.as_local_datetime();
|
||||
t.as_local_datetime_fmt() = {
|
||||
.delimiter = toml::datetime_delimiter_kind::upper_T,
|
||||
// https://datatracker.ietf.org/doc/html/rfc3339#section-5.6
|
||||
.has_seconds = true, // Mandated by TOML 1.0.0
|
||||
.subsecond_precision = normalizeSubsecondPrecision(ldt.time),
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
if (t.is_offset_datetime()) {
|
||||
auto & odt = t.as_offset_datetime();
|
||||
t.as_offset_datetime_fmt() = {
|
||||
.delimiter = toml::datetime_delimiter_kind::upper_T,
|
||||
// https://datatracker.ietf.org/doc/html/rfc3339#section-5.6
|
||||
.has_seconds = true, // Mandated by TOML 1.0.0
|
||||
.subsecond_precision = normalizeSubsecondPrecision(odt.time),
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
if (t.is_local_time()) {
|
||||
auto & lt = t.as_local_time();
|
||||
t.as_local_time_fmt() = {
|
||||
.has_seconds = true, // Mandated by TOML 1.0.0
|
||||
.subsecond_precision = normalizeSubsecondPrecision(lt),
|
||||
};
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static void prim_fromTOML(EvalState & state, const PosIdx pos, Value ** args, Value & val)
|
||||
{
|
||||
auto toml = state.forceStringNoCtx(*args[0], pos, "while evaluating the argument passed to builtins.fromTOML");
|
||||
|
||||
std::istringstream tomlStream(std::string{toml});
|
||||
|
||||
std::function<void(Value &, toml::value)> visit;
|
||||
|
||||
visit = [&](Value & v, toml::value t) {
|
||||
auto visit = [&](auto & self, Value & v, toml::value t) -> void {
|
||||
switch (t.type()) {
|
||||
case toml::value_t::table: {
|
||||
auto table = toml::get<toml::table>(t);
|
||||
|
||||
size_t size = 0;
|
||||
for (auto & i : table) {
|
||||
(void) i;
|
||||
size++;
|
||||
}
|
||||
|
||||
auto attrs = state.buildBindings(size);
|
||||
auto attrs = state.buildBindings(table.size());
|
||||
|
||||
for (auto & elem : table) {
|
||||
forceNoNullByte(elem.first);
|
||||
visit(attrs.alloc(elem.first), elem.second);
|
||||
self(self, attrs.alloc(elem.first), elem.second);
|
||||
}
|
||||
|
||||
v.mkAttrs(attrs);
|
||||
} break;
|
||||
;
|
||||
case toml::value_t::array: {
|
||||
auto array = toml::get<std::vector<toml::value>>(t);
|
||||
|
||||
auto list = state.buildList(array.size());
|
||||
for (const auto & [n, v] : enumerate(list))
|
||||
visit(*(v = state.allocValue()), array[n]);
|
||||
self(self, *(v = state.allocValue()), array[n]);
|
||||
v.mkList(list);
|
||||
} break;
|
||||
;
|
||||
case toml::value_t::boolean:
|
||||
v.mkBool(toml::get<bool>(t));
|
||||
break;
|
||||
;
|
||||
case toml::value_t::integer:
|
||||
v.mkInt(toml::get<int64_t>(t));
|
||||
break;
|
||||
;
|
||||
case toml::value_t::floating:
|
||||
v.mkFloat(toml::get<NixFloat>(t));
|
||||
break;
|
||||
;
|
||||
case toml::value_t::string: {
|
||||
auto s = toml::get<std::string_view>(t);
|
||||
forceNoNullByte(s);
|
||||
v.mkString(s);
|
||||
} break;
|
||||
;
|
||||
case toml::value_t::local_datetime:
|
||||
case toml::value_t::offset_datetime:
|
||||
case toml::value_t::local_date:
|
||||
case toml::value_t::local_time: {
|
||||
if (experimentalFeatureSettings.isEnabled(Xp::ParseTomlTimestamps)) {
|
||||
#if HAVE_TOML11_4
|
||||
normalizeDatetimeFormat(t);
|
||||
#endif
|
||||
auto attrs = state.buildBindings(2);
|
||||
attrs.alloc("_type").mkString("timestamp");
|
||||
std::ostringstream s;
|
||||
|
|
@ -80,16 +147,24 @@ static void prim_fromTOML(EvalState & state, const PosIdx pos, Value ** args, Va
|
|||
throw std::runtime_error("Dates and times are not supported");
|
||||
}
|
||||
} break;
|
||||
;
|
||||
case toml::value_t::empty:
|
||||
v.mkNull();
|
||||
break;
|
||||
;
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
visit(val, toml::parse(tomlStream, "fromTOML" /* the "filename" */));
|
||||
visit(
|
||||
visit,
|
||||
val,
|
||||
toml::parse(
|
||||
tomlStream,
|
||||
"fromTOML" /* the "filename" */
|
||||
#if HAVE_TOML11_4
|
||||
,
|
||||
toml::spec::v(1, 0, 0) // Be explicit that we are parsing TOML 1.0.0 without extensions
|
||||
#endif
|
||||
));
|
||||
} catch (std::exception & e) { // TODO: toml::syntax_error
|
||||
state.error<EvalError>("while parsing TOML: %s", e.what()).atPos(pos).debugThrow();
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue