8 changed files with 429 additions and 151 deletions
@ -0,0 +1,126 @@ |
|||
# bsv - Backup, Synchronization, Versioning |
|||
# Copyright (C) 2023 Simon Boyé |
|||
# |
|||
# This program is free software: you can redistribute it and/or modify |
|||
# it under the terms of the GNU Affero General Public License as published by |
|||
# the Free Software Foundation, either version 3 of the License, or |
|||
# (at your option) any later version. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU Affero General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU Affero General Public License |
|||
# along with this program. If not, see <https://www.gnu.org/licenses/>. |
|||
from __future__ import annotations |
|||
|
|||
from typing import Any, Callable, TypeVar |
|||
|
|||
from rich.console import Console |
|||
from rich.text import Text |
|||
|
|||
|
|||
_console: Console | None = None |
|||
def get_console() -> Console: |
|||
assert _console is not None |
|||
return _console |
|||
|
|||
_error_console: Console | None = None |
|||
def get_error_console() -> Console: |
|||
assert _error_console is not None |
|||
return _error_console |
|||
|
|||
|
|||
def init_consoles(color: str="auto"): |
|||
global _console |
|||
global _error_console |
|||
|
|||
assert _console is None |
|||
assert _error_console is None |
|||
|
|||
kwargs: dict[str, Any] = { |
|||
"tab_size": 4, |
|||
} |
|||
match color: |
|||
case "always": |
|||
kwargs["force_terminal"] = True |
|||
case "auto": |
|||
pass |
|||
case "never": |
|||
kwargs["no_color"] = True |
|||
|
|||
_console = Console( |
|||
**kwargs, |
|||
) |
|||
_error_console = Console( |
|||
stderr = True, |
|||
**kwargs, |
|||
) |
|||
|
|||
|
|||
PromptType = TypeVar("PromptType") |
|||
|
|||
class NoDefaultType: |
|||
def __repr__(self): |
|||
return "NoDefault" |
|||
NoDefault = NoDefaultType() |
|||
|
|||
def prompt( |
|||
prompt: str, |
|||
factory: Callable[[str], PromptType], |
|||
*, |
|||
console: Console | None = None, |
|||
default: PromptType | NoDefaultType = NoDefault, |
|||
show_default: bool = True, |
|||
) -> PromptType: |
|||
if console is None: |
|||
console = get_console() |
|||
|
|||
prompt_text = Text(prompt, style="prompt") |
|||
prompt_text.end = "" |
|||
if show_default and default is not NoDefault: |
|||
prompt_text.append(" ") |
|||
prompt_text.append(f"({default})", style="prompt.default") |
|||
prompt_text.append(": ") |
|||
|
|||
while True: |
|||
try: |
|||
value = console.input(prompt_text) |
|||
except KeyboardInterrupt: |
|||
console.print("") |
|||
raise |
|||
|
|||
if not value and not isinstance(default, NoDefaultType): |
|||
return default |
|||
try: |
|||
return factory(value) |
|||
except ValueError as err: |
|||
console.print(err) |
|||
|
|||
def prompt_confirmation(prompt: str, *, console: Console | None=None, default: bool=True) -> bool: |
|||
if console is None: |
|||
console = get_console() |
|||
|
|||
prompt_text = Text(prompt, style="prompt") |
|||
prompt_text.end = "" |
|||
prompt_text.append(" ") |
|||
if default: |
|||
prompt_text.append("(Y/n)", style="prompt.default") |
|||
else: |
|||
prompt_text.append("(y/N)", style="prompt.default") |
|||
prompt_text.append(": ") |
|||
|
|||
while True: |
|||
try: |
|||
value = console.input(prompt_text).strip().lower() |
|||
except KeyboardInterrupt: |
|||
console.print("") |
|||
raise |
|||
|
|||
if not value and not isinstance(default, NoDefaultType): |
|||
return default |
|||
if value not in "yn": |
|||
console.print("Please answer 'y' or 'n'.") |
|||
else: |
|||
return value == "y" |
|||
Loading…
Reference in new issue