Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add std.manifestToml #111

Open
markj-db opened this issue Feb 9, 2021 · 2 comments
Open

Add std.manifestToml #111

markj-db opened this issue Feb 9, 2021 · 2 comments

Comments

@markj-db
Copy link
Contributor

markj-db commented Feb 9, 2021

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

google/jsonnet#866

This is an incompatibility issue as tracked by #73

@lihaoyi-databricks
Copy link
Contributor

lihaoyi-databricks commented Aug 3, 2023

@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\""

@coderplay
Copy link

coderplay commented Aug 3, 2023

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants