Skip to content

Commit 2ec8267

Browse files
committed
🍻 auto write jsonschema comment
1 parent 008317e commit 2ec8267

3 files changed

Lines changed: 39 additions & 16 deletions

File tree

arclet/entari/config/file.py

Lines changed: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ def __post_init__(self):
106106

107107

108108
_loaders: dict[str, Callable[[str], dict]] = {}
109-
_dumpers: dict[str, Callable[[dict, int], str]] = {}
109+
_dumpers: dict[str, Callable[[dict, int, str | None], tuple[str, bool]]] = {}
110110

111111

112112
@dataclass
@@ -144,7 +144,7 @@ def handle(m: re.Match[str]):
144144

145145
raise ValueError(f"Unsupported file format: {path.suffix}")
146146

147-
def dumper(self, path: Path, save_path: Path, data: dict, indent: int):
147+
def dumper(self, path: Path, save_path: Path, data: dict, indent: int, apply_schema: bool):
148148
if not path.exists():
149149
return
150150
origin = self.loader(path)
@@ -153,12 +153,15 @@ def dumper(self, path: Path, save_path: Path, data: dict, indent: int):
153153
else:
154154
origin = data
155155
end = save_path.suffix.split(".")[-1]
156+
schema_file = None
157+
if apply_schema:
158+
schema_file = f"{save_path.stem}.schema.json"
156159
if end in _dumpers:
157-
ans = _dumpers[end](origin, indent)
160+
ans, applied = _dumpers[end](origin, indent, schema_file)
158161
if self._env_replaced:
159162
lines = ans.splitlines(keepends=True)
160163
for i, line in self._env_replaced.items():
161-
lines[i] = line
164+
lines[i + applied] = line
162165
ans = "".join(lines)
163166
with save_path.open("w", encoding="utf-8") as f:
164167
f.write(ans)
@@ -243,11 +246,11 @@ def dump(self, indent: int = 2):
243246
for file in self.plugin_extra_files:
244247
path = Path(file)
245248
if path.is_file():
246-
self.dumper(path, path, self._clean(self.plugin.pop(path.stem)), indent)
249+
self.dumper(path, path, self._clean(self.plugin.pop(path.stem)), indent, False)
247250
else:
248251
for _path in path.iterdir():
249252
if _path.is_file():
250-
self.dumper(_path, _path, self._clean(self.plugin.pop(_path.stem)), indent)
253+
self.dumper(_path, _path, self._clean(self.plugin.pop(_path.stem)), indent, False)
251254
for key in list(self.plugin.keys()):
252255
if key.startswith("$"):
253256
continue
@@ -261,9 +264,9 @@ def dump(self, indent: int = 2):
261264
self.plugin[key] = self._clean(value)
262265
return self._origin_data
263266

264-
def save(self, path: str | os.PathLike[str] | None = None, indent: int = 2):
267+
def save(self, path: str | os.PathLike[str] | None = None, indent: int = 2, apply_schema: bool = False):
265268
self.save_flag = True
266-
self.dumper(self.path, Path(path or self.path), self.dump(indent), indent)
269+
self.dumper(self.path, Path(path or self.path), self.dump(indent), indent, apply_schema)
267270

268271
@classmethod
269272
def load(cls, path: str | os.PathLike[str] | None = None) -> "EntariConfig":
@@ -352,7 +355,7 @@ def decorator(func: Callable[[str], dict]):
352355
def register_dumper(*ext: str):
353356
"""Register a dumper for a specific file extension."""
354357

355-
def decorator(func: Callable[[dict, int], str]):
358+
def decorator(func: Callable[[dict, int, str | None], tuple[str, bool]]):
356359
for e in ext:
357360
_dumpers[e] = func
358361
return func
@@ -366,8 +369,12 @@ def json_loader(text: str) -> dict:
366369

367370

368371
@register_dumper("json")
369-
def json_dumper(origin: dict, indent: int):
370-
return json.dumps(origin, indent=indent, ensure_ascii=False)
372+
def json_dumper(origin: dict, indent: int, schema_file: str | None = None) -> tuple[str, bool]:
373+
schema_applied = False
374+
if schema_file and "$schema" not in origin:
375+
origin = {"$schema": f"{schema_file}", **origin}
376+
schema_applied = True
377+
return json.dumps(origin, indent=indent, ensure_ascii=False), schema_applied
371378

372379

373380
@register_loader("yaml", "yml")
@@ -381,7 +388,7 @@ def yaml_loader(text: str) -> dict:
381388

382389

383390
@register_dumper("yaml", "yml")
384-
def yaml_dumper(origin: dict, indent: int):
391+
def yaml_dumper(origin: dict, indent: int, schema_file: str | None = None) -> tuple[str, bool]:
385392
if YAML is None:
386393
raise RuntimeError("yaml is not installed. Please install with `arclet-entari[yaml]`")
387394
yaml = YAML()
@@ -390,4 +397,15 @@ def yaml_dumper(origin: dict, indent: int):
390397
yaml.width = 4096
391398
sio = StringIO()
392399
yaml.dump(origin, sio)
393-
return sio.getvalue()
400+
ans = sio.getvalue()
401+
schema_applied = False
402+
if schema_file:
403+
root = Path.cwd()
404+
if (root / ".vscode").exists():
405+
if not ans.startswith("# yaml-language-server: $schema="):
406+
ans = f"# yaml-language-server: $schema={schema_file}\n{ans}"
407+
schema_applied = True
408+
elif not ans.startswith("# $schema:"):
409+
ans = f"# $schema: {schema_file}\n{ans}"
410+
schema_applied = True
411+
return ans, schema_applied

arclet/entari/config/format/toml.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,15 @@ def toml_loader(text: str) -> dict[str, Any]:
2020

2121

2222
@register_dumper("toml")
23-
def toml_dumper(origin: dict[str, Any], indent: int = 4):
23+
def toml_dumper(origin: dict[str, Any], indent: int, schema_file: str | None) -> tuple[str, bool]:
2424
"""
2525
Dump a dictionary to a TOML file.
2626
"""
2727
if dumps is None:
2828
raise RuntimeError("tomlkit is not installed. Please install with `arclet-entari[toml]`")
29-
return dumps(origin)
29+
ans = dumps(origin)
30+
schema_applied = False
31+
if schema_file and not ans.startswith("# schema: "):
32+
ans = f"# schema: {schema_file}\n{ans}"
33+
schema_applied = True
34+
return ans, schema_applied

arclet/entari/core.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -416,7 +416,7 @@ def run(
416416
if self._path_scale:
417417
del sys.path[self._path_scale[0] : self._path_scale[1]]
418418
if EntariConfig.instance.path.exists():
419-
EntariConfig.instance.save()
419+
EntariConfig.instance.save(apply_schema=self.gen_schema)
420420
log.core.info("Entari Shutdown.")
421421

422422
@classmethod

0 commit comments

Comments
 (0)