Skip to content

Instantly share code, notes, and snippets.

@spencerpogo
Last active June 10, 2024 06:42
Show Gist options
  • Save spencerpogo/0538252ed4b82d65e59115075369d34d to your computer and use it in GitHub Desktop.
Save spencerpogo/0538252ed4b82d65e59115075369d34d to your computer and use it in GitHub Desktop.
Converts JSON objects into nix (hackishly).
"""Converts JSON objects into nix (hackishly)."""
import sys
import json
INDENT = " " * 2
def strip_comments(t):
# fixme: doesn't work if JSON strings contain //
return "\n".join(l.partition("//")[0] for l in t.split("\n"))
def indent(s):
return "\n".join(INDENT + i for i in s.split("\n"))
def nix_stringify(s):
# fixme: this doesn't handle string interpolation and possibly has more bugs
return json.dumps(s)
def sanitize_key(s):
if s and s.isalnum() and not s[0].isdigit():
return s
return nix_stringify(s)
def flatten_obj_item(k, v):
keys = [k]
val = v
while isinstance(val, dict) and len(val) == 1:
k = next(iter(val.keys()))
keys.append(k)
val = val[k]
return keys, val
def fmt_object(obj, flatten):
fields = []
for k, v in obj.items():
if flatten:
keys, val = flatten_obj_item(k, v)
formatted_key = ".".join(sanitize_key(i) for i in keys)
else:
formatted_key = sanitize_key(k)
val = v
fields.append(f"{formatted_key} = {fmt_any(val, flatten)};")
return "{\n" + indent("\n".join(fields)) + "\n}"
def fmt_array(o, flatten):
body = indent("\n".join(fmt_any(i, flatten) for i in o))
return f"[\n{body}\n]"
def fmt_any(o, flatten):
if isinstance(o, str) or isinstance(o, bool) or isinstance(o, int):
return json.dumps(o)
if isinstance(o, list):
return fmt_array(o, flatten)
if isinstance(o, dict):
return fmt_object(o, flatten)
raise TypeError(f"Unknown type {type(o)!r}")
def main():
flatten = "--flatten" in sys.argv
args = [a for a in sys.argv[1:] if not a.startswith("--")]
if len(args) < 1:
print(f"Usage: {sys.argv[0]} [--flatten] <file.json>", file=sys.stderr)
sys.exit(1)
with open(args[0], "r") as f:
data = json.loads(strip_comments(f.read()))
print(fmt_any(data, flatten=flatten))
if __name__ == "__main__":
main()
@eclairevoyant
Copy link

@dzmitry-lahoda builtins can basically handle this already:

$ nix-instantiate --eval -E 'builtins.fromJSON (builtins.readFile ./test.json)'
{ a = { b = { c = 1; }; }; }

and can be piped through a formatter:

$ nix-instantiate --eval -E 'builtins.fromJSON (builtins.readFile ./test.json)' | nixfmt
{
  a = {
    b = {
      c = 1;
    };
  };
}

@spikespaz
Copy link

spikespaz commented Jun 8, 2024

#! /usr/bin/env bash
filename="$(basename "$0")-$(date +%s)-$(basename "$1")"
cat "$1" | sed -e 's;//.*$;;g' > /tmp/"$filename"
echo "$(nix eval --expr 'builtins.fromJSON (builtins.readFile /tmp/'"$filename"')' --impure | nixfmt)"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment