diff --git a/panel/src/panel/configuration/to_module.py b/panel/src/panel/configuration/to_module.py new file mode 100644 index 00000000..36e3c131 --- /dev/null +++ b/panel/src/panel/configuration/to_module.py @@ -0,0 +1,53 @@ +import json +import textwrap + +# TODO(@fricklerhandwerk): mix this in as a method on the Pydantic Schema itself +def generate_module(schema, service_name): + properties = schema.get("properties", {}) + required = schema.get("required", []) + + options = [] + for name, prop in properties.items(): + nix_type = { + "string": "types.str", + "integer": "types.int", + "boolean": "types.bool" + }[prop["type"]] + + desc = prop.get("description") + + default = None + if name not in required and "default" in prop: + to_nix_value = { + "string": lambda v: f'"{v}"', + "boolean": lambda v: str(v).lower(), + "integer": lambda v: str(v), + "number": lambda v: str(v) + } + default = to_nix_value[prop["type"]](prop["default"]) + + option = textwrap.dedent(f""" + {name} = mkOption {{{f''' + description = "{desc}";''' if desc else ''} + type = {nix_type};{f''' + default = {default};''' if default is not None else ''} + }}; + """) + + options.append(option.strip()) + + module = textwrap.dedent(f""" + {{ lib, ... }}: + + let + cfg = config.services.{service_name}; + inherit (lib) mkOption types; + in + {{ + options.services.{service_name} = {{ + {textwrap.indent("\n\n".join(options), ' ').strip()} + }}; + }} + """) + + return module diff --git a/panel/src/panel/tests/test_to_module.py b/panel/src/panel/tests/test_to_module.py new file mode 100644 index 00000000..f3c2ae13 --- /dev/null +++ b/panel/src/panel/tests/test_to_module.py @@ -0,0 +1,44 @@ +from django.test import TestCase +from pydantic import BaseModel, Field +import textwrap + +from panel.configuration import to_module + + +class ConvertToModule(TestCase): + def test_minimal_module(self): + class Example(BaseModel): + name: str = Field(..., description="Service name") + port: int = Field(8080) + enable: bool = Field(True, description="Enable service") + + expected = textwrap.dedent(""" + { lib, ... }: + + let + cfg = config.services.myservice; + inherit (lib) mkOption types; + in + { + options.services.myservice = { + name = mkOption { + description = "Service name"; + type = types.str; + }; + + port = mkOption { + type = types.int; + default = 8080; + }; + + enable = mkOption { + description = "Enable service"; + type = types.bool; + default = true; + }; + }; + } + """) + + actual = to_module.generate_module(Example.schema(), "myservice") + self.assertEqual(actual, expected)