Prompt user whether to replace file contents
This commit is contained in:
parent
8c12e26951
commit
208ccb0de2
6 changed files with 67 additions and 21 deletions
|
|
@ -1,10 +1,10 @@
|
|||
from . import cmd, file, modules
|
||||
from . import file, modules, util
|
||||
from .orchestrator import Module, Orchestrator
|
||||
|
||||
__all__: list[str] = [
|
||||
"Module",
|
||||
"Orchestrator",
|
||||
"cmd",
|
||||
"file",
|
||||
"modules",
|
||||
"util",
|
||||
]
|
||||
|
|
|
|||
16
pasch/cmd.py
16
pasch/cmd.py
|
|
@ -1,16 +0,0 @@
|
|||
import shlex
|
||||
import subprocess
|
||||
|
||||
from rich import print
|
||||
from rich.markup import escape
|
||||
|
||||
|
||||
def run_execute(*cmd: str) -> None:
|
||||
print(f"[bright_black]$ {escape(shlex.join(cmd))}")
|
||||
subprocess.run(cmd, check=True)
|
||||
|
||||
|
||||
def run_capture(*cmd: str) -> str:
|
||||
print(f"[bright_black]$ {escape(shlex.join(cmd))}")
|
||||
result = subprocess.run(cmd, check=True, capture_output=True, encoding="utf-8")
|
||||
return result.stdout
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
from pasch.cmd import run_execute
|
||||
from pasch.orchestrator import Module, Orchestrator
|
||||
from pasch.util import run_execute
|
||||
|
||||
|
||||
class Echo(Module):
|
||||
|
|
|
|||
|
|
@ -4,10 +4,12 @@ import random
|
|||
import string
|
||||
from pathlib import Path
|
||||
|
||||
from rich.console import Console
|
||||
from rich.markup import escape
|
||||
|
||||
from pasch.file.file import File
|
||||
from pasch.orchestrator import Module, Orchestrator
|
||||
from pasch.util import fmt_diff, prompt
|
||||
|
||||
|
||||
def random_tmp_path(path: Path) -> Path:
|
||||
|
|
@ -42,6 +44,21 @@ def path_to_str(path: Path) -> str:
|
|||
return str(path.resolve())
|
||||
|
||||
|
||||
def diff_and_prompt(c: Console, path: Path, new_content_bytes: bytes) -> bool:
|
||||
try:
|
||||
new_content = new_content_bytes.decode("utf-8")
|
||||
except:
|
||||
return False
|
||||
|
||||
try:
|
||||
old_content = path.read_text(encoding="utf-8")
|
||||
except:
|
||||
return False
|
||||
|
||||
c.print(fmt_diff(old_content, new_content))
|
||||
return prompt("Replace file contents?", default=False)
|
||||
|
||||
|
||||
class FileDb:
|
||||
def __init__(self, path: Path) -> None:
|
||||
self._path = path
|
||||
|
|
@ -128,7 +145,8 @@ class Files(Module):
|
|||
|
||||
if reason := self._file_db.verify_hash(path, cur_hash):
|
||||
self.c.print(f"[red]Error:[/] {escape(reason)}")
|
||||
return
|
||||
if not diff_and_prompt(self.c, path, content):
|
||||
return
|
||||
|
||||
# We want to avoid scenarios where we fail to remember a file we've
|
||||
# written. It is better to remember a file with an incorrect hash than
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@ from subprocess import CalledProcessError
|
|||
|
||||
from rich.markup import escape
|
||||
|
||||
from pasch.cmd import run_capture, run_execute
|
||||
from pasch.orchestrator import Module, Orchestrator
|
||||
from pasch.util import run_capture, run_execute
|
||||
|
||||
|
||||
@dataclass
|
||||
|
|
|
|||
44
pasch/util.py
Normal file
44
pasch/util.py
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
import difflib
|
||||
import shlex
|
||||
import subprocess
|
||||
|
||||
from rich import print
|
||||
from rich.markup import escape
|
||||
from rich.syntax import Syntax
|
||||
|
||||
|
||||
def run_execute(*cmd: str) -> None:
|
||||
print(f"[bright_black]$ {escape(shlex.join(cmd))}")
|
||||
subprocess.run(cmd, check=True)
|
||||
|
||||
|
||||
def run_capture(*cmd: str) -> str:
|
||||
print(f"[bright_black]$ {escape(shlex.join(cmd))}")
|
||||
result = subprocess.run(cmd, check=True, capture_output=True, encoding="utf-8")
|
||||
return result.stdout
|
||||
|
||||
|
||||
def fmt_diff(old: str, new: str, old_name="old", new_name="new") -> Syntax:
|
||||
diff_text = "".join(
|
||||
difflib.unified_diff(
|
||||
old.splitlines(keepends=True),
|
||||
new.splitlines(keepends=True),
|
||||
fromfile=old_name,
|
||||
tofile=new_name,
|
||||
lineterm="\n",
|
||||
)
|
||||
)
|
||||
return Syntax(diff_text, "diff", line_numbers=False)
|
||||
|
||||
|
||||
def prompt(question: str, default: bool | None = None) -> bool:
|
||||
default_str = {True: "[Y/n]", False: "[y/N]", None: "[y/n]"}[default]
|
||||
while True:
|
||||
reply = input(f"{question} {default_str} ").strip().lower()
|
||||
if not reply and default is not None:
|
||||
return default
|
||||
if reply in {"y", "yes"}:
|
||||
return True
|
||||
if reply in {"n", "no"}:
|
||||
return False
|
||||
print("Please enter y/yes or n/no.")
|
||||
Loading…
Add table
Add a link
Reference in a new issue