modelable 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- modelable/__init__.py +1 -0
- modelable/__main__.py +3 -0
- modelable/_pydantic_py314_compat.py +31 -0
- modelable/cli.py +41 -0
- modelable/commands/__init__.py +1 -0
- modelable/commands/apicurio.py +84 -0
- modelable/commands/codegen.py +241 -0
- modelable/commands/common.py +43 -0
- modelable/commands/compile.py +237 -0
- modelable/commands/create.py +164 -0
- modelable/commands/diff.py +82 -0
- modelable/commands/graph.py +53 -0
- modelable/commands/llm.py +564 -0
- modelable/commands/lsp.py +15 -0
- modelable/commands/runtime.py +37 -0
- modelable/commands/scenario.py +104 -0
- modelable/commands/spec.py +197 -0
- modelable/commands/workspace.py +240 -0
- modelable/compat/__init__.py +11 -0
- modelable/compat/checker.py +179 -0
- modelable/compat/diff.py +169 -0
- modelable/compiler/__init__.py +3 -0
- modelable/compiler/compiler.py +19 -0
- modelable/compiler/workspace.py +346 -0
- modelable/diagnostics/__init__.py +3 -0
- modelable/diagnostics/model.py +27 -0
- modelable/emitters/__init__.py +0 -0
- modelable/emitters/base.py +22 -0
- modelable/emitters/csharp.py +245 -0
- modelable/emitters/dbt_yaml.py +290 -0
- modelable/emitters/diagnostics.py +25 -0
- modelable/emitters/fhir.py +694 -0
- modelable/emitters/fhir_validator.py +36 -0
- modelable/emitters/go.py +334 -0
- modelable/emitters/java.py +264 -0
- modelable/emitters/json_schema.py +458 -0
- modelable/emitters/markdown.py +252 -0
- modelable/emitters/odcs.py +355 -0
- modelable/emitters/openlineage.py +315 -0
- modelable/emitters/openmetadata.py +258 -0
- modelable/emitters/python.py +282 -0
- modelable/emitters/rust.py +643 -0
- modelable/emitters/shapes.py +261 -0
- modelable/emitters/sql.py +266 -0
- modelable/emitters/targets.py +141 -0
- modelable/emitters/typescript.py +352 -0
- modelable/expressions/__init__.py +0 -0
- modelable/expressions/cel.py +547 -0
- modelable/governance/__init__.py +3 -0
- modelable/governance/checker.py +271 -0
- modelable/governance/por.py +46 -0
- modelable/grammar/__init__.py +1 -0
- modelable/grammar/modelable.lark +257 -0
- modelable/graph/__init__.py +5 -0
- modelable/graph/export.py +442 -0
- modelable/llm/__init__.py +43 -0
- modelable/llm/chat.py +255 -0
- modelable/llm/config.py +87 -0
- modelable/llm/context.py +194 -0
- modelable/llm/engine.py +976 -0
- modelable/llm/importers.py +1077 -0
- modelable/llm/provenance.py +84 -0
- modelable/llm/providers.py +182 -0
- modelable/llm/qa.py +126 -0
- modelable/llm/recommendations.py +33 -0
- modelable/llm/redaction.py +19 -0
- modelable/llm/render.py +279 -0
- modelable/llm/update_plan.py +101 -0
- modelable/llm/validation_help.py +10 -0
- modelable/lsp/__init__.py +3 -0
- modelable/lsp/__main__.py +4 -0
- modelable/lsp/code_actions.py +210 -0
- modelable/lsp/completion.py +480 -0
- modelable/lsp/definition.py +343 -0
- modelable/lsp/diagnostics.py +31 -0
- modelable/lsp/document_symbols.py +197 -0
- modelable/lsp/federation.py +261 -0
- modelable/lsp/folding.py +33 -0
- modelable/lsp/formatting.py +64 -0
- modelable/lsp/highlight.py +30 -0
- modelable/lsp/hover.py +370 -0
- modelable/lsp/inlay_hints.py +158 -0
- modelable/lsp/references.py +511 -0
- modelable/lsp/rename.py +564 -0
- modelable/lsp/semantic_tokens.py +412 -0
- modelable/lsp/server.py +370 -0
- modelable/lsp/workspace.py +83 -0
- modelable/lsp/workspace_symbols.py +104 -0
- modelable/parser/__init__.py +94 -0
- modelable/parser/ir.py +451 -0
- modelable/parser/parse.py +47 -0
- modelable/parser/transformer.py +798 -0
- modelable/parser/wire.py +68 -0
- modelable/planner/__init__.py +0 -0
- modelable/planner/lineage.py +91 -0
- modelable/planner/planner.py +134 -0
- modelable/planner/plans.py +122 -0
- modelable/py.typed +0 -0
- modelable/registry/__init__.py +9 -0
- modelable/registry/apicurio.py +166 -0
- modelable/registry/base.py +18 -0
- modelable/registry/factory.py +18 -0
- modelable/registry/index.py +419 -0
- modelable/registry/local.py +26 -0
- modelable/registry/oci.py +22 -0
- modelable/registry/resolver.py +213 -0
- modelable/registry/schema.sql +119 -0
- modelable/registry/signature.py +26 -0
- modelable/release.py +125 -0
- modelable/runtime/__init__.py +5 -0
- modelable/runtime/adapter/__init__.py +17 -0
- modelable/runtime/adapter/base.py +18 -0
- modelable/runtime/adapter/postgres.py +82 -0
- modelable/specs/__init__.py +23 -0
- modelable/specs/tracking.py +220 -0
- modelable/validation/__init__.py +3 -0
- modelable/validation/semantic.py +659 -0
- modelable-1.0.0.dist-info/METADATA +61 -0
- modelable-1.0.0.dist-info/RECORD +122 -0
- modelable-1.0.0.dist-info/WHEEL +4 -0
- modelable-1.0.0.dist-info/entry_points.txt +2 -0
- modelable-1.0.0.dist-info/licenses/LICENSE +201 -0
modelable/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from modelable import _pydantic_py314_compat as _ # noqa: F401
|
modelable/__main__.py
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Compatibility shim for pydantic 2.x on Python 3.14.
|
|
3
|
+
|
|
4
|
+
pydantic 2.x calls typing._eval_type(..., prefer_fwd_module=True) when it
|
|
5
|
+
detects Python 3.14, but Python 3.14 RC2 renamed that parameter to
|
|
6
|
+
parent_fwdref. This shim patches typing._eval_type to accept both names so
|
|
7
|
+
pydantic model definitions succeed without modification.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import typing
|
|
13
|
+
|
|
14
|
+
_real_eval_type = typing._eval_type # type: ignore[attr-defined]
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _compat_eval_type(
|
|
18
|
+
t: object,
|
|
19
|
+
globalns: object = None,
|
|
20
|
+
localns: object = None,
|
|
21
|
+
type_params: object = None,
|
|
22
|
+
*,
|
|
23
|
+
prefer_fwd_module: object = None,
|
|
24
|
+
**kwargs: object,
|
|
25
|
+
) -> object:
|
|
26
|
+
if prefer_fwd_module is not None:
|
|
27
|
+
kwargs.setdefault("parent_fwdref", prefer_fwd_module)
|
|
28
|
+
return _real_eval_type(t, globalns, localns, type_params=type_params, **kwargs)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
typing._eval_type = _compat_eval_type # type: ignore[attr-defined]
|
modelable/cli.py
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import click
|
|
4
|
+
|
|
5
|
+
from modelable.commands.apicurio import register_apicurio_commands
|
|
6
|
+
from modelable.commands.codegen import register_codegen_commands
|
|
7
|
+
from modelable.commands.compile import register_compile_commands
|
|
8
|
+
from modelable.commands.create import register_create_commands
|
|
9
|
+
from modelable.commands.diff import register_diff_commands
|
|
10
|
+
from modelable.commands.graph import register_graph_commands
|
|
11
|
+
from modelable.commands.llm import register_llm_commands
|
|
12
|
+
from modelable.commands.lsp import register_lsp_commands
|
|
13
|
+
from modelable.commands.runtime import register_runtime_commands
|
|
14
|
+
from modelable.commands.scenario import register_scenario_commands
|
|
15
|
+
from modelable.commands.spec import register_spec_commands
|
|
16
|
+
from modelable.commands.workspace import register_workspace_commands
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@click.group()
|
|
20
|
+
@click.version_option(package_name="modelable", prog_name="modelable")
|
|
21
|
+
def cli() -> None:
|
|
22
|
+
"""Modelable domain-owned data model compiler.
|
|
23
|
+
|
|
24
|
+
MVP workflows cover validate, resolve, lineage, diff, compile, docs,
|
|
25
|
+
inspect, codegen, lsp, scenario, create helpers, and Apicurio JSON Schema
|
|
26
|
+
artifact publish/pull.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
register_workspace_commands(cli)
|
|
31
|
+
register_compile_commands(cli)
|
|
32
|
+
register_create_commands(cli)
|
|
33
|
+
register_diff_commands(cli)
|
|
34
|
+
register_graph_commands(cli)
|
|
35
|
+
register_lsp_commands(cli)
|
|
36
|
+
register_llm_commands(cli)
|
|
37
|
+
register_codegen_commands(cli)
|
|
38
|
+
register_scenario_commands(cli)
|
|
39
|
+
register_runtime_commands(cli)
|
|
40
|
+
register_apicurio_commands(cli)
|
|
41
|
+
register_spec_commands(cli)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import sys
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
import click
|
|
8
|
+
|
|
9
|
+
from modelable.commands.common import console, load_workspace_or_exit
|
|
10
|
+
from modelable.emitters.json_schema import emit_json_schema
|
|
11
|
+
from modelable.registry.apicurio import ApicurioArtifact, ApicurioRegistryClient, ApicurioRegistryError
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def register_apicurio_commands(cli_group: click.Group) -> None:
|
|
15
|
+
cli_group.add_command(publish)
|
|
16
|
+
cli_group.add_command(pull)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@click.group()
|
|
20
|
+
def publish() -> None:
|
|
21
|
+
"""Publish generated artifacts to external registries."""
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@publish.command(name="apicurio")
|
|
25
|
+
@click.argument("source", type=click.Path(exists=True, path_type=Path))
|
|
26
|
+
@click.option("--url", required=True, help="Apicurio Registry base URL or /apis/registry/v3 URL.")
|
|
27
|
+
@click.option("--group", default="default", show_default=True, help="Apicurio artifact group.")
|
|
28
|
+
@click.option("--token", default=None, help="Bearer token. Defaults to MODELABLE_APICURIO_TOKEN.")
|
|
29
|
+
@click.option("--dry-run", is_flag=True, help="List artifacts without publishing.")
|
|
30
|
+
def publish_apicurio(source: Path, url: str, group: str, token: str | None, dry_run: bool) -> None:
|
|
31
|
+
"""Publish JSON Schema artifacts generated from SOURCE to Apicurio Registry."""
|
|
32
|
+
workspace = load_workspace_or_exit(source)
|
|
33
|
+
emitted = emit_json_schema(workspace, Path(".modelable/apicurio"))
|
|
34
|
+
artifacts = [
|
|
35
|
+
ApicurioArtifact(
|
|
36
|
+
artifact_id=artifact.artifact_id,
|
|
37
|
+
version=artifact.artifact_id.rsplit(".v", 1)[1],
|
|
38
|
+
content=artifact.content,
|
|
39
|
+
)
|
|
40
|
+
for artifact in emitted
|
|
41
|
+
if isinstance(artifact.content, dict)
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
if dry_run:
|
|
45
|
+
console.print(f"[yellow]DRY RUN[/yellow] would publish {len(artifacts)} JSON Schema artifact(s) to {url}")
|
|
46
|
+
for artifact in artifacts:
|
|
47
|
+
console.print(f"- {artifact.artifact_id} (group={group}, version={artifact.version})")
|
|
48
|
+
sys.exit(0)
|
|
49
|
+
|
|
50
|
+
client = ApicurioRegistryClient(url, token=token or os.getenv("MODELABLE_APICURIO_TOKEN"))
|
|
51
|
+
try:
|
|
52
|
+
for artifact in artifacts:
|
|
53
|
+
client.publish_json_schema(artifact, group=group)
|
|
54
|
+
console.print(f"[green]OK[/green] published {group}/{artifact.artifact_id}@{artifact.version}")
|
|
55
|
+
except ApicurioRegistryError as exc:
|
|
56
|
+
console.print(f"[red]ERROR[/red] {exc}")
|
|
57
|
+
sys.exit(1)
|
|
58
|
+
|
|
59
|
+
if not artifacts:
|
|
60
|
+
console.print("[yellow]No JSON Schema artifacts generated.[/yellow]")
|
|
61
|
+
sys.exit(0)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
@click.group()
|
|
65
|
+
def pull() -> None:
|
|
66
|
+
"""Pull generated artifacts from external registries."""
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
@pull.command(name="apicurio")
|
|
70
|
+
@click.argument("ref")
|
|
71
|
+
@click.option("--url", required=True, help="Apicurio Registry base URL or /apis/registry/v3 URL.")
|
|
72
|
+
@click.option("--group", default="default", show_default=True, help="Apicurio artifact group.")
|
|
73
|
+
@click.option("--out", "out_dir", type=click.Path(path_type=Path), default=Path("./dist/jsonschema"))
|
|
74
|
+
@click.option("--token", default=None, help="Bearer token. Defaults to MODELABLE_APICURIO_TOKEN.")
|
|
75
|
+
def pull_apicurio(ref: str, url: str, group: str, out_dir: Path, token: str | None) -> None:
|
|
76
|
+
"""Pull a JSON Schema artifact by Modelable REF from Apicurio Registry."""
|
|
77
|
+
client = ApicurioRegistryClient(url, token=token or os.getenv("MODELABLE_APICURIO_TOKEN"))
|
|
78
|
+
try:
|
|
79
|
+
path = client.pull_json_schema(ref, group=group, out_dir=out_dir)
|
|
80
|
+
except ApicurioRegistryError as exc:
|
|
81
|
+
console.print(f"[red]ERROR[/red] {exc}")
|
|
82
|
+
sys.exit(1)
|
|
83
|
+
console.print(f"[green]OK[/green] wrote {path}")
|
|
84
|
+
sys.exit(0)
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import click
|
|
4
|
+
from rich.console import Console
|
|
5
|
+
|
|
6
|
+
from modelable.emitters.shapes import type_shape_catalog
|
|
7
|
+
from modelable.emitters.targets import (
|
|
8
|
+
get_codegen_target,
|
|
9
|
+
list_implemented_codegen_targets,
|
|
10
|
+
)
|
|
11
|
+
from modelable.emitters.targets import (
|
|
12
|
+
list_codegen_targets as _list_codegen_targets,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
console = Console()
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def register_codegen_commands(cli_group: click.Group) -> None:
|
|
19
|
+
cli_group.add_command(codegen)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@click.group()
|
|
23
|
+
def codegen() -> None:
|
|
24
|
+
"""Explore supported code generation formats and type mappings."""
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@codegen.command(name="formats")
|
|
28
|
+
def formats() -> None:
|
|
29
|
+
"""List supported compilation targets."""
|
|
30
|
+
console.print("Supported code generation formats:", markup=False)
|
|
31
|
+
for entry in _list_codegen_targets():
|
|
32
|
+
console.print(f"- {entry.name}: {entry.description} [{entry.status}, {entry.kind}]", markup=False)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@codegen.command(name="types")
|
|
36
|
+
@click.option(
|
|
37
|
+
"--format",
|
|
38
|
+
"format_name",
|
|
39
|
+
type=click.Choice([entry.name for entry in list_implemented_codegen_targets()]),
|
|
40
|
+
default="typescript",
|
|
41
|
+
show_default=True,
|
|
42
|
+
help="Target format to describe.",
|
|
43
|
+
)
|
|
44
|
+
def types(format_name: str) -> None:
|
|
45
|
+
"""Show the field-type mapping for a target format."""
|
|
46
|
+
entry = get_codegen_target(format_name)
|
|
47
|
+
console.print("Target inventory:", markup=False)
|
|
48
|
+
for target in _list_codegen_targets():
|
|
49
|
+
console.print(f"- {target.name}: {target.status} [{target.kind}]", markup=False)
|
|
50
|
+
console.print("", markup=False)
|
|
51
|
+
console.print(f"{format_name} type mappings", markup=False)
|
|
52
|
+
console.print(f"{entry.description}", markup=False)
|
|
53
|
+
console.print("", markup=False)
|
|
54
|
+
console.print("Type shape catalog:", markup=False)
|
|
55
|
+
for label, shape, note in type_shape_catalog():
|
|
56
|
+
line = f"- {label}: {shape.describe()}"
|
|
57
|
+
if note:
|
|
58
|
+
line += f" ({note})"
|
|
59
|
+
console.print(line, markup=False)
|
|
60
|
+
console.print("", markup=False)
|
|
61
|
+
|
|
62
|
+
for source_type, target_type, note in _type_mappings_for(format_name):
|
|
63
|
+
line = f"- {source_type} -> {target_type}"
|
|
64
|
+
if note:
|
|
65
|
+
line += f" ({note})"
|
|
66
|
+
console.print(line, markup=False)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def list_codegen_targets() -> list[dict[str, object]]:
|
|
70
|
+
return [
|
|
71
|
+
{
|
|
72
|
+
"name": target.name,
|
|
73
|
+
"description": target.description,
|
|
74
|
+
"status": target.status,
|
|
75
|
+
"kind": target.kind,
|
|
76
|
+
"default_out_dir": str(target.default_out_dir) if target.default_out_dir is not None else None,
|
|
77
|
+
}
|
|
78
|
+
for target in _list_codegen_targets()
|
|
79
|
+
]
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def _type_mappings_for(format_name: str) -> list[tuple[str, str, str | None]]:
|
|
83
|
+
if format_name == "json-schema":
|
|
84
|
+
return [
|
|
85
|
+
("string", '{"type":"string"}', None),
|
|
86
|
+
("bool", '{"type":"boolean"}', None),
|
|
87
|
+
("int", '{"type":"integer","format":"int64"}', None),
|
|
88
|
+
("float", '{"type":"number"}', None),
|
|
89
|
+
("uuid", '{"type":"string","format":"uuid"}', None),
|
|
90
|
+
("timestamp", '{"type":"string","format":"date-time"}', None),
|
|
91
|
+
("date", '{"type":"string","format":"date"}', None),
|
|
92
|
+
("time", '{"type":"string","format":"time"}', None),
|
|
93
|
+
("duration", '{"type":"string","format":"duration"}', None),
|
|
94
|
+
("binary", '{"type":"string","contentEncoding":"base64"}', None),
|
|
95
|
+
("decimal(p, s)", '{"type":"string","pattern":"^-?\\d+(\\.\\d+)?$"}', None),
|
|
96
|
+
("array<T>", '{"type":"array","items":<T>}', None),
|
|
97
|
+
("map<K, V>", '{"type":"object","additionalProperties":<V>}', None),
|
|
98
|
+
("ref<T>", '{"type":"string","x-modelable-ref":"T"}', None),
|
|
99
|
+
("enum(...)", '{"type":"string","enum":[...]}', None),
|
|
100
|
+
("object { ... }", '{"type":"object","properties":{...}}', None),
|
|
101
|
+
("named", '{"type":"object","x-modelable-field":{"namedType":"Name"}}', None),
|
|
102
|
+
]
|
|
103
|
+
if format_name == "csharp":
|
|
104
|
+
return [
|
|
105
|
+
("string", "string", "optional fields use string?"),
|
|
106
|
+
("bool", "bool", "optional fields use bool?"),
|
|
107
|
+
("int", "int", "optional fields use int?"),
|
|
108
|
+
("float", "double", "optional fields use double?"),
|
|
109
|
+
("uuid", "Guid", "optional fields use Guid?"),
|
|
110
|
+
("timestamp", "DateTime", "optional fields use DateTime?"),
|
|
111
|
+
("date", "DateOnly", "optional fields use DateOnly?"),
|
|
112
|
+
("time", "TimeOnly", "optional fields use TimeOnly?"),
|
|
113
|
+
("duration", "TimeSpan", "optional fields use TimeSpan?"),
|
|
114
|
+
("binary", "byte[]", "optional fields use byte[]?"),
|
|
115
|
+
("decimal(p, s)", "decimal", "optional fields use decimal?"),
|
|
116
|
+
("array<T>", "List<T>", None),
|
|
117
|
+
("map<K, V>", "Dictionary<string, V>", None),
|
|
118
|
+
("ref<T>", "string", "references compile to reference strings"),
|
|
119
|
+
("enum(...)", "string", None),
|
|
120
|
+
("object { ... }", "{ ... }", "inline objects become nested records"),
|
|
121
|
+
("named", "Name", None),
|
|
122
|
+
]
|
|
123
|
+
if format_name == "java":
|
|
124
|
+
return [
|
|
125
|
+
("string", "String", "optional fields use Optional<String>"),
|
|
126
|
+
("bool", "Boolean", "optional fields use Optional<Boolean>"),
|
|
127
|
+
("int", "Long", "optional fields use Optional<Long>"),
|
|
128
|
+
("float", "Double", "optional fields use Optional<Double>"),
|
|
129
|
+
("uuid", "UUID", "optional fields use Optional<UUID>"),
|
|
130
|
+
("timestamp", "Instant", "optional fields use Optional<Instant>"),
|
|
131
|
+
("date", "LocalDate", "optional fields use Optional<LocalDate>"),
|
|
132
|
+
("time", "LocalTime", "optional fields use Optional<LocalTime>"),
|
|
133
|
+
("duration", "Duration", "optional fields use Optional<Duration>"),
|
|
134
|
+
("binary", "byte[]", "optional fields use Optional<byte[]>"),
|
|
135
|
+
("decimal(p, s)", "BigDecimal", "optional fields use Optional<BigDecimal>"),
|
|
136
|
+
("array<T>", "List<T>", None),
|
|
137
|
+
("map<K, V>", "Map<String, V>", None),
|
|
138
|
+
("ref<T>", "String", "references compile to reference strings"),
|
|
139
|
+
("enum(...)", "String", None),
|
|
140
|
+
("object { ... }", "{ ... }", "inline objects become nested records"),
|
|
141
|
+
("named", "NameV1", None),
|
|
142
|
+
]
|
|
143
|
+
if format_name == "python":
|
|
144
|
+
return [
|
|
145
|
+
("string", "str", "optional fields use Optional[str]"),
|
|
146
|
+
("bool", "bool", "optional fields use Optional[bool]"),
|
|
147
|
+
("int", "int", "optional fields use Optional[int]"),
|
|
148
|
+
("float", "float", "optional fields use Optional[float]"),
|
|
149
|
+
("uuid", "UUID", "optional fields use Optional[UUID]"),
|
|
150
|
+
("timestamp", "datetime", "optional fields use Optional[datetime]"),
|
|
151
|
+
("date", "date", "optional fields use Optional[date]"),
|
|
152
|
+
("time", "time", "optional fields use Optional[time]"),
|
|
153
|
+
("duration", "timedelta", "optional fields use Optional[timedelta]"),
|
|
154
|
+
("binary", "bytes", "optional fields use Optional[bytes]"),
|
|
155
|
+
("decimal(p, s)", "Decimal", "optional fields use Optional[Decimal]"),
|
|
156
|
+
("array<T>", "list[T]", None),
|
|
157
|
+
("map<K, V>", "dict[str, V]", None),
|
|
158
|
+
("ref<T>", "str", "references compile to reference strings"),
|
|
159
|
+
("enum(...)", "str", None),
|
|
160
|
+
("object { ... }", "{ ... }", "inline objects become nested dataclasses"),
|
|
161
|
+
("named", "Name", None),
|
|
162
|
+
]
|
|
163
|
+
if format_name == "rust":
|
|
164
|
+
return [
|
|
165
|
+
("string", "String", "optional fields use Option<String>"),
|
|
166
|
+
("bool", "bool", "optional fields use Option<bool>"),
|
|
167
|
+
("int", "i64", "optional fields use Option<i64>"),
|
|
168
|
+
("float", "f64", "optional fields use Option<f64>"),
|
|
169
|
+
("uuid", "String", "optional fields use Option<String>"),
|
|
170
|
+
("timestamp", "String", "optional fields use Option<String>"),
|
|
171
|
+
("date", "String", "optional fields use Option<String>"),
|
|
172
|
+
("time", "String", "optional fields use Option<String>"),
|
|
173
|
+
("duration", "String", "optional fields use Option<String>"),
|
|
174
|
+
("binary", "Vec<u8>", "optional fields use Option<Vec<u8>>"),
|
|
175
|
+
("decimal(p, s)", "String", "optional fields use Option<String>"),
|
|
176
|
+
("array<T>", "Vec<T>", None),
|
|
177
|
+
("map<K, V>", "HashMap<String, V>", None),
|
|
178
|
+
("ref<T>", "String", "references compile to reference strings"),
|
|
179
|
+
("enum(...)", "String", None),
|
|
180
|
+
("object { ... }", "{ ... }", "inline objects become nested structs"),
|
|
181
|
+
("named", "Name", None),
|
|
182
|
+
]
|
|
183
|
+
if format_name == "go":
|
|
184
|
+
return [
|
|
185
|
+
("string", "string", "optional fields use *string"),
|
|
186
|
+
("bool", "bool", "optional fields use *bool"),
|
|
187
|
+
("int", "int64", "optional fields use *int64"),
|
|
188
|
+
("float", "float64", "optional fields use *float64"),
|
|
189
|
+
("uuid", "string", "optional fields use *string"),
|
|
190
|
+
("timestamp", "time.Time", "optional fields use *time.Time"),
|
|
191
|
+
("date", "time.Time", "optional fields use *time.Time"),
|
|
192
|
+
("time", "time.Time", "optional fields use *time.Time"),
|
|
193
|
+
("duration", "time.Duration", "optional fields use *time.Duration"),
|
|
194
|
+
("binary", "[]byte", "optional fields use *[]byte"),
|
|
195
|
+
("decimal(p, s)", "string", "optional fields use *string"),
|
|
196
|
+
("array<T>", "[]T", None),
|
|
197
|
+
("map<K, V>", "map[string]V", None),
|
|
198
|
+
("ref<T>", "string", "references compile to reference strings"),
|
|
199
|
+
("enum(...)", "string", None),
|
|
200
|
+
("object { ... }", "{ ... }", "inline objects become nested structs"),
|
|
201
|
+
("named", "Name", None),
|
|
202
|
+
]
|
|
203
|
+
if format_name == "markdown":
|
|
204
|
+
return [
|
|
205
|
+
("string", "string", "rendered as canonical .mdl text"),
|
|
206
|
+
("bool", "bool", "rendered as canonical .mdl text"),
|
|
207
|
+
("int", "int", "rendered as canonical .mdl text"),
|
|
208
|
+
("float", "float", "rendered as canonical .mdl text"),
|
|
209
|
+
("uuid", "uuid", "rendered as canonical .mdl text"),
|
|
210
|
+
("timestamp", "timestamp", "rendered as canonical .mdl text"),
|
|
211
|
+
("date", "date", "rendered as canonical .mdl text"),
|
|
212
|
+
("time", "time", "rendered as canonical .mdl text"),
|
|
213
|
+
("duration", "duration", "rendered as canonical .mdl text"),
|
|
214
|
+
("binary", "binary", "rendered as canonical .mdl text"),
|
|
215
|
+
("decimal(p, s)", "decimal(p, s)", "rendered as canonical .mdl text"),
|
|
216
|
+
("array<T>", "array<T>", "rendered as canonical .mdl text"),
|
|
217
|
+
("map<K, V>", "map<K, V>", "rendered as canonical .mdl text"),
|
|
218
|
+
("ref<T>", "ref<T>", "rendered as canonical .mdl text"),
|
|
219
|
+
("enum(...)", "enum(...)", "rendered as canonical .mdl text"),
|
|
220
|
+
("object { ... }", "object { ... }", "rendered as canonical .mdl text"),
|
|
221
|
+
("named", "Name", "rendered as canonical .mdl text"),
|
|
222
|
+
]
|
|
223
|
+
return [
|
|
224
|
+
("string", "string", None),
|
|
225
|
+
("bool", "boolean", None),
|
|
226
|
+
("int", "number", None),
|
|
227
|
+
("float", "number", None),
|
|
228
|
+
("uuid", "string", "preserves uuid format as a string"),
|
|
229
|
+
("timestamp", "string", "preserves date-time semantics"),
|
|
230
|
+
("date", "string", "preserves date semantics"),
|
|
231
|
+
("time", "string", "preserves time semantics"),
|
|
232
|
+
("duration", "string", "preserves duration semantics"),
|
|
233
|
+
("binary", "string", "base64-encoded"),
|
|
234
|
+
("decimal(p, s)", "string", "decimal precision and scale encoded as text"),
|
|
235
|
+
("array<T>", "T[]", None),
|
|
236
|
+
("map<K, V>", "Record<string, V>", None),
|
|
237
|
+
("ref<T>", "string", "reference string"),
|
|
238
|
+
("enum(...)", "'a' | 'b' | ...", None),
|
|
239
|
+
("object { ... }", "{ ... }", None),
|
|
240
|
+
("named", "Name", None),
|
|
241
|
+
]
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
from rich.console import Console
|
|
7
|
+
|
|
8
|
+
from modelable.compiler.workspace import load_workspace
|
|
9
|
+
from modelable.diagnostics.model import render_diagnostic
|
|
10
|
+
from modelable.parser.ir import ParseError, VersionPinned
|
|
11
|
+
|
|
12
|
+
console = Console()
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def load_workspace_or_exit(path: Path):
|
|
16
|
+
try:
|
|
17
|
+
workspace = load_workspace(path)
|
|
18
|
+
except FileNotFoundError:
|
|
19
|
+
console.print("[yellow]No .mdl files found.[/yellow]")
|
|
20
|
+
sys.exit(0)
|
|
21
|
+
except ParseError as exc:
|
|
22
|
+
console.print(f"[red]ERROR[/red] {render_diagnostic(exc.diagnostic(path=path))}")
|
|
23
|
+
sys.exit(1)
|
|
24
|
+
|
|
25
|
+
if workspace.errors:
|
|
26
|
+
for diagnostic in workspace.errors:
|
|
27
|
+
console.print(f"[red]ERROR[/red] {render_diagnostic(diagnostic)}", soft_wrap=True)
|
|
28
|
+
sys.exit(1)
|
|
29
|
+
|
|
30
|
+
return workspace
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def render_version_spec(version_spec) -> str:
|
|
34
|
+
kind = getattr(version_spec, "kind", None)
|
|
35
|
+
if kind == "exact":
|
|
36
|
+
return str(version_spec.version)
|
|
37
|
+
if kind == "range":
|
|
38
|
+
return f">={version_spec.min_inclusive}<{version_spec.max_exclusive}"
|
|
39
|
+
if kind == "min":
|
|
40
|
+
return f">={version_spec.min_inclusive}"
|
|
41
|
+
if isinstance(version_spec, VersionPinned):
|
|
42
|
+
return f"{version_spec.version}#{version_spec.content_hash}"
|
|
43
|
+
return "?"
|