sjsonnet icon indicating copy to clipboard operation
sjsonnet copied to clipboard

Add std.manifestToml

Open markj-db opened this issue 4 years ago • 2 comments

This was recently added to the standard library. I don't frankly know how useful it is.

https://github.com/google/jsonnet/pull/866

This is an incompatibility issue as tracked by #73

markj-db avatar Feb 09 '21 16:02 markj-db

@coderplay hit a need for this when interfacing with TiDB, which uses TOML for config. For now, we just vendored the implementation from the upstream std.jsonnet (below) and it seems to work, but we should pick this up in the next maintenance pass and port it to Scala for improved performance and consistency with the rest of Std.scala

{
 manifestToml(value):: self.manifestTomlEx(value, '  '),
 all(arr)::
    assert std.isArray(arr) : 'all() parameter should be an array, got ' + std.type(arr);
    local arrLen = std.length(arr);
    local aux(idx) =
      if idx >= arrLen then
        true
      else
        local e = arr[idx];
        assert std.isBoolean(e) : std.format('element "%s" of type %s is not a boolean', e, std.type(e));
        if !e then
          false
        else
          aux(idx + 1);
    aux(0),
  manifestTomlEx(value, indent)::
    local
      escapeStringToml = std.escapeStringJson,
      escapeKeyToml(key) =
        local bare_allowed = std.set(std.stringChars('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-'));
        if std.setUnion(std.set(std.stringChars(key)), bare_allowed) == bare_allowed then key else escapeStringToml(key),
      isTableArray(v) = std.isArray(v) && std.length(v) > 0 && self.all(std.map(std.isObject, v)),
      isSection(v) = std.isObject(v) || isTableArray(v),
      renderValue(v, indexedPath, inline, cindent) =
        if v == true then
          'true'
        else if v == false then
          'false'
        else if v == null then
          error 'Tried to manifest "null" at ' + indexedPath
        else if std.isNumber(v) then
          '' + v
        else if std.isString(v) then
          escapeStringToml(v)
        else if std.isFunction(v) then
          error 'Tried to manifest function at ' + indexedPath
        else if std.isArray(v) then
          if std.length(v) == 0 then
            '[]'
          else
            local range = std.range(0, std.length(v) - 1);
            local new_indent = if inline then '' else cindent + indent;
            local separator = if inline then ' ' else '\n';
            local lines = ['[' + separator]
                          + std.join([',' + separator],
                                     [
                                       [new_indent + renderValue(v[i], indexedPath + [i], true, '')]
                                       for i in range
                                     ])
                          + [separator + (if inline then '' else cindent) + ']'];
            std.join('', lines)
        else if std.isObject(v) then
          local lines = ['{ ']
                        + std.join([', '],
                                   [
                                     [escapeKeyToml(k) + ' = ' + renderValue(v[k], indexedPath + [k], true, '')]
                                     for k in std.objectFields(v)
                                   ])
                        + [' }'];
          std.join('', lines),
      renderTableInternal(v, path, indexedPath, cindent) =
        local kvp = std.flattenArrays([
          [cindent + escapeKeyToml(k) + ' = ' + renderValue(v[k], indexedPath + [k], false, cindent)]
          for k in std.objectFields(v)
          if !isSection(v[k])
        ]);
        local sections = [std.join('\n', kvp)] + [
          (
            if std.isObject(v[k]) then
              renderTable(v[k], path + [k], indexedPath + [k], cindent)
            else
              renderTableArray(v[k], path + [k], indexedPath + [k], cindent)
          )
          for k in std.objectFields(v)
          if isSection(v[k])
        ];
        std.join('\n\n', sections),
      renderTable(v, path, indexedPath, cindent) =
        cindent + '[' + std.join('.', std.map(escapeKeyToml, path)) + ']'
        + (if v == {} then '' else '\n')
        + renderTableInternal(v, path, indexedPath, cindent + indent),
      renderTableArray(v, path, indexedPath, cindent) =
        local range = std.range(0, std.length(v) - 1);
        local sections = [
          (cindent + '[[' + std.join('.', std.map(escapeKeyToml, path)) + ']]'
           + (if v[i] == {} then '' else '\n')
           + renderTableInternal(v[i], path, indexedPath + [i], cindent + indent))
          for i in range
        ];
        std.join('\n\n', sections);
    if std.isObject(value) then
      renderTableInternal(value, [], [], '')
    else
      error 'TOML body must be an object. Got ' + std.type(value),

}.manifestTomlEx({ key1: "value", key2: 1, section: { a: 1, b: "str", c: false, d: [1, "s", [2, 3]], subsection: { k: "v", }, }, sectionArray: [ { k: "v1", v: 123 }, { k: "v2", c: "value2" }, ], }, " ")
~/universe$ bin/sjsonnet foo.jsonnet

"key1 = \"value\"\nkey2 = 1\n\n[section]\n a = 1\n b = \"str\"\n c = false\n d = [\n  1,\n  \"s\",\n  [ 2, 3 ]\n ]\n\n [section.subsection]\n  k = \"v\"\n\n[[sectionArray]]\n k = \"v1\"\n v = 123\n\n[[sectionArray]]\n c = \"value2\"\n k = \"v2\""

lihaoyi-databricks avatar Aug 03 '23 05:08 lihaoyi-databricks

Thanks a lot Haoyi, for reviewing this feature! Looking forward to the scala implementation.

coderplay avatar Aug 03 '23 05:08 coderplay