Coverage for src/tomcli/cli/set.py: 71%

68 statements  

« prev     ^ index     » next       coverage.py v7.2.3, created at 2023-04-13 11:39 +0300

1# Copyright (C) 2023 Maxwell G <maxwell@gtmx.me> 

2# 

3# SPDX-License-Identifier: MIT 

4 

5# ruff: noqa: UP007 

6 

7from __future__ import annotations 

8 

9import dataclasses 

10from collections.abc import Mapping, MutableMapping 

11import sys 

12from typing import Any, Optional 

13 

14from typer import Argument, Context, Exit, Option, Typer 

15 

16from tomcli.cli._util import _std_cm 

17from tomcli.toml import Reader, Writer, dump, load 

18 

19app = Typer() 

20 

21 

22def get_part(data: MutableMapping[str, Any], selector: str) -> Any: 

23 if selector == ".": 

24 return data 

25 

26 cur = data 

27 parts = selector.split(".") 

28 idx = 0 

29 try: 

30 for idx, part in enumerate(parts): # noqa: B007 

31 cur = cur[part] 

32 except (IndexError, KeyError): 

33 up_to = ".".join(parts[: idx + 1]) 

34 msg = f"Invalid selector {selector!r}: could not find {up_to!r}" 

35 raise Exit(msg) from None 

36 return cur 

37 

38 

39@dataclasses.dataclass() 

40class ModderCtx: 

41 path: str 

42 out: str 

43 reader: str | None = None 

44 writer: str | None = None 

45 allow_fallback_r: bool = True 

46 allow_fallback_w: bool = True 

47 

48 def set_default_rw(self, reader: Reader, writer: Writer): 

49 if self.reader is None: 

50 self.reader = reader 

51 else: 

52 self.allow_fallback_r = False 

53 if self.writer is None: 

54 self.writer = writer 

55 else: 

56 self.allow_fallback_w = False 

57 

58 def load(self) -> MutableMapping[str, Any]: 

59 with _std_cm(self.path, sys.stdin.buffer, "rb") as fp: 

60 return load(fp, self.reader, self.allow_fallback_r) 

61 

62 def dump(self, __data: Mapping[str, Any]) -> None: 

63 with _std_cm(self.out, sys.stdout.buffer, "wb") as fp: 

64 dump(__data, fp, self.writer, self.allow_fallback_w) 

65 

66 

67@app.callback() 

68def callback( 

69 ctx: Context, 

70 path: str = Argument(...), 

71 output: Optional[str] = Option(None, "-o", "--output"), 

72 reader: Optional[Reader] = None, 

73 writer: Optional[Writer] = None, 

74): 

75 ctx.obj = ModderCtx(path, output or path, reader, writer) 

76 

77 

78@app.command(name="del") 

79def delete( 

80 ctx: Context, 

81 selector: str = Argument(...), 

82): 

83 modder: ModderCtx = ctx.find_object(ModderCtx) 

84 modder.set_default_rw(Reader.TOMLKIT, Writer.TOMLKIT) 

85 data = modder.load() 

86 if selector == ".": 

87 raise Exit("Thank you for your patronage, but we won't delete the whole file.") 

88 parts = selector.split(".") 

89 idx = 0 

90 cur = data 

91 try: 

92 for idx, part in enumerate(parts): 

93 if idx + 1 == len(parts): 

94 del cur[part] 

95 break 

96 cur = cur[part] 

97 except (IndexError, KeyError): 

98 up_to = ".".join(parts[: idx + 1]) 

99 msg = f"Invalid selector {selector!r}: could not find {up_to!r}" 

100 raise Exit(msg) from None 

101 modder.dump(data)