mirror of
https://github.com/NixOS/nix.git
synced 2025-12-08 01:51:01 +01:00
In b70d22b `mkStringNoCopy()` was renamed to
`mkString()`, but this is a bit risky since in code like
vStringRegular.mkString("regular");
we want to be sure that the right overload is picked. (This is
especially problematic since the overload that takes an
`std::string_view` *does* allocate.) So let's be explicit.
(Rebased from https://github.com/NixOS/nix/pull/11551)
192 lines
6 KiB
C++
192 lines
6 KiB
C++
#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});
|
|
|
|
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);
|
|
auto attrs = state.buildBindings(table.size());
|
|
|
|
for (auto & elem : table) {
|
|
forceNoNullByte(elem.first);
|
|
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))
|
|
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").mkStringNoCopy("timestamp");
|
|
std::ostringstream s;
|
|
s << t;
|
|
auto str = toView(s);
|
|
forceNoNullByte(str);
|
|
attrs.alloc("value").mkString(str);
|
|
v.mkAttrs(attrs);
|
|
} else {
|
|
throw std::runtime_error("Dates and times are not supported");
|
|
}
|
|
} break;
|
|
case toml::value_t::empty:
|
|
v.mkNull();
|
|
break;
|
|
}
|
|
};
|
|
|
|
try {
|
|
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();
|
|
}
|
|
}
|
|
|
|
static RegisterPrimOp primop_fromTOML(
|
|
{.name = "fromTOML",
|
|
.args = {"e"},
|
|
.doc = R"(
|
|
Convert a TOML string to a Nix value. For example,
|
|
|
|
```nix
|
|
builtins.fromTOML ''
|
|
x=1
|
|
s="a"
|
|
[table]
|
|
y=2
|
|
''
|
|
```
|
|
|
|
returns the value `{ s = "a"; table = { y = 2; }; x = 1; }`.
|
|
)",
|
|
.fun = prim_fromTOML});
|
|
|
|
} // namespace nix
|