mirror of
https://github.com/ItsDrike/dotfiles.git
synced 2024-12-25 20:54:34 +00:00
Add support for partial overwriting
This commit is contained in:
parent
681427ad9a
commit
7f2f567807
53
sync.py
53
sync.py
|
@ -219,6 +219,7 @@ def print_report(diffs: Iterable[FileDiff]) -> None:
|
||||||
class FixChoice(Enum):
|
class FixChoice(Enum):
|
||||||
OVERWRITE_SYSTEM = auto()
|
OVERWRITE_SYSTEM = auto()
|
||||||
OVERWRITE_DOTFILE = auto()
|
OVERWRITE_DOTFILE = auto()
|
||||||
|
PARTIAL_OVERWRITE = auto()
|
||||||
SKIP = auto()
|
SKIP = auto()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -229,26 +230,52 @@ class FixChoice(Enum):
|
||||||
else:
|
else:
|
||||||
overwrite_system_prompt = f"Overwrite the system {system_type} with the dotfile {dotfile_type}"
|
overwrite_system_prompt = f"Overwrite the system {system_type} with the dotfile {dotfile_type}"
|
||||||
overwrite_dotfile_prompt = f"Overwrite the dotfile {dotfile_type} with the system {system_type}"
|
overwrite_dotfile_prompt = f"Overwrite the dotfile {dotfile_type} with the system {system_type}"
|
||||||
answer = choice_input(
|
|
||||||
f"How to fix {file_path}?",
|
options = [overwrite_system_prompt, overwrite_dotfile_prompt, "Skip this fix"]
|
||||||
[overwrite_system_prompt, overwrite_dotfile_prompt, "Skip this fix"],
|
|
||||||
)
|
# With 2 files, partial overwrites are also a possibility
|
||||||
|
partial_prompt = "Selectively apply partial overwrites to either file"
|
||||||
|
if system_type == dotfile_type == "file":
|
||||||
|
# Add the partial option before the skip option
|
||||||
|
options.insert(-1, partial_prompt)
|
||||||
|
|
||||||
|
answer = choice_input(f"How to fix {file_path}?", options)
|
||||||
|
|
||||||
if answer == overwrite_system_prompt:
|
if answer == overwrite_system_prompt:
|
||||||
return cls.OVERWRITE_SYSTEM
|
return cls.OVERWRITE_SYSTEM
|
||||||
elif answer == overwrite_dotfile_prompt:
|
if answer == overwrite_dotfile_prompt:
|
||||||
return cls.OVERWRITE_DOTFILE
|
return cls.OVERWRITE_DOTFILE
|
||||||
|
if answer == partial_prompt:
|
||||||
|
return cls.PARTIAL_OVERWRITE
|
||||||
elif answer == "Skip this fix":
|
elif answer == "Skip this fix":
|
||||||
return cls.SKIP
|
return cls.SKIP
|
||||||
|
|
||||||
raise Exception("This can't happen (just here for typing.NoReturn)")
|
raise Exception("Invalid answer, this can't happen")
|
||||||
|
|
||||||
|
|
||||||
def _overwrite_file(source: Path, target: Path):
|
def _overwrite_file(source: Path, target: Path, partial: bool = False):
|
||||||
"""Overwrite content of `target` file/directory/symlink with `source` file/directory/symlink."""
|
"""Overwrite content of `target` file/directory/symlink with `source` file/directory/symlink.
|
||||||
|
|
||||||
|
If `partial` is True, `vimdiff` or `nvim -d` is opened for an interactive editor,
|
||||||
|
where the user can selectively apply any changes to either file.
|
||||||
|
"""
|
||||||
if not source.exists():
|
if not source.exists():
|
||||||
raise ValueError(f"Can't overwrite target with non-existent source ({target=}, {source=})")
|
raise ValueError(f"Can't overwrite target with non-existent source ({target=}, {source=})")
|
||||||
|
|
||||||
|
if partial:
|
||||||
|
if not target.exists():
|
||||||
|
raise ValueError(f"Can't apply a partial patch with non-existent target ({target=})")
|
||||||
|
|
||||||
|
if shutil.which("nvim") is not None:
|
||||||
|
prog = ["nvim", "-d"]
|
||||||
|
elif shutil.which("vim") is not None:
|
||||||
|
prog = ["vimdiff"]
|
||||||
|
else:
|
||||||
|
raise ValueError("No diff tool installed, please install neovim or vim")
|
||||||
|
|
||||||
|
subprocess.run([*prog, str(source), str(target)])
|
||||||
|
return
|
||||||
|
|
||||||
# Remove the target, if it already exists
|
# Remove the target, if it already exists
|
||||||
if target.exists():
|
if target.exists():
|
||||||
if target.is_dir():
|
if target.is_dir():
|
||||||
|
@ -287,6 +314,7 @@ def apply_fix(diff: FileDiff) -> None:
|
||||||
case status:
|
case status:
|
||||||
raise Exception(f"Diff status {status!r} didn't match on any cases. This should never happen")
|
raise Exception(f"Diff status {status!r} didn't match on any cases. This should never happen")
|
||||||
|
|
||||||
|
partial = False
|
||||||
match _choice:
|
match _choice:
|
||||||
case FixChoice.SKIP:
|
case FixChoice.SKIP:
|
||||||
return
|
return
|
||||||
|
@ -296,11 +324,18 @@ def apply_fix(diff: FileDiff) -> None:
|
||||||
case FixChoice.OVERWRITE_DOTFILE:
|
case FixChoice.OVERWRITE_DOTFILE:
|
||||||
source = diff.sys_file
|
source = diff.sys_file
|
||||||
target = diff.dot_file
|
target = diff.dot_file
|
||||||
|
case FixChoice.PARTIAL_OVERWRITE:
|
||||||
|
# It doesn't really matter which is the target and which is source here
|
||||||
|
# since the user will be given an interactive environment where they can
|
||||||
|
# make changes to either file.
|
||||||
|
source = diff.dot_file
|
||||||
|
target = diff.sys_file
|
||||||
|
partial = True
|
||||||
case choice:
|
case choice:
|
||||||
raise Exception(f"Choice {choice!r} didn't match on any cases. This should never happen")
|
raise Exception(f"Choice {choice!r} didn't match on any cases. This should never happen")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
_overwrite_file(source=source, target=target)
|
_overwrite_file(source=source, target=target, partial=partial)
|
||||||
except PermissionError:
|
except PermissionError:
|
||||||
print("Fix failed: insufficient permissions")
|
print("Fix failed: insufficient permissions")
|
||||||
return
|
return
|
||||||
|
|
Loading…
Reference in a new issue