Coverage for src/twofas/cli_settings.py: 100%
55 statements
« prev ^ index » next coverage.py v7.4.1, created at 2024-01-29 11:15 +0100
« prev ^ index » next coverage.py v7.4.1, created at 2024-01-29 11:15 +0100
1"""
2This file deals with managing settings for 2fas.
3"""
5import typing
6from pathlib import Path
7from typing import Any
9import tomli_w
10from configuraptor import TypedConfig, asdict, beautify, singleton
11from configuraptor.core import convert_key
13config = Path("~/.config").expanduser()
14config.mkdir(exist_ok=True)
15DEFAULT_SETTINGS = config / "2fas.toml"
16DEFAULT_SETTINGS.touch(exist_ok=True)
18CONFIG_KEY = "tool.2fas"
21def expand_path(file: str | Path) -> str:
22 """
23 Expand ~/... into /home/<user>/...
24 """
25 return str(Path(file).expanduser())
28def expand_paths(paths: typing.Iterable[str]) -> list[str]:
29 """
30 Expand multiple paths.
31 """
32 return [expand_path(f) for f in paths]
35@beautify
36class CliSettings(TypedConfig, singleton.Singleton):
37 """
38 Class for the ~/.config/2fas.toml settings file.
39 """
41 files: list[str] | None
42 default_file: str | None
43 auto_verbose: bool = False
45 def add_file(self, filename: str | None, _config_file: str | Path = DEFAULT_SETTINGS) -> None:
46 """
47 Add a new 2fas file to the configs history list.
48 """
49 if not filename:
50 return
52 filename = expand_path(filename)
54 files = self.files or []
55 if filename not in files:
56 files.append(filename)
58 set_cli_setting("files", expand_paths(files), _config_file)
60 self.files = expand_paths(files)
62 def remove_file(self, filenames: str | typing.Iterable[str], _config_file: str | Path = DEFAULT_SETTINGS) -> None:
63 """
64 Remove a known 2fas file from the config's history list.
65 """
66 if isinstance(filenames, str | Path):
67 filenames = [filenames]
69 filenames = set(expand_paths(filenames))
71 files = [expand_path(_) for _ in (self.files or []) if _ not in filenames]
73 if self.default_file in filenames:
74 new_default = files[0] if files else None
75 set_cli_setting("default-file", new_default, _config_file)
76 self.default_file = new_default
78 set_cli_setting("files", files, _config_file)
79 self.files = files
82def load_cli_settings(input_file: str | Path = DEFAULT_SETTINGS, **overwrite: Any) -> CliSettings:
83 """
84 Load the config file into a CliSettings instance.
85 """
86 return CliSettings.load([input_file, overwrite], key=CONFIG_KEY)
89def get_cli_setting(key: str, filename: str | Path = DEFAULT_SETTINGS) -> typing.Any:
90 """
91 Get a setting from the config file.
92 """
93 key = convert_key(key)
94 settings = load_cli_settings(filename)
95 return getattr(settings, key)
98def set_cli_setting(key: str, value: typing.Any, filename: str | Path = DEFAULT_SETTINGS) -> None:
99 """
100 Update a setting in the config file.
101 """
102 filepath = Path(filename)
103 key = convert_key(key)
105 settings = load_cli_settings(filepath)
106 settings.update(**{key: value}, _convert_types=True)
108 inner_data = asdict(
109 settings,
110 with_top_level_key=False,
111 )
113 # toml can't deal with None, so skip those:
114 inner_data = {k: v for k, v in inner_data.items() if v is not None}
115 outer_data = {"tool": {"2fas": inner_data}}
117 filepath.write_text(tomli_w.dumps(outer_data))