#!/usr/bin/env python3 import sys import json ra_package_json = sys.argv[1] with open(ra_package_json) as f: ra_package = json.load(f) config = ra_package["contributes"]["configuration"]["properties"] config_dict = {} in_common_block = False def py_to_nix(obj): if obj is None: return "null" if obj is False: return "false" if obj is True: return "true" if isinstance(obj, str): s = f'"{obj}"' if "${" in s: s = s.replace("${", "$\\{") return s if isinstance(obj, int): return f"{obj}" if isinstance(obj, dict): val = "{" for key in obj: key_val = py_to_nix(obj[key]) val += f'"{key}" = {key_val};\n' val += "}" return val if isinstance(obj, list): return "[" + " ".join(py_to_nix(val) for val in obj) + "]" print(f"Unhandled value: {obj}") sys.exit(1) def ty_to_nix(ty): if ty == "boolean": return "types.bool" if ty == "string": return "types.str" # This is an object without any additional properties if ty == "object": return "types.attrsOf types.anything" if isinstance(ty, list) and ty[0] == "null": if len(ty) > 2: print("Unhandled type", ty) sys.exit() nullable_ty = ty_to_nix(ty[1]) return f"types.nullOr ({nullable_ty})" if isinstance(ty, list): either_types = (ty_to_nix(t) for t in ty) either_types = " ".join(f"({t})" for t in either_types) return f"types.oneOf ([{either_types}])" print(f"Unhandled type: {ty}") sys.exit(1) def prop_ty_to_nix(prop_info): if "type" in prop_info: if "enum" in prop_info: enum = "[" + " ".join(f'"{member}"' for member in prop_info["enum"]) + "]" if prop_info["type"] == "string": return f"types.enum {enum}" print("TODO: with unknown enum type", prop_info["type"]) sys.exit() if "additionalProperties" in prop_info or "properties" in prop_info: print("TODO: with (additional)Properties", prop_info) sys.exit() if "minimum" in prop_info or "maximum" in prop_info: can_be_null = False if "null" in prop_info["type"]: can_be_null = True if len(prop_info["type"]) > 2: print("Unhandled int type", prop_info["type"]) sys.exit() prop_info["type"] = prop_info["type"][1] if prop_info["type"] == "number": int_ty = "types.number" elif prop_info["type"] == "integer": int_ty = "types.int" else: print("Unhandled int type", prop_info["type"]) sys.exit() if "minimum" in prop_info and "maximum" in prop_info: min = prop_info["minimum"] max = prop_info["maximum"] int_ty = f"{int_ty}s.between {min} {max}" elif "minimum" in prop_info: min = prop_info["minimum"] int_ty = f"types.addCheck {int_ty} (x: x >= {min})" else: print("TODO: max number", prop_info) sys.exit() if can_be_null: return f"types.nullOr ({int_ty})" else: return int_ty if "array" in prop_info["type"] or prop_info["type"] == "array": if "items" not in prop_info: print("Array without items") sys.exit() items_ty = prop_ty_to_nix(prop_info["items"]) array_ty = f"types.listOf ({items_ty})" if prop_info["type"] == "array": return array_ty elif prop_info["type"] == ["null", "array"]: return f"types.nullOr ({array_ty})" else: print("Unhandled array type", prop_info) sys.exit() return ty_to_nix(prop_info["type"]) elif "anyOf" in prop_info: can_be_null = False if {"type": "null"} in prop_info["anyOf"]: can_be_null = True prop_info["anyOf"].remove({"type": "null"}) types = (prop_ty_to_nix(prop) for prop in prop_info["anyOf"]) one_of = " ".join(f"({ty})" for ty in types) one_of_ty = f"types.oneOf ([{one_of}])" if can_be_null: return f"types.nullOr ({one_of_ty})" else: return one_of_ty else: print("TODO: no *type*", prop_info) sys.exit() for opt in config: if opt.startswith("$"): in_common_block = True continue if not in_common_block: continue opt_path = opt.split(".") if opt_path[0] != "rust-analyzer": print("ERROR: expected all options to start with 'rust-analyzer'") sys.exit(1) path = opt_path[1:-1] option = opt_path[-1] top_dict = config_dict for p in path: if not p in top_dict: top_dict[p] = {} top_dict = top_dict[p] prop_info = config[opt] is_optional = False ty = prop_ty_to_nix(prop_info) default = py_to_nix(prop_info["default"]) if "markdownDescription" in prop_info: desc = prop_info["markdownDescription"] else: desc = prop_info["description"] desc += f"\n\ndefault value is: \n```nix\n {default}\n```" top_dict[ option ] = """ mkOption {{ type = types.nullOr ({ty}); default = null; description = '' {desc} ''; }} """.format( ty=ty, default=default, desc=desc ) def print_dict(d): print("{") for key in d: print(f'"{key}" = ') if isinstance(d[key], str): print(d[key]) else: print_dict(d[key]) print(";") print("}") print("# THIS FILE IS AUTOGENERATED DO NOT EDIT") print("lib: with lib;") print_dict(config_dict)